vue原理相关
Vue核心概念
- vue实例化
- 虚拟dom
- 模板编译
- 数据绑定(响应式)
- 组件化
MVVM
model和view层通过中间的vm连接和驱动。model层数据变化会改变视图,view改变通过事件来修改数据。vue参考了MVVM实现了双向绑定,react是MVC; 但是vue仍然可以通过refs/parent等操作dom,所以不全是mvvm
vue模板解析
- 1、先将代码转换为AST树
根据正则匹配,从第一个字符开始,筛选过的就删掉继续index++向后匹配。
如果匹配开始标签就放入一个stack中,此时如果匹配到结束标签则出栈对比是否一致,不一致报错 - 2、优化AST树
找出静态节点并标记,之后就不需要diff了
递归遍历ast树中的节点,如果没有表达式、v-if、v-for等,就标记static为true - 3、生成render函数、在使用new Function(with() {})包裹
转换成render函数。编译结束。一定要包裹new Function和with来更改上下文环境
v-if解析出来就是三元表达式,v-for解析出来_l((3),..) - 4、render函数执行后得到的是虚拟dom;AST是需要吧代码使用正则匹配生成的,然后转换成render,而虚拟dom则是通过render函数直接生成一个对象
虚拟dom
VDom:三部曲
-
用 JavaScript 对象模拟真实 DOM 树,对真实 DOM 进行抽象;
-
diff 算法 — 比较两棵虚拟 DOM 树的差异;
-
patch 算法 — 将两个虚拟 DOM 对象的差异应用到真正的 DOM 树。
可以查看本文作者写的另一篇详解虚拟 DOM 的文章《深入剖析:Vue核心之虚拟DOM》https://juejin.cn/post/6844903895467032589#heading-14
vue的双向数据绑定、响应式原理
响应式设计重要的三个对象;
- 监听器 Observer ,用来劫持并监听所有属性(转变成setter/getter形式),如果属性发生变化,就通知订阅者(watcher)
- 订阅器 Dep,用来收集订阅者(Watcher),对监听器 Observer和订阅者 Watcher进行统一管理,每一个属性数据都有一个dep记录保存订阅他的watcher。
- 订阅者 Watcher,可以收到属性的变化通知并执行相应的方法,从而更新视图,每个watcher上都会保存对应的dep;(将模板和Observer对象结合生成实例)
- 解析器 Compile,可以解析每个节点的相关指令,对模板数据和订阅器进行初始化
如何亮点解答??
1、在生命周期的initState方法中将data,prop,method,computed,watch中的数据劫持, 通过observe方法与Object.defineProperty方法将相关对象转为换Observer对象。
2、然后在initRender方法中解析模板,通过Watcher对象,Dep对象与观察者模式将模板中的 指令与对象的数据建立依赖关系,使用全局对象Dep.target实现依赖收集。
3、当数据变化时,setter被调用,触发Object.defineProperty方法中的dep.notify方法, 遍历该数据依赖列表,执行器update方法通知Watcher进行视图更新(微任务异步更新)。
-
vue是无法检测到对象属性的添加和删除,但是可以使用全局Vue.set方法(或vm.$set实例方法)。
-
vue无法检测利用索引设置数组,但是可以使用全局Vue.set方法(或vm.$set实例方法)。
-
无法检测直接修改数组长度,但是可以使用splice;
再去结合vue3做的相关优化,阐述;
Object.defineProperty?
- 必须预先知道要拦截的 key 是什么,所以它并不能检测对象属性的添加和删除。尽管 Vue.js 为了解决这个问题提供 delete 实例方法,但是对于用户来说,还是增加了一定的心智负担。
- Vue.js 无法判断你在运行时到底会访问到哪个属性,所以对于这样一个嵌套层级较深的对象,如果要劫持它内部深层次的对象变化,就需要递归遍历这个对象,执行 Object.defineProperty 把每一层对象数据都变成响应式的。毫无疑问,如果我们定义的响应式数据过于复杂,这就会有相当大的性能负担。
- 数组实现响应式
Proxy(数据劫持优化)
- 劫持的是整个对象,那么自然对于对象的属性的增加和删除都能检测到
- 注意的是 Proxy API 并不能监听到内部深层次的对象变化,因此 Vue.js 3.0 的处理方式是在 getter 中去递归响应式,这样的好处是真正访问到的内部对象才会变成响应式,而不是无脑递归,这样无疑也在很大程度上提升了性能
区别:
1、语法层面上
defineProperty只能响应首次渲染时候的属性,
Proxy需要的是整体监听,不需要关心里面有什么属性,而且Proxy的配置项有13种,可以做更细致的事情,这是之前的defineProperty无法达到的。
2、兼容层面上
vue2.x之所以只能兼容到IE8就是因为defineProperty无法兼容IE8,其他浏览器也会存在轻微兼容问题。
proxy的话除了IE,其他浏览器都兼容,这次vue3还是使用了它,说明vue3直接放弃了IE的兼容考虑。
image.png image.png数据发生变化后,dom是怎么更新的?流程? === watcher.update怎么样更新视图? 将watcher放入一个更新队列里。
待解答、、patch、diff
https://juejin.cn/post/6947514223169798175#heading-1