• render函数使用和详解


    背景

    在平时编程时,大部分是通过template来创建html。但是在一些特殊的情况下,使用template方式时,就无法很好的满足需求,在这个时候就需要 通过JavaScript 的编程能力来进行操作。此时,就到了render函数展示拳脚去时候了。

    render的作用

    在官网的这里示例中,使用组件,将相同的内容通过solt放进h1-h6的标签中,在使用传统方式时,代码不仅冗长,而且在每一个级别的标题中重复书写了 ,在要插入锚点元素时还要再次重复。而使用render函数后,代码就精简了很多。

    Vue.component('anchored-heading', {
      render: function (createElement) {
        return createElement(
          'h' + this.level,   // 标签名称
          this.$slots.default // 子节点数组
        )
      },
      props: {
        level: {
          type: Number,
          required: true
        }
      }
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    render函数的作用是,当场景中用 template 实现起来代码冗长繁琐而且有大量重复,这个时候使用就可以极大的简化代码。

    render函数讲解

    在使用render中,会使用到一个参数createElement,而这个createElement参数,本质上,也是一个函数,是vue中构建虚拟dom所使用的工具。下面就围绕着这个createElement来看一下。

    createelement方法,有三个参数:

    return createEement(, {}, [])
    
    • 1
    1. 第一个参数: 是生成在页面上显示的标签元素(必需参数)
    2. 第二个参数: 是包含配置信息的数据对象(诸如HTML特性,属性,事件侦听器已经要绑定的classstyle)
    3. 第三个参数: 是一个子节点字符串或者包含子节点的数组.

    注意:组件树中的所有vnode必须是唯一的
    通过传入createElement参数,创建虚拟节点,然后再将节点返回给render返回出去。

    总的来说,render函数的本质就是创建一个虚拟节点。
    这里是 createElement 接受的参数:

    // @returns {VNode}
    createElement(
      // {String | Object | Function}
      // 一个 HTML 标签名、组件选项对象,或者
      // resolve 了上述任何一种的一个 async 函数。必填项。
    
      'div',
    
      // {Object}
      // 一个与模板中 attribute 对应的数据对象。可选。
      {
        style:'width:100px'
      },
    
      // {String | Array}
      // 子级虚拟节点 (VNodes),由 `createElement()` 构建而成,
      // 也可以使用字符串来生成“文本虚拟节点”。可选。
      [
        '先写一些文字',
        createElement('h1', '一则头条'),
        createElement(MyComponent, {
          props: {
            someProp: 'foobar'
          }
        })
      ]
    )
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    rendertemplate的区别

    相同之处:
    render 函数 跟 template 一样都是创建 html 模板

    不同之处:

    • Template适合逻辑简单,render适合复杂逻辑。
    • 使用者template理解起来相对容易,但灵活性不足;自定义render函数灵活性高,但对使用者要求较高。
    • render的性能较高,template性能较低。
    • 使用render函数渲染没有编译过程,相当于使用者直接将代码给程序。所以,使用它对使用者要求高,且易出现错误
    • Render 函数的优先级要比template的级别要高,但是要注意的是Mustache(双花括号)语法就不能再次使用
      注意:template和render不能一起使用,否则无效

    render举例

    第一个参数{String | Object | Function}

    第一个参数是一个必须的参数,这个参数可以是字符串string、对象object,或者一个函数function
    比如我们想创建一个标签,以前的写法是这样的

    <div id="app">
    </div>
    <script>
        //  实例
        const vm = new Vue({
            el: "#app",
            template:"
    "
    , }) </script>
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    或者在组件里面

    <script>
    export default {
        methods: {
        },
        render(h) {
            const cmp = {
                template: '
    '
    , } return h(cmp) } } </script>
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    或者

    <script>
    export default {
        methods: {
        },
        render(h) {
            return h('div')
        }
    }
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    或者

    这是最基本的用法

    那么我们如何使用渲染函数来创建标签呢

    字符串

    第一个参数可以是标签名的字符串

    <script>
    export default {
        methods: {
        },
        render(h) {
            return h('div')
        }
    }
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    或者

    <div id="app">
    </div>
    
    <script>
        const vm = new Vue({
            el: "#app",
            render:function(createElement){
                return createElement("div")
            }
        })
    </script>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    createElement 传入了一个字符串参数div, 我们就会发现页面上div标签被渲染出来了

    在这里插入图片描述

    对象

    参数除了是字符串外,可以是组件的选项对象

    <div id="app"></div>
    
    <script>
        //  组件选项对象
        let MyComponent = {
            template:"

    我就一个组件而已

    "
    , }; // 实例中注册组件 const vm = new Vue({ el: "#app", render(createElement){ return createElement(MyComponent) } }) </script>
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    或者

    <script>
    export default {
        methods: {
        },
        render(h) {
            let MyComponent = {
            template:"

    我就一个组件而已

    "
    , }; return h(MyComponent) } } </script>
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    然后引入使用

    <template>
      <div id="app">
        <CustomPop/>
      </div>
    </template>
    
    <script>
    
    import CustomPop from './components/CustomPop.vue'
    
    export default {
      name: 'App',
      components: {
        CustomPop
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    我们发现会直接显示组件的内容
    在这里插入图片描述

    若遇到以下错误信息

    You are using the runtime-only build of Vue where the template compiler is not available
    
    • 1

    请参考这篇博客的解决办法
    https://blog.csdn.net/qq_44732146/article/details/132767730

    函数

    其实第一个参数也可以是一个函数,只不过这个函数执行完毕后,需要返回一个标签名的字符串或者组件对象

    <div id="app"></div>
    
    <script>
        //  实例中注册组件
        const vm = new Vue({
            el: "#app",
            render(createElement){
                let eleFn = function(){
                    return {
                        template:"
    Hello Vue!
    "
    } } return createElement(eleFn()) } }) </script>
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    或者以组件的方式

    <script>
    export default {
        methods: {
        },
        render(h) {
            let eleFn = () => {
                return { template: "
    Hello Vue!
    "
    } }; return h(eleFn()) } } </script>
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    显示结果在这里插入图片描述

    第二个参数:{Object}

    是一个可选参数,这个参数是一个Object。关于第一个参数创建的 标签的属性

    {
      // 与 `v-bind:class` 的 API 相同,
      // 接受一个字符串、对象或字符串和对象组成的数组
      'class': {
        foo: true,
        bar: false
      },
      // 与 `v-bind:style` 的 API 相同,
      // 接受一个字符串、对象,或对象组成的数组
      style: {
        color: 'red',
        fontSize: '14px'
      },
      // 普通的 HTML attribute
      attrs: {
        id: 'foo'
      },
      // 组件 prop
      props: {
        myProp: 'bar'
      },
      // DOM property
      domProps: {
        innerHTML: 'baz'
      },
      // 事件监听器在 `on` 内,
      // 但不再支持如 `v-on:keyup.enter` 这样的修饰器。
      // 需要在处理函数中手动检查 keyCode。
      on: {
        click: this.clickHandler
      },
      // 仅用于组件,用于监听原生事件,而不是组件内部使用
      // `vm.$emit` 触发的事件。
      nativeOn: {
        click: this.nativeClickHandler
      },
      // 自定义指令。注意,你无法对 `binding` 中的 `oldValue`
      // 赋值,因为 Vue 已经自动为你进行了同步。
      directives: [
        {
          name: 'my-custom-directive',
          value: '2',
          expression: '1 + 1',
          arg: 'foo',
          modifiers: {
            bar: true
          }
        }
      ],
      // 作用域插槽的格式为
      // { name: props => VNode | Array }
      scopedSlots: {
        default: props => createElement('span', props.text)
      },
      // 如果组件是其它组件的子组件,需为插槽指定名称
      slot: 'name-of-slot',
      // 其它特殊顶层 property
      key: 'myKey',
      ref: 'myRef',
      // 如果你在渲染函数中给多个元素都应用了相同的 ref 名,
      // 那么 `$refs.myRef` 会变成一个数组。
      refInFor: true
    }
    
    //  class style的不同写法
    {
        class:["class1",{"class2":true}]style: {background: red}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70

    请注意,class,style并没有在attrs属性中,他们是单独设置的,这是因为v-bind指令的特性,如果仅仅将class或者style设置在attrs对象中的一个属性,就不能将class和style设置为数组或是对象了。
    举例:

    const vm = new Vue({
        el: "#app",
        render: function (createElement) {
    
            // 第一个参数是一个简单的HTML标签字符 “必选”
            // 第二个参数是一个包含模板相关属性的数据对象 “可选”
            return createElement('div', {
                'class': {
                    foo: true,
                    bar: false
                },
                style: {
                    color: 'skyblue',
                    fontSize: '24px'
                },
                attrs: {
                    id: 'foo'
                },
                domProps: {
                    innerHTML: 'Hello Vue!'
                }
            })
        }
    })
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    最终生成的DOM,将会带一些属性和内容的div元素,如下图所示:
    在这里插入图片描述

    第三个参数: {String | Array}

    这个参数是可选的,可以给其传一个StringArray处理子节点

    字符串

    当第三个参数为字符串是,就是节点的文本内容

    <script>
        const vm = new Vue({
            el: "#app",
            render:function(createElement){
                return createElement("input", {
                    class:"title"
                },"这是标题")
            }
        })
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    或者组件的方式

    <script>
    export default {
        methods: {
        },
        render(h) {
            return h("div", {
                class: "title"
            }, "这是标题")
        }
    }
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    导入该组件就可以使用了

    显示结果:

    在这里插入图片描述

    数组

    数组中可以放多个节点,可以说是字符串的文本节点, 也可以是通过createElement或者render创建创建的节点

    <script>
        const vm = new Vue({
            el: "#app",
            render(h){
                return h("div",{
                    class:"box",
                },[
                    h("span","我是一个span标签"),
                    h("input",{
                        attrs:{
                            type:"button",
                            value:"按钮"
                        }
                    })
                ])
            }
        })
    </script>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    组件的方式

    <script>
    export default {
        methods: {
        },
        render(h) {
            return h("div", {
                class: "box",
            }, [
                h("span", "我是一个span标签"),
                h("input", {
                    attrs: {
                        type: "button",
                        value: "按钮"   
                    }
                })
            ])
        }
    }
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    显示结果:
    在这里插入图片描述

    渲染函数简写

    我们会发现每次写createElement会非常繁琐,所以我们可以给这个函数定义别名h

    那么我们的代码就可以修改为

    <div id="app"></div>
    
    <script>
        const vm = new Vue({
            el: "#app",
            render(h){
                return h("input", {
                    class:"title"
                },["这是标题"])
            }
        })
    </script>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    约束:

    约束的理解

    组件树中的所有虚拟DOM(VNode)必须是唯一的,

    例如,我想通过render函数实现下面的效果
    在这里插入图片描述

    你可能想到的简单方式

    <div id="app">
    </div>
    
    <script>
        //  实例中注册组件
        const vm = new Vue({
            el:"#app",
            data:{
                msg:'hello'
            },
            render(h){
                // 1\. 创建虚拟DOM 节点
                let myVNode = h("p","hello");
    
                // 2\. 返回最终的虚拟DOM
                return h("div",[
                    myVNode,myVNode,myVNode,myVNode
                ])
            }
        })
    </script>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    在这里插入图片描述
    我们会发现虽然可以实现效果, 也没有报错,但是不合法

    那么我们应该如何实现呢

    建议使用的方法

    使用数据map帮我们处理创建多个虚拟DOM

    示例:

    <script>
        const vm = new Vue({
            el:"#app",
            data:{
                msg:'hello'
            },
            render(h){
                // 返回最终的虚拟DOM
                return h("div",
                         Array.apply(null,{length:4}).map(() => {
                            return h("p","hello")
                        })
                 )
            }
        })
    </script>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
  • 相关阅读:
    适配器模式
    Arduino 基础语法
    C语⾔内存函数
    C/C++算法入门 | 图形输出
    MongoDB命令汇总
    【自留地】前端 - uniapp - Vue - React - Flutter
    Vue开发历程---音乐播放器
    基于ruoyi框架项目-部署到服务器上
    计算机视觉五大核心研究任务全解:分类识别、检测分割、人体分析、三维视觉、视频分析
    MySql学习之慢SQL优化和慢SQL案例
  • 原文地址:https://blog.csdn.net/qq_44732146/article/details/132767335