“这是我参与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的数据响应式实现原理
通过发布订阅模式实现
(图片来源:www.cnblogs.com/llx1996/p/9…)
-
- 监听器Observer,遍历并监听data里面所有的属性,并使用Object.definProperty把所有的属性都加上getter和setter,如果数据有改变就会触发setter通知订阅器Dep数据变更.(核心就是Object.defineProperty)
-
- 订阅器Dep,用来在监听器Observer和订阅者Watcher之间进行统一管理,用subs数组来收集观察者,用Dep.notify来通知Watcher.
-
- 观察者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生成通常需要三步
- element.js
把真实dom节点变成js对象,来模拟真实dom
- diff.js
把新旧dom变成两棵虚拟dom树,比较两棵树的差异
- 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