Vue——render函数

render函数的知识点比较多,作为单独的一篇;

首先,render函数返回VNode,即虚拟DOM节点,VNode存储了基本所有DOM元素所需的信息,Vue之后就是根据VNode的信息准确地渲染出DOM元素并插入文档中;render函数接受两个参数: createElement函数(该函数负责创建VNode)和context(函数组件的上下文,只在函数组件中可用);第二是createElement函数接收的参数(为了构建目标元素,应该传递什么结构的参数);第三是函数组件中的render函数;第四JSX语法;

1,若render和template同时存在,render函数会替代template,render更接近编译器;render函数可用Vue.compile(template)返回的render方法, 即render: Vue.compile(template).render; createElement(?tag, ?data, ?children)

//是不是拥有实例的组件,由functional选项决定,没有给出functional则是,functional为true则不是
//render主要返回VNode节点;-----!!
{
    render(createElement){
        //return createElement(); //则返回一个emptyVNode,即没有元素信息的VNode节点;
        //return createElmenet('div', {style: {'color': 'white'}, staticStyle: 
        //    {'backgroundColor': 'pink'}}, [...this.$slots.default]);
        //或者省略data选项; 
        return createElement('div', ['text for test']);
    }
}

2,createElement函数的使用,实现比较复杂的虚拟节点的生成,这由传给createElement的参数所决定,也可以说是将要生成的虚拟节点的配置,最终在文档渲染的元素的样子;传给createElement的参数:createElement(?tag, ?data, ?children)

tag:Object|String|Function: 若为Object,则它是个组件选项对象,就跟定义一个普通组件的选项一样;若是个String,则它是个原生DOM标签;若是个async函数,resolve上述之一的类型数据;组件选项对象有这些property:data, props, computed, methods, (inject, provide, 这两个在定义普通函数时一般用不到),(directives,components,filters,这三个一般在简单组件中不会用到,components用到的概率比较大一点),template,render(若给定render,则会替代template),name(组件名称,一般建议给上),(model, 一般组件绑定v-model指令,修改其默认prop和event时使用),watch(一般不用,当需要获取异步数据或处理比较大的逻辑时才使用),还有其他的,但也没怎么用先不列举;

data:Object:数据对象,它是VNode虚拟节点的property 'data',貌似在编译时可以确定data数据;有这些可选选项:props(Object,如 {test: 'hello'},它将作为组件tag的props数据,需要组件tag指出props选项来接收它), domProps(Object,如{test: 'hello'},它将作为tag的元素property,如果tag是组件选项,那它将是组件模板根元素的property), attrs(将作为tag的attribute,如果tag是组件,则按组件的规则作用于其模板上),on(Object, 如{click: function(){}}, 绑定到tag上的事件), nativeOn(Object, 如{click: function(){}}, 只是nativeOn在tag是组件的情况下可用,非组件会报错,它将会绑定到组件模板根元素上), (key(字符串或数字), ref(必须是字符串,目前不知怎么用?), refInfo(Boolean), 这三个不知怎么用?) directives(Array<Object>, 如[ {arg: 'test', value: 'hello', name:'somefilter', rawName:'v-somefilter:test.bar', expression: 'someVariableOrOther', modifiers:{bar: true}}]), scopedSlots(Object{[string]:[Function], 如{default: () => VNode|Array<VNode>},当在这给了default的插槽内容,children会被忽略,否则children会传入<slot></slot>中,貌似当tag是组件才使用}, 当tag是组件,它将匹配模板中的插槽),  slot('name-of-slot', 如果组件其他组件的子组件,需要指出插槽名字!??) , class(相当于v-bind:class), style(相当于v-bind:style), staticClass(静态class绑定), staticStyle(静态style绑定);貌似就这样写了;

children: Array<string | Function | VNode>: 若是函数,那它一定是第一个元素才有效,相当于scopedSlots.default = children[0],而children的其他元素都将被忽略children.length=0; 若是string将会被当作VNode节点的text而不会被Vue编译,会被创建一个VNode节点;children的元素也可以是由createElement创建的VNode节点;

若是在拥有实例的组件中(functional: false),render会跟template一样,会把绑在组件上的样式和类名绑到tag上;tag若是组件,在data定义的数据会覆盖该context下的对应的数据;

//使用例子 ------
//当然,render函数不仅仅是这样使用,它在createElement可以有其他的逻辑,如条件之类的
//函数组件和拥有实例的组件的区别在环境上下文的形式不同;函数组件的render在下一节提及
var helloComp = {
    name:'hello',
    render(createElement){
        
        var data = {
            scopedSlots: this.$scopedSlots,
            attrs: {test: 'dddd', 'putong': 'putonging'},
            nativeOn:{
                click: ()=> console.log('click')
            },
            on:{
                click: ()=> console.log('tian')
            },
            props: {hello: 'helloHi'} 
        };    

        var children = [
            '{{hello}}', '...for test...'
        ];

        return createElement({
            props: ['hello', 'test'],
            name: 'test', 
            template: `<div><div>{{hello}}{{test}}</div><slot></slot><slot name="foo" item="item"></slot></div>`},
        data, children);
    }
}
//html
<hello-comp class="hehe" style="background-color: pink, color: white">
    <template v-slot:foo="item">
        <div>for foo + {{item.item}}</div>
    </template>
    <template>
        <div>for default</div>
    </template>
</hello-comp>
//插入文档的元素将是这样的:背景色是pink,字体颜色是white,点击该DOM节点会
//调用回调,console.log('click');
//children被忽略,看是因为scopedSlots.default的原因;
<div putong='putonging' style="background-color: pink, color: white" class="hehe">
    <div>helloHidddd</div>
    <div>for default</div>
    <div>for foo + item</div>
</div>

//当tag是个标签的时候;
{
    //...
    render(){
        //...
        
        delete data.nativeOn; //因为tag是原生DOM标签时不能使用nativeOn;
        //data.scopedSlots没有起作用;
        return createElement('div', data, children);
    }
}
//最终文档中的元素是: 点击该DOM节点会调用回调,console.log('tian');----------
<div test='dddd' putong='putonging' style="background-color: pink, color: white" class="hehe">
    {{hello}}...for test...
</div>

//要跟组件那样的效果: ------------
{
    //...
    render(){
        //...
        
        delete data.nativeOn; //因为tag是原生DOM标签时不能使用nativeOn;
        //data.scopedSlots没有起作用;
        var children = [createElement('div', ['helloHidddd']), ...this.$slots.default, ...this.$scopedSlots.foo({item: 'item'})];

        return createElement('div', data, children );
    }
}

3, 函数组件中的render函数,差别在context上下文参数,该参数包含组件所有的信息:像attributes、插槽内容、props、注入等;

//跟上边的例子不同的是,这个是在函数组件中

4, JSX语法为render函数带来了很大的便利,需要用到Babel插件,接下来会有机会接触的,暂时先放着。

5,参考文档:

Vue

上一篇:记录(待学习任务)


下一篇:详解在vue项目中使用render函数