new Vue 的时候到底做了什么
Vue 加载流程
1.初始化的第一阶段是 Vue 实例也就是 vm 对象创建前后:首先 Vue 进行生命周期,事件初始化发生在 beforeCreate 生命周期函数前,然后进行数据监测和数据代理的初始化,也就是创建 vm 对象的过程,当 vm 对象创建完成就可以通过 vm 对象访问到劫持的数据,比如 data 中的数据,methods 中的方法等。然后 Vue 调用内部的 render 函数开始解析模板将其解析为一个 JS 对象也即在内存中生成虚拟 DOM 也就是 Vnode 对象。第二阶段是 vm 对象挂载前后:挂载完成前页面呈现的是未经过 Vue 编译的 DOM 结构,所有对 DOM 的操作最终都不会生效。挂载前首先将内存中的 Vnode 转换为真实 DOM 插入页面,此时完成挂载。页面中呈现的就是经过 Vue 编译的 DOM 结构,至此初始化过程结束。
2.开启订阅消息也就是数据劫持代理监听,其实就是写了一个 watcher 函数去监听数据的改变,发送网络请求,绑定自定义事件等初始化操作。当数据发生变化以后即状态变更的时候,会重新构造新的 Vnode 对象。然后用新的 Vnode 对象和旧的 Vnode 对象进行差异比较也就是 DIFF 算法,然后把差异应用到旧的 Vnode 对象所构建的真正的 DOM 树上这个过程就是 patch,视图就更新了
每一个组件在加载时都会调用 Vue 内部的 render 函数把该组件的 tamplate 选项的模板解析为一个 JS 对象,这个对象和 DOM 节点对象结构一样,然后是数据劫持代理监听,当数据发生变化以后,将旧 Vnode 对象和生成的新 Vnode 对象比较差异然后更新 DOM
更多面试题解答参见 前端vue面试题详细解答
什么是 DIFF
diff 算法是一种对比算法。对比两者是旧虚拟 DOM 和新虚拟 DOM,对比出是哪个虚拟节点更改了,找出这个虚拟节点,并只更新这个虚拟节点所对应的真实节点,而不用更新其他数据没发生改变的节点,实现精准地更新真实 DOM,进而提高效率
其有两个特点:
比较只会在同层级进行, 不会跨层级比较
在 diff 比较的过程中,循环从两边向中间比较
DIFF 算法的过程:
当数据发生改变时,订阅者
watcher
就会调用patch
给真实的DOM
打补丁通过
isSameVnode
进行判断,相同则调用patchVnode
方法patchVnode
做了以下操作:找到对应的真实
dom
,称为el
如果都有都有文本节点且不相等,将
el
文本节点设置为Vnode
的文本节点如果
oldVnode
有子节点而VNode
没有,则删除el
子节点如果
oldVnode
没有子节点而VNode
有,则将VNode
的子节点真实化后添加到el
如果两者都有子节点,则执行
updateChildren
函数比较子节点updateChildren
主要做了以下操作:设置新旧
VNode
的头尾指针新旧头尾指针进行比较,循环向中间靠拢,根据情况调用
patchVnode
进行patch
重复流程、调用createElem
创建一个新节点,从哈希表寻找key
一致的VNode
节点再分情况操作
关于 Vue 中 el,template,render,$mount 的渲染
渲染根节点:
先判断有无 el 属性,有的话直接获取 el 根节点,没有的话调用 $mount 去获取根节点。
渲染模板:
有 render:这时候优先执行 render 函数,render 优先级 > template。
无 render:有 template 时拿 template 去解析成 render 函数的所需的格式,并使用调用 render 函数渲染。无 template 时拿 el 根节点的 outerHTML 去解析成 render 函数的所需的格式,并使用调用 render 函数渲染
渲染的方式:无论什么情况,最后都统一是要使用 render 函数渲染
评论