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,参考文档: