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 - Stringconst vm = new Vue({render: function(createElement) {return createElement("h1")}})//使用render - Objectconst vm = new Vue({render:function(createElement) {return createElement({template: '<h1>h1标签</h1>'})}})//使用render -Functionconst 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 = thisreturn 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函数
