zoukankan      html  css  js  c++  java
  • Vue.js 创建一个 CNODE 社区(6)

    render

    关于 render 的更详细的内容可以查看 Vue 官方文档

    Vue 推荐在绝大多数情况下使用 template 来创建你的 HTML。然而在一些场景中,你真的需要 JavaScript 的完全编程的能力,这时你可以用 render 函数,它比 template 更接近编译器。

    我们先设想一下这个场景:

    使用 v-bind 和 slot ,实现组件切换的功能。通过监听父组件按钮的事件,改变父组件 level 的值,然后把新的值通过 props 传递给了子组件;然后进行 v-if 判断,显示对应的组件。

    使用 template 做法

    <div id="app">
          <my-child :level='level'>
              我是组件中内容
          </my-child>
          <br>
          <button @click='changeFontSize(1)'>改变字号-大</button>
          <button @click='changeFontSize(2)'>改变字号-中</button>
          <button @click='changeFontSize(3)'>改变字号-小</button>
          <template id='myTemp'>
              <div>
                <h1 v-if='level==1'><slot></slot></h1>
                <h2 v-if='level==2'><slot></slot></h2>
                <h3 v-if='level==3'><slot></slot></h3>
            </div>
          </template>
      </div>
    
    Vue.component('myChild',{
        props:['level'],
        template:'#myTemp'
    })
    var app = new Vue({
        el:'#app',
        data:{
            level:3
        },
        methods:{
            changeFontSize:function(value){
                this.level = value
            }
        }
    })
    

    demo :JSbin

    但是,如果有更多的组件需要进行判断和切换呢?

    template 就会越写越长,并且我们可以看到:

    代码冗余

    每当点击按钮切换组件的时候,之前的组件移除后,他们的组件也会渲染在页面上,会堆积在 DOM 树中,导致代码变得冗余,难以维护。

    通过 render

    现在通过 render 函数来改写一下这个功能:

    <div id="app">
          <my-child :level='level'>
              我是组件中内容
          </my-child>
          <br>
          <button @click='changeFontSize(1)'>改变字号-大</button>
          <button @click='changeFontSize(2)'>改变字号-中</button>
          <button @click='changeFontSize(3)'>改变字号-小</button>
      </div>
    
    Vue.component('myChild',{
        props:['level'],
        render:function(createElement){
            return createElement('h'+this.level,this.$slots.default)
        }
    })
    var app = new Vue({
        el:'#app',
        data:{
            level:3
        },
        methods:{
            changeFontSize:function(value){
                this.level = value
            }
        }
    })
    

    通过 render 处理可以让结构更简洁,代码也没有冗余:

    render

    demo : JSbin


    render 详解

    demo : JSbin

    Vue.component('myChild',{
        props:['level'],
        render:function(createElement){
            return createElement('h'+this.level,this.$slots.default)
        }
    })
    

    在 render 函数的方法中,参数必须是 createElement。,createElement 的类型是 function,他会返回一个 vnode 虚拟节点。

    • createElement 的第一个参数

    可以是 String | Object | Function

    // String - HTML标签,会在组件所在的位置渲染一个 div 节点
    render:function(createElement){
            return createElement('div')
        }
    
    // Object - 一个含有数据选项的对象
    render:function(createElement){
            return createElement({
                template:`<div></div>`
            })
        }
    
    // Function - 一个方法,返回含有数据选项的对象
    render:function(createElement){
            let createDom = function(){
                return {
                    template:`<div></div>`
                }
            }
            return createElement(createDom)
        }
    
    • createElement 的第二个参数(可选)

    createElement 的第二个参数是数据对象,只能是 object。

    // Function - 一个方法,返回含有数据选项的对象
    render:function(createElement){
        return createElement({
            template:`<div>这里字体是红色的</div>`
        },{
            class:{
                foo:true,
                bar:false
            },
            style:{
                color:'red',
                fontSize:'30px'
            },
            attrs:{
                id:'foo'
            },
            // 用来显示原生的 dom 属性
            domProps:{
                innerHTML:`<span style='color:yellow;font-size:16px'>这里是黄色的</span>`
            }
        })
    }
    

    render

    可以看到,我们通过给 createElement 方法添加第二个参数,为第一个参数所创造的节点增添了不少的属性。

    • createElement 的第三个参数(可选)

    第三个参数也是可选,String | Array,作为我们构建函数的子节点来使用的

    render:function(createElement){
        return createElement('div',{
            class:{
                foo:true,
                bar:false
            }},[
            createElement('h1','创建子节点1'),
            createElement('h6','创建子节点2')
        ])
    }
    

    第三个参数

    可以看到,我们通过给 createElement 方法添加第三个参数,为第一个参数所创造的节点增添了两个子节点(h1/h6)以及分别为两个子节点添加了一个子节点('创建子节点1'/'创建子节点2')。


    this.$slots在render函数中的应用

    利用 createElement 的第三个参数可以存储数组、创建子节点的特点,可以把指定的 slot (this.$slots.slotName返回的是一个 vnode 数组)存储到某个节点中。

    demo : JSbin

    <div id="app">
        <child>
            <p>正文</p>
            <span>正文段落</span>
            <h1 slot='header'>header</h1>
            <h2 slot='footer'>footer</h2>
        </child>
    </div>
    
    Vue.component('child',{
        render:function(createElement){
            // 注意,这里的 header 是一个 vnode 数组,所以他是 createElement 的第三个参数
            let header = this.$slots.header
            let footer = this.$slots.footer
            let main = this.$slots.default
            return createElement('div',[
                createElement('header',header),
                createElement('main',main),
                createElement('footer',footer)
            ])
        }
    })
    var app = new Vue({
        el:'#app'
    })
    

    在 render 函数中使用 props 传递数据

    和之前的例子一样:

    <div id="app">
          <my-child :level='level'>
              我是组件中内容
          </my-child>
          <br>
          <button @click='changeFontSize(1)'>改变字号-大</button>
          <button @click='changeFontSize(2)'>改变字号-中</button>
          <button @click='changeFontSize(3)'>改变字号-小</button>
      </div>
    
    Vue.component('myChild',{
        props:['level'],
        render:function(createElement){
            return createElement('h'+this.level,this.$slots.default)
        }
    })
    var app = new Vue({
        el:'#app',
        data:{
            level:3
        },
        methods:{
            changeFontSize:function(value){
                this.level = value
            }
        }
    })
    

    v-model 在 render 函数中的应用

    demo : JSbin

    <div id="app">
        <app-component :name='name' v-model='name'></app-component> <br>
        {{ name }}
    </div>
    
    Vue.component('app-component',{
        render:function(createElement){
    
            // 这里的 this 等于子组件的this
            let self = this
            return createElement('input',{
                domProps:{
    
                    // 这句的作用是把父组件传递进来的值显示在输入框里
                    value:self.name
                },
                on:{
                    input:function(event){
                        self.$emit('input',event.target.value)
                    }
                }
            })
        },
        props:['name']
    })
    var app = new Vue({
        el:'#app',
        data:{
            name:'lily'
        }
    })
    

    首先我们注册了一个子组件,并且通过 render 函数渲染模板,其中设置了一个 value 属性和 input 的监听事件;

    声明 self 是为了确定 this 的指向是子组件;

    value 属性的值,是父组件通过 :name='name'props 传递给子组件的;

    子组件通过 v-model 绑定了父组件的数据 name,当用户在输入框中输入时,我们在子组件中设置的监听事件就会触发 $emit 事件,把输入框中的 value 传递给了 v-model,v-model 更新了 name 的值为 value,从而实现子组件传值给父组件。


    作用域插槽在render函数中的使用

    demo : JSbin

    <div id="app">
        <app-component>
        <template scope='prop'>
            {{ prop.text}} <br>
            {{ prop.msg}}
        </template>
        </app-component>
    </div>
    
    Vue.component('app-component',{
        render:function(createElement){
    
            // 其实这句话就相当于之前的 template:`<div><slot text='scopeText' msg='scopeMsg'></slot></div>`
            return createElement('div',this.$scopedSlots.default({
                text:'scopeText',
                msg:'scopeMsg'
            }))
        }
    })
    var app = new Vue({
        el:'#app'
    })
    

    函数化组件的应用

    之前创建的锚点标题组件是比较简单,没有管理或者监听任何传递给他的状态,也没有生命周期方法。它只是一个接收参数的函数。在这个例子中,我们标记组件为 functional,这意味它是无状态 (没有响应式数据),无实例 (没有 this 上下文)。

    组件需要的一切都是通过上下文(传入的 context)传递

    在添加 functional: true 之后,锚点标题组件的 render 函数之间简单更新增加 context 参数,this.$slots.default 更新为 context.children,之后this.level 更新为 context.props.level。

    demo : JSbin

    <div id="app">
        <app-component></app-component>
    </div>
    
    Vue.component('app-component',{
        functional: true,
        render:function(createElement,context){
            return createElement('button',{
                on:{
                    click:function(){
                        console.log(context)
                        console.log(context.parent)
                    }
                }
            },'点击我查看上下文')
        }
    })
    var app = new Vue({
        el:'#app',
    })
    

    关于 render,学习就先到这儿了。

    得赶紧了解完基础概念,才能更快地学到实战部分...

  • 相关阅读:
    UVA 11987 几乎就是并查集= =
    UVALive 5908 更新一下线段相交模板
    【poor几何】UVALive 5908 更新一下线段相交模板
    【poor几何】UVALive 5908 更新一下线段相交模板
    UVALive 3634 熟悉一下STL
    UVALive 3634 熟悉一下STL
    UVALive 3634 熟悉一下STL
    hdu2665 主席树模板题
    hdu2665 主席树模板题
    迷宫问题 POJ
  • 原文地址:https://www.cnblogs.com/No-harm/p/9749157.html
Copyright © 2011-2022 走看看