持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第1天,点击查看活动详情
vue2 是如何实现响应式的?
- 实现数据的双向绑定,首先要对数据进行劫持监听,所以我们需要设置一个监听器
Observer
,用来监听所有属性。如果属性发生变化了,就需要告诉订阅者Watcher
(Watcher 是一个封装了渲染视图逻辑的类,用于派发更新的),结合Vnode
经过patch()
中的diff算法
看是否需要更新(dep.notify() 通知 收集的 Watcher 重新渲染)。因为订阅者Watcher
是有很多个,所以每一个属性都有自己的一个消息订阅器Dep
来专门收集这些订阅者(即以依赖的watcher
),然后在监听器Observer
和订阅者Watcher
之间进行统一管理
在数据劫持方面从 vue2 的 Object.defineProperty()升级为 Proxy,这样做有什么好处?
- 解决了在 vue2 中 set 方法监听不到数组的改变,无需重写 array 原型上的方法
- 给对象新增加一个属性时,不需要再手动去监听这个新增属性(使用 vm.$set 才能保证新增的属性也是响应式的)
- vue2 实现了一个
definedReactive
,内部是用Object.defineProperty
对每一个属性进行监听,需要遍历来对所有属性监听,只能监控到最外层的对象,深度监听一个对象时需要递归,Proxy 代理的是整个对象,而不是对象的某个特定属性,不需要我们通过遍历来逐个进行数据绑定 - 在使用 Object.defineProperty()给对象添加一个属性之后,我们对对象属性的读写操作仍然在对象本身。在 Proxy 中,如果想要读写操作生效,我们就要对 Proxy 的实例对象 proxyObj 进行操作。
- 另外 Proxy 提供 13 种拦截操作proxyMdn
//Object.defineProperty
// 实现一个响应式函数
function defineProperty(obj, key, val) {
//如果某对象的属性也是一个对象,递归进入该对象,进行监听
if (typeof val === "object") {
observer(val);
}
Object.defineProperty(obj, key, {
get() {
console.log(访问了key属性);
return val;
},
set(newVal) {
// 如果newVal是一个对象,递归进入该对象进行监听
if (typeof val === "object") {
observer(key);
}
console.log(key属性被修改为newVal了);
val = newVal;
},
});
}
// 实现一个遍历函数Observer
function Observer(obj) {
//如果传入的不是一个对象,return
if (typeof obj !== "object" || obj === null) return;
Object.keys(obj).forEach((key) => {
defineProperty(obj, key, obj[key]);
});
}
//Proxy
//定义handler对象
let hander = {
get(obj, key) {
// 如果对象里有这个属性,就返回属性值,如果没有,就返回false
return key in obj ? obj[key] : false;
},
set(obj, key, val) {
obj[key] = val;
return true;
},
};
Vue3.0 优点
- 将 Vue 内部的绝大部分 api 对外暴露,使 Vue 具备开发大型项目的能力,例如 compile 编译 api 等
- webpack 的 treeshaking(tree shaking 是 DCE 的一种方式,它可以在打包时忽略没有用到的代码。)支持度友好
- 使用 Proxy 进行响应式变量定义,性能提高,对 typescript 支持更加友好
- 受 ReactHook 启发,可在 Vue2.0 中单独使用 composition-api 插件,或者直接用它开发插件
- Vue 3 的 Template 支持多个根标签,Vue 2 不支持