1.vue渲染页面整体流程
渲染过程解析:
- template:静态模板template经过转换解析生成AST.
- AST: AST最终会编译为render函数,Vue使用HTML的Parser将HTML模板解析为AST,并且对AST进行一些优化的标记处理,提取最大的静态树,方便Virtual DOM时直接跳过Diff.
- render:渲染函数用来生成virtual dom。vue推荐使用template来构建应用界面,在底层实现中vue会将模板编译为渲染函数,当然我们也可以不写模板直接写渲染函数。
- virtual DOM:虚拟DOM树,Vue的Virtual DOM Patching算法是基于Snabbdom的实现,并在些基础上作了很多的调整和改进.
2.两种构建过程
- 独立构建:包含模板编译器,渲染过程
HTML字符串 → render函数 → VNode → 真实DOM节点
- 运行时构建:不包含模板编译器,渲染过程
render函数 → VNode → 真实DOM节点
<br />
由此图可以看出,vue提供了三种渲染方法 ,el template render,这三种渲染模式最终都是要而得到render函数,只不过用户自定义的render函数省去了程序分析的过程,等同于处理过的render函数,而普通的template或者el只是字符串,需要解析成AST,再由AST转换为render函数。
3.render语法
render:function(createElement) {
return createElement(arg1, arg2, arg3);
}
createElement函数提供了三个参数,其中第一个参数是必填的,后两个参数是可选的,下文将分别介绍这三个参数的作用。
3.1createElement参数
3.1.1 参数一:{ String| Object | Function }
用于生成在页面上显示的标签元素
//未使用render方式创建h1标签的写法
const vm = new Vue({
el: "#app",
template: "<h1>h1标签</h1>"
})
//使用render - String
const vm = new Vue({
render: function(createElement) {
return createElement("h1")
}
})
//使用render - Object
const vm = new Vue({
render:function(createElement) {
return createElement({
template: '<h1>h1标签</h1>'
})
}
})
//使用render -Function
const vm = new Vue({
render: function(createElement) {
const elFunc = function() {
return {
template: '<h1>h1标签</h1>'
}
}
return createElement(elFunc())
}
})
3.1.2 参数二 { Object }
包含配置信息的数据对象,如html特性,属性,时间侦听器以及要绑定的class及style
<div id="app">
<custom-element></custom-element>
</div>
Vue.component('custom-element', {
render: function (createElement) {
var self = this
// 第一个参数是一个简单的HTML标签字符 “必选”
// 第二个参数是一个包含模板相关属性的数据对象 “可选”
return createElement('div', {
'class': {
foo: true,
bar: false
},
style: {
color: 'red',
fontSize: '14px'
},
attrs: {
id: 'boo'
},
domProps: {
innerHTML: 'Hello Vue!'
}
})
}
})
let app = new Vue({
el: '#app'
})
3.1.3 参数三 { String | Array }
子节点字符串或包含子节点的数组
<div id="app">
<custom-element></custom-element>
</div>
Vue.component('custom-element', {
render: function (createElement) {
var self = this
return createElement(
'div', // 第一个参数是一个简单的HTML标签字符 “必选”
{
class: {
title: true
},
style: {
border: '1px solid',
padding: '10px'
}
}, // 第二个参数是一个包含模板相关属性的数据对象 “可选”
[
createElement('h1', 'Hello Vue!'),
createElement('p', '开始学习Vue!')
] // 第三个参数是传了多个子元素的一个数组 “可选”
)
}
})
let app = new Vue({
el: '#app'
})
4.使用JavaScript代替模板功能
在使用vue模板时,可以在模板中使用v-if v-show v-for等指令,但是使用render渲染函数时,需要通过原生JS实现响应功能。
<ul v-if="items.length">
<li v-for="item in items">{{ item }}</li>
</ul>
<p v-else>No items found.</p>
以上代码通过render函数可以这样实现:
render: function(createElement) {
if(this.items.length) {
return createElement("ul",null,this.items.map( item => {
return createElement("li")
}))
}else {
return createElement("<p>No items found.</p>")
}
}
5.插槽
你可以从this.$slots获取VNodes列表中的静态内容:
render: function (createElement) {
// 相当于 `<div><slot></slot></div>`
return createElement('div', this.$slots.default)
}
还可以从this.$scopedSlots
中获得能用作函数的作用域插槽,这个函数返回VNodes:
props: ['message'],
render: function (createElement) {
// `<div><slot :text="message"></slot></div>`
return createElement('div', [
this.$scopedSlots.default({
text: this.message
})
])
}
如果要用渲染函数向子组件中传递作用域插槽,可以利用VNode数据中的scopedSlots
域:
<div id="app">
<custom-ele></custom-ele>
</div>
Vue.component('custom-ele', {
render: function (createElement) {
return createElement('div', [
createElement('child', {
scopedSlots: {
default: function (props) {
return [
createElement('span', 'From Parent Component'),
createElement('span', props.text)
]
}
}
})
])
}
})
Vue.component('child', {
render: function (createElement) {
return createElement('strong', this.$scopedSlots.default({
text: 'This is Child Component'
}))
}
})
let app = new Vue({
el: '#app'
})
6. JSX
在Vue中使用JSX可以让我们回到更接近于模板的语法上
import AnchoredHeading from './AnchoredHeading.vue'
new Vue({
el: '#demo',
render: function (h) {
return (
<AnchoredHeading level={1}>
<span>Hello</span> world!
</AnchoredHeading>
)
}
})
原文引自:Vue render函数