写文章不容易,点个赞呗兄弟
专一 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工做原理,源码版助于了解内部详情,让咱们一块儿学习吧 研究基于 Vue版本 【2.5.17】node
若是你以为排版难看,请点击 下面连接 或者 拉到 下面关注公众号也能够吧数组
【Vue原理】Compile - 白话版 性能优化
终于到了要讲 compile 白话的时候了,你们准备好了吗,白话版确定不会很复杂啦,源码版就不必定了。。。bash
源码版我写了9篇啊!每篇的篇幅都很长啊!!我都快写奔溃了啊!!函数
都快坚持不下来了,我算了算, compile 的源码版,我好像快写了一个多月???性能
卧槽,居然写了这么久.....学习
好吧,如今开始咱们的正文优化
compile 的内容很是多,大体分为三块主要内容,我也称他们是Vue的 渲染三巨头ui
就是 parse,optimize,generatespa
虽然分为三块,可是要明确一点
compile 的做用是解析模板,生成渲染模板的 render
通过 compile 以后,就会生成下面的 render
_c('div', [_c('span'), _v(num)])
复制代码
而 render 的做用,也是为了生成跟模板节点一一对应的 Vnode
{
tag: "div",
children:[{
tag: "span",
text: undefined
},{
tag: undefined
text: "111"
}]
}
复制代码
下面咱们就来一个个看渲染三巨头
这是 compile 的第一个步骤
接收 template 原始模板,按照模板的节点 和数据 生成对应的 ast
生成的 ast 是这样,全部模板中出现的数据,你均可以在 ast 中找到
{
tag: "div",
attrsMap: {test: "2"},
children:[{
tag: "span",
children: [],
attrsMap: {name: "1"}
}]
}
复制代码
ast 是什么?我的简单理解的话
以数据的形式去描述一个东西的全部的特征吧,说错别打我
好比说ast 描述我
{
name: "神仙朱",
sex: 1,
desc: "一个靓仔"
}
复制代码
具体能够查一下,相关内容挺多的
另外,这里不会讲细节,parse 是怎么生成 ast 的,由于涉及不少源码,放在源码版了
这是 compile 的第二步
遍历递归每个ast节点,标记静态的节点(没有绑定任何动态数据),
这样就知道那部分不会变化,因而在页面须要更新时,减小去比对这部分DOM
从而达到性能优化的目的
span 和 b 就是静态节点,在 optimize 处理中,就会给他们添加 static 判断是不是静态节点
{
static: false,
staticRoot: false,
tag: "div",
children: [{
staticRoot: true,
tag: "span",
children: [{
static: true,
tag: "b"
}]
},{
static: false,
text: "{{a}}"
}]
}
复制代码
而你也看到一个属性,staticRoot,这个是表示这个节点是不是静态根节点的意思
用来标记 某部分静态节点 最大的祖宗节点,后面更新的时候,只要碰到这个属性,就知道他的全部子孙节点都是静态节点了,而不须要每一个子孙节点都要判断一次浪费时间
具体是怎么作的,感兴趣的话欢迎看之后的源码版
这是 compile 的第三步
把前两步生成完善的 ast 组装成 render 字符串(这个 render 变成函数后是可执行的函数,不过如今是字符串的形态,后面会转成函数)
通过前两步变成 ast
{
static: false,
staticRoot: false,
tag: "div",
children: [{
static: false,
staticRoot: false,
tag: "span",
children: [{
static: false,
text: "{{b}}"
}]
},{
static: false,
text: "{{a}}"
}]
}
复制代码
而后,generate 接收 ast,先处理最外层 ast,而后开始递归遍历子节点,直到全部节点被处理完
这个过程当中,字符串会被一点一点拼接完成,好比上面的 ast 拼接结果就是下面这样
_c 是生成节点对应的 Vnode 的一个函数
`
_c('div', [
_c('span', [
_v(b)
]),
_v(a)
])
`
复制代码
一、一开始接收到 ast,处理最外层 ast 这个点,是 div,因而拼接获得字符串
code = ` _c('div', [ `
复制代码
二、遍历 div 子节点,遇到 span,拼接在 div 的子节点数组中
code = `_c('div', [ _c('span', [ `
复制代码
三、开始处理 span 的子节点 b,放进 span 的 子节点数组中
code = ` _c('div', [ _c('span', [ _v(b) `
复制代码
四、span 子节点处理完,闭合 span 的 子节点数组
code = ` _c('div', [ _c('span', [ _v(b) ] `
复制代码
五、继续处理 span 同级 的子节点,是个文本节点,可是是动态值,变量是 a
code = `_c('div', [ _c('span', [ _v(b) ] , _v(a) `
复制代码
六、全部子节点都处理完毕,闭合 div 的 子节点数组
code = ` _c('div', [ _c('span', [ _v(b) ] , _v(a) )] `
复制代码
前面两步把 template 解析生成了 render 字符串,可是须要执行的话,仍是须要转换成函数的
怎么转呢?就是下面这样
render = new Function(render)
复制代码
而后 render 保存在实例上,具体位置是
vm.$options.render
至此,compile 全部的功能就完成了
而关于 render 的内容,好比说 render 中出现的各类函数是什么,会专门放在 render 的文章去记录