所有问题答案均为精简版,详情版后续补充链接
Vue2与Vue3有什么异同:
-
数据劫持优化:
- Vue2 中的
Object.defineProperty
,因为需要预先知道要拦截的key是什么,所以并不能检测对象属性的添加和删除
,对于一个较深层级的对象来说,不管是否会访问到深层级的属性,都会进行深度监听,一次性计算量大 - Vue3使用的
proxy
实现。对于深层级对象,仍然需要递归实现,但是它是在proxy的getter操作中赋予响应式。意味着只有访问到这个层级的属性才会建立响应
,而不是像2一样直接给整个对象都加上响应。
- Vue2 中的
-
渲染优化(渲染器):
从双端Diff算法升级为快速Diff算法
-
编译优化(编译器)
-
通过数据劫持和依赖收集,Vue2数据更新并触发重新渲染的粒度是
组件级
的。由于 Vue 使用了模板和组件的组合方式,
每个组件都有自己独立的数据对象、依赖收集和渲染上下文。
这意味着数据变化只会触发当前组件内部的重新渲染
,不会影响其他组件,因此数据更新和重新渲染的粒度是组件级的。但如果组件中有静态节点,即以后都不会发生改变,但是Diff算法还是会重新比较这些静态节点,尽管我们已经知道这些比较毫无意义。
-
Vue3在编译阶段对静态模板进行了分析,编译生成了
BlockTree
,它会指导Diff算法的执行跳过静态节点。
-
-
组合式APi(写法)
-
无需额外考虑this指向,调用者只需要关系函数的返回值
-
代码简洁,快速开发
- 函数式编程大量使用函数,减少了代码的重复,降低服用成本
- 在选项式中通过
mixin
可以使用代码复用,但是使用它可能会导致`数据来源不明确和命名冲突
-
方便的代码管理,方便调试
函数式编程不依赖、也不会改变外界的状态,只要给定输入参数,返回的结果必定相同。因此,每一个函数都可以被看做独立单元,很有利于进行单元测试(unit testing)和除错(debugging),以及模块化组合。易于"并发编程"
- 打包过程中可以更好的利用 tree shaking 过滤无用代码
-
-
源码体积优化
- 移除了部分API,例如filter等
- 引入
tree-shaking
减小打包体积。
-
框架、源码层面优化
Vue2的源码统一放在src下,Vue3则是放在package下。内部文件都是根据功能划分,但是Vue3的功能划分
力度更细致
,每个项目之间没有影响,有着自己的API,类型定义和单元测试。Vue3用的是typeScript。对于框架、组件库typeScript很有必要,因为有利于IDE自动补全,并且有更清晰的架构
例如:若只想
获得Vue提供的响应式能力
,对于2来说,则需要引入整个Vue.js.但是对于3来说,可以仅引用Vue下的reactivity响应库。
$nextTick是如何实现的
Vue用异步队列的方式来控制DOM更新和nextTick回调先后执行。microtask
因为其高优先级特性,能确保队列中的微任务在一次事件循环前被执行完毕。因为浏览器和移动端兼容问题,Vue不得不做了microtask
向macrotask
的兼容(降级)方案。
- 主要思路是通过
event loop
最后的UI render
,只要让nextTick里的代码放在UI render步骤后面执行,就能访问到更新后的DOM了。 - 每一次事件循环都包含一个microtask队列,在循环结束后会依次执行队列中的microtask并移除,然后再开始下一次事件循环。
- 不能使用微任务时,只能用宏任务了(降级策略):setImmediate(只支持IE和nodejs)、MessageChannel(回调也是microtask,但也是个新API,面临兼容性的尴尬)、setTimeout(执行的最小时间间隔是约4ms)
- 最终降级原则保留为Promise=>MutationObserver=>setImmediate=>MessageChannel=>setTimeout
Pinia与Vuex的区别
- 无需要创建自定义的复杂包装器来支持 TypeScript,一切都可标注类型,API 的设计方式是尽可能地利用 TS 类型推理;
- 弃用了mutation;
- Pinia没有modules,如果想使用多个store,直接定义多个store传入不同的id即可,故也无需嵌套合并至一个store中;
- Pinia是可以直接修改状态的,并且调试工具能够记录到每一次state的变化,而vuex直接修改不能够记录每一次state的变化记录,需要在mutation中修改,影响我们的调试。当vuex开启严格模式的时候,直接修改state会抛出错误;
- Pinia可以使用$reset将状态重置为初始值;
- Pinia支持了storeToRefs可以使用ES6解构;
- Vuex中commit和dispatch会有过多的魔法字符串注入,而pinia只需要导入函数并调用它们,然后享受自动补全的乐趣就好。
Proxy 与 Object.defineProperty 的区别及优劣势
Proxy 的优势如下:
- Proxy代理整个对象,Object.defineProperty只代理对象上的某个属性;
- 数组新增删除修改时,Proxy可以监听到;
- Proxy 有多达 13 种拦截方法,不限于 apply、ownKeys、deleteProperty、has 等等是 Object.defineProperty 不具备的;
- 如果对象内部要全部递归代理,则Proxy可以只在调用时递归,而Object.defineProperty需要在一开始就全部递归,Proxy性能优于Object.defineProperty;
- 对象上定义新属性时,Proxy可以监听到;
Object.defineProperty 的优势如下:
- 兼容性好,支持 IE9及以上,而 Proxy 的存在浏览器兼容性问题,而且无法用 polyfill 磨平,因此 Vue 的作者才声明需要等到下个大版本( 3.0 )才能用 Proxy 重写。
【keep-alive】的实现及常考点
贴一个自己的文章Vue-keepAlive的实现与考点
vue组件data为什么是函数
当一个组件被定义,data 必须声明为返回一个初始数据对象的函数,因为组件可能被用来创建多个实例。
如果 data 仍然是一个纯粹的对象,则所有的实例将共享引用同一个数据对象!
通过提供 data 函数,每次创建一个新实例后,我们能够调用 data 函数,从而返回初始数据的一个全新副本数据对象。