写文章不容易,点个赞呗兄弟
专一 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工做原理,源码版助于了解内部详情,让咱们一块儿学习吧
研究基于 Vue版本 【2.5.17】
若是你以为排版难看,请点击 下面连接 或者 拉到 下面关注公众号也能够吧node
【Vue原理】Diff - 源码版 之 重新建实例到开始diff 函数
Diff 的内容不少,咱们先来探索一下从 新建实例 到 开始Diff 的流程走一遍,本文很短学习
先对整个流程有个把握,再仔细去探索 Diff 的思想this
首先,当你新建实例的时候,好比这样spa
你调用一个 Vue 函数,因此来看下 Vue 函数prototype
function Vue() { ... 已省略其余 new Watcher(function() { vm._update(vm._render()); }) ... 已省略其余 }
函数中作了两件事code
一、为实例新建一个 watcherblog
二、为 watcher 绑定更新回调(就是 new Watcher 传入的 function )token
每一个实例都会有一个专属的 watcher,而绑定的回调,在页面更新时会调用ci
咱们如今来看下简化的 Watcher 的源码
funciton Watcher(expOrFn){ this.getter = expOrFn; this.get(); } Watcher.prototype.get = function () { this.getter() }
watcher 会保存更新回调,而且在新建 watcher 的时候就会马上调用一遍更新回调
如今咱们继续看 更新回调的内容
vm._update(vm._render());
若是你看到以前的文章应该知道这两个函数的做用
如今就来简单说一下
生成页面模板对应的 Vnode 树,好比
生成的 Vnode 树是( 其中num的值是111 )
{ tag: "div", children:[{ tag: "span" },{ tag: undefined, text: "111" }] }
这一步是经过 compile 生成的,具体的话能够简单看下 Compile - 白话版
有兴趣的有耐心的也能够看源码版
比较 旧Vnode 树 和 vm._render 生成的新 Vnode 树 进行比较
比较完后,更新页面的DOM,从而完成更新
ok,咱们看下源码
Vue.prototype._update = function(vnode) { var vm = this; var prevEl = vm.$el; var prevVnode = vm._vnode; vm._vnode = vnode; // 不存在旧节点 if (!prevVnode) { vm.$el = vm.__patch__( vm.$el, vnode, vm.$options._parentElm, vm.$options._refElm ); } else { vm.$el = vm.__patch__( prevVnode, vnode ); } };
解释其中几个点
看过 VNode - 源码版 应该知道,这个属性保存的就是当前 Vnode 树
当页面开始更新,而生成了新的 Vnode 树以后
这个属性则会替换成新的Vnode
因此保存在这里,是为了方便拿到 旧 Vnode 树
是的,没有错,你在两处地方看到这个东西
这个东西就是 Diff 的主要内容,内有乾坤,内容不少,不会在这里说,毕竟今天只探索流程
可是要看看这个东西怎么来的
var patch = createPatchFunction(); Vue.prototype.__patch__ = patch ;
嗯,是通过一个 createPatchFunciton 生成的
而后赋值到 Vue 的原型上,因此能够 vm.__patch__ 调用喽
咱们再来讲说 _update 函数中出现的那两处 patch
不须要进行比较,直接所有建立
vm.$el 保存的是 DOM 节点,若是不存在旧节点,那么 vm.$el 此时也是不存在的
而传入 vm.$el 为空的时候,patch 拿到这个值判断为空的时候,就直接建立DOM,不会去作其余操做了
须要把旧节点和新节点比较,尽可能找到最小差别部分,而后进行更新,这部份内容就是 Diff 的重点了,须要花费很多精力的。会放在其余文章进行记录
鉴于本人能力有限,不免会有疏漏错误的地方,请你们多多包涵,若是有任何描述不当的地方,欢迎后台联系本人,有重谢