【Vue原理】Render - 源码版 之 静态 Render
写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧
研究基于 Vue版本 【2.5.17】
如果你觉得排版难看,请点击 下面链接 或者 拉到 下面关注公众号也可以吧
【Vue原理】Render - 源码版 之 静态 Render
上一篇我们讲了 render 函数,而 Vue 为了更新时速度快一些,加入了一个 staticRender
没错,就是 静态 render,看过前面文章的人,应该知道什么是 静态 render
静态 render 就是用于渲染哪些不会变化的节点
大家可以先看看,Vue 是怎么判断某个节点是否是静态节点
好,下面开始我们的正文,想了想,我们还是以几个问题开始吧
1、静态 render 是什么样子的
2、静态 render 是怎么生成和 保存
3、静态 render 怎么执行
什么是 静态Render
静态 render 其实跟 render 是一样的,都是执行得到 Vnode
只是静态 render,没有绑定动态数据而已,也就是说不会变化
比如说,一个简单 render 是这样的
绑定了动态数据,需要从实例去获取
_c('div',[_v(_s(aa))])
而静态 render 是这样的
没有动态数据,这个静态render 的执行结果是永远不会变的
_c('div',[_c('span',[_v("1")])])
生成保存静态Render
静态 render 是在 generate 阶段生成的,生成的方式和 render 是一样的
比如在一个模板中,有很多个静态 根节点,像这样
首先,Vue 会在遍历模板的时候,发现 span 和 strong 本身以及其子节点都是静态的
那么就会给 span 和 strong 节点本身设置一个属性 staticRoot,表示他们是静态根节点
然后这两个静态根节点就会生成自己专属的 静态 render
如何标记静态根节点的具体可以看 Compile 之 optimize 标记静态节点
怎么把静态根节点生成 render 的可以看 Compile 之 generate 节点拼接 中 genStatic 的部分
如果你有一直看我的Vue 笔记的话,你应该这里是会有点印象的
之后
静态 render 生成之后是需要保存的,那么保存在哪里呢?
保存在一个数组中,名叫 staticRenderFns,就是直接push 进去
当然了,此时的 push 进去的 静态 render 还是字符串,并没有变成函数
以上面的模板为例,这里的 staticRenderFns 就是这样,包含了两个字符串
staticRenderFns = [ "_c('span',[_c('b',[_v("1")])])", "_c('strong',[_c('b',[_v("1")])])" ]
但是在后面会逐个遍历变成可执行的函数
staticRenderFns = staticRenderFns.map(code => { return new Function(code) });
那么 这个 staticRenderFns 又是什么啊?
每个 Vue 实例都有一个独立的 staticRenderFns,用来保存实例本身的静态 render
staticRenderFns 的位置是
vm.$options.staticRenderFns
执行静态Render
静态 render 需要配合 render 使用,怎么说
看个例子
这个模板的 render 函数是
_c('div',[ _m(0), _v(_s(a), _m(1) ])
_m(0) , _m(1) 就是执行的就是 静态 render 函数,然后返回 Vnode
于是 render 也可以完成 vnode 树的构建了
那么 _m 是什么呢?
在 Vue 初始化时,给Vue的原型便注册了这个函数,也就是说每个实例都继承到 _m
function installRenderHelpers(target) { target._m = renderStatic; } installRenderHelpers(Vue.prototype);
再来看 renderStatic
function renderStatic(index) { var cached = this._staticTrees || (this._staticTrees = []); var tree = cached[index]; // 如果缓存存在,就直接返回 if (tree) return tree // 这里是执行 render 的地方 tree = cached[index] = this.$options.staticRenderFns[index].call( this, null, this ); // 只是标记静态 和 节点id 而已 markStatic(tree, "__static__" + index, false); return tree }
这个函数做的事情可以分为几件
1、执行静态render
2、缓存静态render 结果
3、标记 静态 render 执行得到的 Vnode
我们来一个个说
1 执行静态render
上面我们说过了,静态render 保存在 数组 staticRenderFns
所以这个函数接收一个索引值,表示要执行数组内哪个静态render
取出静态render 后,执行并绑定 Vue 实例为上下文对象
然后得到 Vnode
2 缓存静态render 结果
这一步就是要把上一步得到的 Vnode 缓存起来
那么缓存在哪里呢?
_staticTrees
这是一个数组,每个实例都会有一个独立的 _staticTrees,用来存在自身的静态 render 执行得到的 Vnode
看一下上个模板中实例保存的 _staticTrees
3 标记 静态 render 执行得到的 Vnode
我们已经执行静态render得到了 Vnode,这一步目的是标记
标记什么呢
1、添加标志位 isStatic
2、添加 Vnode 唯一id
renderStatic 中我们看到标记的时候,调用了 markStatic 方法,现在就来看看
function markStatic( tree, key ) { if (Array.isArray(tree)) { for (var i = 0; i < tree.length; i++) { if ( tree[i] && typeof tree[i] !== 'string') { var node = tree[i] node.isStatic = true; node.key = key + "_" + i; } } } else { tree.isStatic = true; tree.key = key } }
为什么添加标志位 isStatic?
前面我们添加的所有静态标志位都是针对 模板生成的 ast
这里我们是给 Vnode 添加 isStatic,这才能完成Vue的目的
Vue 目的就是性能优化,在页面改变时,能尽量少的更新节点
于是在页面变化时,当 Vue 检测到该 Vnode.isStatic = true,便不会比较这部分内容
从而减少比对时间
Vnode 唯一id
每个静态根Vnode 都会存在的一个属性
我也没想到 静态Vnode 的 key 有什么作用,毕竟不需要比较,也许是易于区分??
最后
静态 render 我们就讲完了,是不是很简单,在没看源码之前,我以为很难
现在看完,发现也简单的,不过我也是看了几个月的。。。。
鉴于本人能力有限,难免会有疏漏错误的地方,请大家多多包涵,如果有任何描述不当的地方,欢迎后台联系本人,有重谢