最近在研究Vue.js的内容,感觉render函数理解有些吃力,那就写篇笔记记录一下吧。

Vue.js的工作流程

先看一张Vue的工作流程图:

Vue工作流程

由这张图可以看出:Vue先把现有的template解析成ast(Abstract Syntax Tree - 抽象语法树),再将抽象语法树编译为render函数,然后把render函数加载到一个虚拟的DOM对象中(其实中间还有进行一系列的复杂操作,这里只是简单解释),最后由虚拟DOM渲染到真实的DOM,也就是界面中。

为什么要使用render函数

假设一个原本的Vue实例是这样的:

1
2
3
4
5
new Vue({
el: '#app',
components: { App },
template: '<App/>'
})

由于在Vue对象有template属性,并且在template中使用的一个叫App的组件,所以Vue会先把template中的App抽象到语法树中,才能知道有App这个组件,从而去寻找App组件的构造器,以及加载到render函数,渲染等步骤。

对于这个例子,Vue的处理流程是这样的:

Template => AST => Render => VirtualDom => DOM

现在我们把template交给render函数处理:

1
2
3
4
5
6
new Vue({
el: '#app',
// components: { App },
// template: '<App/>'
render: h => h(App)
})

把箭头函数的写法改成ES5原生写法:

1
2
3
4
5
6
7
8
new Vue({
el: '#app',
// components: { App },
// template: '<App/>'
render: function (createElement){
return createElement(App)
}
})

上面的h对象就是这里的createElement,它是一个函数。Vue默认把它叫做h函数,其中 根据 Vue.js 作者 Even You 的回复,h 的含义如下:

It comes from the term “hyperscript”, which is commonly used in many virtual-dom implementations. “Hyperscript” itself stands for “script that generates HTML structures” because HTML is the acronym for “hyper-text markup language”.

它来自单词 hyperscript,这个单词通常用在 virtual-dom 的实现中。Hyperscript 本身是指
生成HTML 结构的 script 脚本,因为 HTML 是 hyper-text markup language 的缩写(超文本标记语言)

这样处理之后,Vue的处理流程就变成了:

Render => VirtualDom => DOM

省去了从Template解析编译到Render的过程,由此以来便节省了内存和依赖。

Vue CLI中render函数的预使用

在使用Vue脚手架创建项目时,会有下面的选择:

Vue build:

  • Runtime + Compiler: recommended for most users
  • Runtime-only: about 6KB lighter min+gzip, but templates (or any Vue-specific HTML) are ONLY allowed in .vue files - render functions are required elsewhere

在正式的生产环境中,我们一般会选择第二种runtine-only的方式构建项目,这样就不会为项目安装template的编译器,转而要求开发人员使用render函数进行template的加载,虽然增大了开发难度,但却为项目提高了性能。

createElement函数的详细用法

直接创建标签

这里createElement函数接收三个参数:

  1. 标签名(String):如h2,p,div
  2. 标签的property(Object):如class属性:{ class: ‘box’ }
  3. 标签的Content(Array):比如一段文本:[ ‘Some Text’ ]

写出来的例子就是这样的:

1
2
3
4
5
6
7
8
9
new Vue({
el: '#app',
render: function (createElement){
return createElement('h2',{
class: 'box'
},
[ 'Some Text' ])
}
})

这就在页面中插入了一个HTML标签:

1
2
3
<h2 class = 'box'>
Some Text
</h2>

嵌套式写法

在原本的内容上再加入一个元素:

1
2
3
4
5
6
7
8
9
new Vue({
el: '#app',
render: function (createElement){
return createElement('h2',{
class: 'box'
},
[ 'Some Text' , createElement('button',[ '按钮' ] )])
}
})

再原本的h2的标签上再加入了一个按钮:

1
2
3
4
5
6
<h2 class = 'box'>
Some Text
<button>
按钮
</button>
</h2>

传入组件

先创建一个组件对象,然后把组件对象传入改函数:

1
2
3
4
5
6
7
8
const cpn = {
template: '<p>{{message}}</p>',
data() {
return {
message: 'Some Message'
}
}
}

正如最上面提到的写法,我们在使用下面的代码引入App时,App就已经是一个含render函数的对象。

1
import App from './App.vue'

至于原因,是因为我们在使用Webpack时用到了一个Loader,叫做vue-template-compiler,正是这个Loader的存在,使得我们在引入vue文件时,它就已经被转换为了一个可以被render函数使用的对象。