一、概述
(1)Vue.js 2.x与Vue.js 1.x最大的区别就在于2.x使用了Virtrual Dom(虚拟DOM)来更新DOM节点,提升渲染性能。
(2)写在template选项里的组件模板,在Vue.js编译时,会解析为Virtrual Dom。
(3)Render,是Vue.js用于实现Virtrual Dom的。
二、Virtrual Dom(虚拟DOM)
(1)React和Vue2都使用了Virtrual Dom技术,Virtrual Dom并不是真正意义上的DOM,而是一个轻量级的JavaScript对象,在状态发生变化时,Virtrual Dom会进行Diff计算,来更新只需要被替换的DOM,而不是全部重绘。
(2)与DOM操作相比,Virtrual Dom是基于JavaScript计算的,所以开销会小很多。
(3)正常的DOM节点在HTML中是这样的:
(4)用Virtrual Dom创建的JavaScript对象一般会是这样的
(5)Virtrual Dom运行过程
(6)vNode对象通过一些特定的选项描述了真实的DOM结构
(7)在Vue.js2中,Virtrual Dom就是通过一种VNode类表达的,每个DOM元素或组件都对应一个VNode对象,在Vue.js源码中是这样定义的:
(8)VNode主要可以分为如下几类。
(9)使用Virtrual Dom就可以完全发挥JavaScript的编程能力。
三、Render函数
(1)Render函数通过createElement参数来创建Virtrual Dom。
(2)使用对比
render函数写法:
四、createElement用法
(1)基本参数
createElement构成了 Vue Virtrual Dom 的模板,它有3个参数。
1)第一个参数必选,可以是一个HTML标签,也可以是一个组件或函数。
2)第二个是可选参数,数据对象,在template中使用。
3)第三个是子节点,也是可选参数,用法一致。
(2)第二个参数:数据对象
例子:
使用Render改写后的代码如下:
就此例而言,template的写法明显比Render写法要可读而且简洁,所以要在合适的场景使用Render函数,否则只会增加负担。
(3)约束
所有的组件树中,如果VNode是组件或含有组件的slot,那么VNode必须唯一。
1)错误示范
1.实例一,重复使用组件
2.实例二,重复使用含有组件的slot
这两个示例都期望在子节点内渲染出两个Child组件,也就是两个<p>text</p>节点,实际预览时只渲染出了一个,因为在这种情况下,VNode受到了约束。
2)对于重复渲染多个组件(或元素)的方法有很多,如下示例
1.组件
上例通过一个循环和工厂函数就可以渲染5个重复的子组件Child。
2.含有组件的slot
对于含有组件的slot,复用就要稍微复杂一点了,需要将slot的每个子节点都克隆一份。
在Render函数里创建了一个cloneVNode的工厂函数,通过递归将slot所有子节点都克隆了一份,并对VNode的关键属性也进行了复制。
(4)使用JavaScript代替模版功能
1)在Render函数中,不再需要Vue内置的指令,比如 v-if,v-for,当然,也没办法使用它们。
2)无论要实现什么功能,都可以用原生JavaScript。
3)v-if,v-else可以这样写
上例直接使用JavaScript的 if 和 else 语句来完成逻辑判断。
4)v-for
5)使用JavaScript的 if 、else语句和数组map方法充分配合使用来渲染一个列表。
首先是判断prop:list是否为空,如果是空,就渲染一个“列表为空”的<p>元素,如果不为空数组,那就把每一项作为<li>渲染,放在<ul>下。
对应的template
6)Render函数里也没有与v-model对应的API,需要自己来实现逻辑。
事实上,v-model就是prop:value和event:input组合使用的一个语法糖。
对应的template
7)对于事件修饰符和按键修饰符,基本也需要自己实现。
例子:
五、函数化组件
(1)Vue.js提供了一个functional的布尔值选项,设置为true可以使组件无状态和无实例,也就是没有data和this上下文。
(2)这样用render函数返回虚拟节点可以更容易渲染,因为函数化组件只是一个函数,渲染开销要小很多。
(3)使用函数化组件时,Render函数提供了第二个参数context来提供临时上下文。组件需要的data、props、slots、children、parent都是通过这个上下文来传递的,比如this.level要改写为context.props.level,this.$slots.default改写为context.children。
(4)示例
(5)函数化组件在业务中并不是很常用,而且也有其他类似的方法来实现,比如上例也可以用组件的is特性来动态挂载。总结起来,函数化组件主要适用于以下两个场景
- 程序化地在多个组件中选择一个。
- 在将children、props、data传递给子组件之前操作它们。
六、JSX
(1)使用Render函数最不友好的地方就是在模版比较简单时,写起来也很复杂,而且难以阅读出DOM结构,尤其当子节点嵌套较多时,嵌套的createElement就像盖楼一样一层一层延伸下去。例如
用template:
使用createElement改写后应该是:
(2)为了让Render函数更好地书写和阅读,Vue.js提供了插件babel-plugin-transform-vue-jsx来支持JSX语法
(3)JSX是一种看起来像HTML,但实际是JavaScript的语言扩展,它用更接近DOM结构的形式来描述一个组件的UI和状态信息,最早在React.js里大量应用。
比如上面的Render用JSX改写后的代码是:
注意:这里的render使用了ES2015的语法缩写了函数,参数h不能省略,否则使用时会触发错误。
(4)示例
上面的示例使用JSX后等于下面的代码:
(5)JSX仍然是JavaScript而不是DOM,如果你的团队不是JSX强驱动的,建议还是以模版template的方式为主,特殊场景(比如锚点标题)使用Render的createElement辅助完成。