事关我对于Vue数据响应式的理解

291 阅读4分钟

“这是我参与8月更文挑战的第2天,活动详情查看:8月更文挑战

写在前面: 这里其实就开始有意思起来了,都是一些比较有用的知识,虽然说是Vue深层次,其实也算是js的更高级应用.

事关我对于Vue更深层次的理解

Vue从模板编译到真实Dom的过程

1. 解析模板代码
template(用parse()和codegen()解析成AST树)
2. 解析AST结构树里面的vue语法
AST树(用Transform()处理,再generate()解析v-if,@click等vue语法成render函数)
//上面两步相对应于执行了Vue.compile(template)方法;
3. 执行render函数编译出虚拟dom
4. 新旧虚拟dom通过patch.js对比
通过patchVnode方法,对比新旧虚拟dom的标签名,属性,样式,子属性和key值,产生三种情况处理(不变,新增或删除,修改)
通过updataChildren方法,对虚拟节点的子节点进行比较
5.最后把更新后的虚拟dom渲染成真实dom.

优势:在频繁和大量的数据变换和dom操作下,可以找出最合理最高效的编译方式;本质为js代码,不受运行环境影响,可跨平台.

具体: rander函数转换成类似ast树结构,再通过diff算法对比新旧虚拟dom的差异,不变的保留,只更新需要修改的数据,从而降低时间复杂度,提高性能.

Vue2和Vue3的响应式具体区分

通过Object.defineProperty方法发布者(订阅者)模式实现数据劫持,通过遍历data里面的数据,手动getter和setter方法并且兼容了数组常用方法

Vue2的数据响应式实现原理

通过发布订阅模式实现

1492200-20181023200852131-291342969.png

(图片来源:www.cnblogs.com/llx1996/p/9…

    1. 监听器Observer,遍历并监听data里面所有的属性,并使用Object.definProperty把所有的属性都加上getter和setter,如果数据有改变就会触发setter通知订阅器Dep数据变更.(核心就是Object.defineProperty)
    1. 订阅器Dep,用来在监听器Observer和订阅者Watcher之间进行统一管理,用subs数组来收集观察者,用Dep.notify来通知Watcher.
    1. 观察者Watcher,通过订阅Dep类可以收到Dep的变化通知,然后通过进行updata操作更新视图.

Vue3响应式数据原理

通过Porxy代理模式实现

Proxy(代理模式)

拦截对data任意属性和方法的任意操作, 包括属性值的读写, 属性的添加, 属性的删除等等.

  • Proxy是一个对象,通过new Proxy(target, handler)来创建,
    • target: 是用来包裹的目标对象
    • handler: 是一个自定义函数,可以为{},但不能为null
  • 常用方法
    • handler.get(target,property,receiver)获取值拦截
    • handler.set(target,property,value,receiver)设置值拦截
    • handler.has(target,prop)in 操作符拦截

Reflect(反射)

Relflect对象与Proxy对象一样,也是ES6为了操作对象而提供的新的API,可以动态对被代理对象的相应属性进行特定的操作

  • 修改某些Object方法的返回结果,让其变得更合理。
  • 让Object操作都变成函数行为。
  • Reflect对象的方法与Proxy对象的方法一一对应,只要是Proxy对象的方法,就能在Reflect对象上找到对应的方法。这就让Proxy对象可以方便地调用对应的Reflect方法,完成默认行为,作为修改行为的基础。也就是说,不管Proxy怎么修改默认行为,你总可以在Reflect上获取默认行为
   <!--Object.defineProperty()在无法定义属性时,会抛出一个错误,
   而Reflect.defineProperty()则会返回false。-->
       //老写法
           try{
               Object.defineProperty(target,property,attributes);
               //success
           }catch(e){
               //failure
           }

       //新写法
           if(Reflect.defineProperty(target,property,attributes)){
               //success
           }else{
               //failure
           }
   <!--如name inobj和deleteobj[name],而Reflect.has(obj,name)和Reflect.deleteProperty(obj,name)
       让它们变成了函数行为。-->
       //老写法
       'assign' in Object//true
       //新写法
       Reflect.has(Object,'assign')//true
           

Vue2的虚拟Dom深入理解

虚拟dom生成通常需要三步

  1. element.js

把真实dom节点变成js对象,来模拟真实dom

  1. diff.js

把新旧dom变成两棵虚拟dom树,比较两棵树的差异

  1. patch.js

把得到的差异应用到真实dom上并渲染出来

vue的虚拟dom深入

vue里面的虚拟dom是用VNode这个类去声明的,里面的重要属性有:

  • tag:指的是节点的标签名
  • data:指的是节点上的class,attr,style,以及event事件
  • children:指该节点的子节点
  • elm:指该节点的真实dom节点
  • key:是VNode的标记,用来提高diff的速度

VNode创建过程

vue实例初始化(init) --> 
vue实例挂载(调用原型上的mount方法) -->
mount执行mountComponent方法(实例化一个渲染Watcher,回调函数执行updataComponent) -->
updataComponent调用vm._render,vm._update更新dom和patch更新节点 -->
_render是实例的私有方法(把实例渲染成虚拟Node,且调用$createElement方法) -->
_createdElement方法创建VNode(createElement函数参数:context,tag,data,children)

diff过程

1.createElm()创建节点(一般三种,不是if(tag元素节点)else if(isComment注释节点) else(文本节点))
2.removeNode()删除节点()
3.更新节点

End