关于vue知识点的梳理

163 阅读5分钟

1.Vue的模板渲染

vue的核心是声明式渲染,不关注流程,只关心执行条件 Vue的,模板编译分为三个阶段,

parse使用正则表达式解析vue模板文件,生成AST抽象语法树 optimize静态节点标记,优化AST语法树,为后期VNode的patch过程对比做优化 generate更觉AST结构,生成render函数

2.vue的响应式系统

vue中所有写在data中的属性都会在vue初始化的时候,被Object.defineProperty(3.0是proxy)进行数据劫持代理。而vue响应式的核心机制是观察者模式,数据是被观察的一方,一旦发生改变就会通知所有的观察者,并作出响应。 vue的响应式主要有三个重要的概念:Observer,Dep,Watcher Observer负责数据劫持,Watcher负责订阅,和观察数据变化,Dep负责接收订阅并通知Observer,接收属性修改的消息,并通知所有Watcher

发布者-Observer

当组件vm初始化的时候,调用defineReactive函数,使用Object.defineProperty对data里面的每个属性进行遍历的数据劫持,为每个属性添加getter和setter 方法,将对应的属性变成响应式的

在组件初始化的时候,调用initState函数,内部执行initstate、initProps、initComputed分别对 data、prop、computed初始化,使其变为响应式

data/prop都是调用defineReactive函数,使之变成响应式 computed是新建computed-watcher对象,遍历computed中的每个属性通过调用defineComputed使之变为响应式

订阅器-Dep

在defineReactive将属性变成响应式的过程中,会为每个属性实例化一个Dep,Dep对象用于依赖收集,实现了一个发布订阅模式,主要是对Watcher进行管理,收集Watcher和当属性改变时,遍历Watcher列表dep.subs,通知所有的Watcher执行update逻辑

Dep的工作原理是,在属性的getter中调用dep.depend()方法,收集Watcher(可能是 组件render fn、computed、watch)进行依赖收集,在属性getter中,调用dep.notify()通知所有Watcher执行更新

观察者-Watcher

主要作用是为观察的属性收集依赖和提供回调函数,当被观察的值发生变化时,会接收到来自Dep的通知,从而触发回调函数 computed-watcher:当属性发生改变的时候,不会立即改变,只有当其他地方需要读取。才会真正计算。lazy(懒计算)特性

3.Virtual DOM

VNode,全称virtual node,即虚拟节点,对真实 DOM 节点的虚拟描述,在 Vue 的每一个组件实例中,会挂载一个$createElement函数,所有的VNode都是由这个函数创建的。 render 函数执行后,会根据VNode Tree将 VNode 映射生成真实 DOM,从而完成视图的渲染。

Diff

是将新老节点进行比较,进行最小单位的修改,进而提高性能

patch

vue的diff算法是对于新旧两个vnode(虚拟节点树进行同层比较,而非是传统的逐层遍历比较)所以时间复杂度是有O(n),大大的提高了算法的效率

判断新老节点有一个函数叫做sameVnode,内部的逻辑大体是比较key和tag标签等一些其他的参数是否相等,如果不是相同节点,就会移除老节点,创建新节点。如果是相同节点,则会进入vnode比较流程,进入patchVnode

patchVnode

该函数的主要作用是判断如何对子节点进行更新

当新老节点都无子节点的时候,只是文本的替换。

当新节点没有子节点而老节点有子节点的时候,则移除该 DOM 节点的所有子节点。

如果老节点没有子节点而新节点存在子节点,清空老节点的dom的文本内容,为当前dom节点加入子节点

如果都有子节点则对子节点进行diff比较,触发updateChildren方法,updateChildren方法也是vue diff的核心

updateChildren

核心的思想是考虑到web端对一个数组节点的操作,要么是插入要么是删减或者颠倒数组这一类的变化,所以定义了四个变量索引oldStartIdx、oldEndIdx、newStartIdx、newEndIdx对应的是oldStartVnode、oldEndVnode与newStartVnode、newEndVnode两两比较

  1. 两个树的oldStartVnode与newStartVnode比较,如若满足sameVnode则两者进行patchVnode,并且index值右移
  2. 两个虚拟节点树oldEndVnode与newEndVnode比较,如若满足sameVnode两者进行patchVnode,并且index值左移
  3. oldStartVnode与newEndVnode比较,如若满足sameVnode,则把旧节点的vnode对应的真实dom节点移动到最后一项,old的index右移,new的index左移
  4. oldEndVnode与newStartVnode比较,如若满足sameVnode,则把旧节点的vnode对应的真实dom节点移动到最前面,old的index左移,new的index右移

如果都不满足,就按照常规操作遍历旧节点树了 在oldStartIdx与oldEndIdx之间查找与newStartVnode满足sameVnode的节点,若存在,则将匹配的节点真实 DOM 移动到oldStartVnode的前面。

在就节点树上遍历循环每一个新节点树的Vnode,存在就把该节点移动到旧节点树的第newVnodeindex之前,不存在就在newVnodeindex之前插入一个节点

如果遍历结束,当 oldStartIdx > oldEndIdx 或者 newStartIdx > newEndIdx,老节点删除,新节点创建放在oldEndVnode后面

下面是一个例子 patch 过程图: