vue3.0在数据的响应式上无疑做了很大的优化,性能得到大大提升。虽然vue3.0还没有正式发布,但是各大公众号已经闹得不亦说乎。
我们都知道vue2的响应式原理的实现依赖的是Object.defineProperty这个API,用它来为要实现响应式的数据设置getter和setter方法。
如果数据多时就要循环遍历。循环遍历就会降低性能。
所以vue3.0就摒弃了这个方法,改为使用ES6中的proxy来实现。而这个方法没有shim,不兼容低版本浏览器。所以vue3.0彻底摒弃了它们。
下面我就用Vue2.0和Vue3.0分别解释一下Vue的响应式原理
- Vue 2.0使用的是Object.defineProperty
- 假设我们修改message的数据,Vue内部是如何监听message数据?
- 当数据发生改变,Vue是如何知道要通知哪些“人”,页面发生刷新?
<div id="app">
<h2>{{message}}</h2> <!--张三-->
<h2>{{message}}</h2> <!--李四-->
<h2>{{message}}</h2> <!--王五-->
<h2>{{name}}</h2>
</div>
//这里的obj对象是data对象传到了Vue里面
const obj = {
message: '你好啊',
name: 'super'
}
Object.keys(obj).forEach(key => {
let value = obj[key]
Object.defineProperty(obj, key, {
set(newValue) {
console.log('监听' + key + '改变');
value = newValue
},
get() {
//2.2要获取message的值,就要调用一下get,一旦发现newValue的值发生改变,
//就要通知一下当前属性
console.log('获取' + key + '对应的值');
return value
}
})
})
set里解析html代码,获取到哪些“人”使用了这个属性。在get里获取到message。要获取message的值,就要调用一次get,一旦发现newValue的值发生改变,就要通知一下(这里通知张三或者李四你该改变了)
那么我们就可以只用订阅者模式,让这里的张三/李四/王五订阅set里面属性的改变
// 发布者订阅模式
//订阅者
class Dep {
constructor() {
this.subs = []
}
addSub(watcher){
this.subs.push(watcher)
}
notify(){
this.subs.forEach(item =>{
item.update()
})
}
}
//发布者
class Watcher{
constructor(name) {
this.name = name
}
update(){
console.log(this.name + '发生update');
}
}
const dep = new Dep()
const w1 = new Watcher('张三')
dep.addSub(w1)
dep.notify()
首先我们新建一个Dep的类,构造器里面的subs数组用来记录“谁”要记录我们的属性,此时dep对象就可以用subs数组就所有的订阅者。这里订阅者我起名字为张三 问题又来了,我们怎么知道所有的订阅者在哪里呢?
我们可以添加一个addSub方法,当通知到张三的时候,张三都会自己调用一下update,把界面更新一下。watcher就是订阅者,将watcher追加到addSub方法里面。
随后我们再一次创建一个叫做Watcher的类,此时张三调用了,就创建一个w1对象,w1也就是watcher,将它追击到addSub数组里面。
如果有一天newValue的值发生改变,我们需要新添加一个notify方法,循环所有的订阅者,让订阅者调用update。意味着我现在一旦调用dep.notify(),所有的订阅者发生改变的时候都能获取到改变。
我们用最简单的图解的方式再来解释一遍
- Vue 3.0使用的是ES6原生的Proxy
proxy翻译过来的意思就是”代理“,ES6对Proxy的定位就是target对象(原对象)的基础上通过handler增加一层”拦截“,返回一个新的代理对象,之后所有在Proxy中被拦截的属性,都可以定制化一些新的流程在上面,先看一个最简单的例子。
const obj = {
message: '你好啊',
name: 'super'
}
let vm = new Proxy(obj, {
get(target, key) {
//第一个参数是obj对象,第二个参数是obj中的key
console.log('获取' + key + '对应的值');
return obj[key];
},
set(target, key, newValue) {
//第一个参数是obj对象,第二个参数是obj中的key,第三个参数是要设置的值
console.log('监听' + key + '改变');
if (target[key] === newValue) {
return
}
target[key] = newValue
document.getElementById('app').textContent = target[key]
}
})
//可以进行一个测试
vm.name = '嘻嘻嘻'