1.在操作data的函数中调用observe方法,观察data中的数据是否有操作
import { observe } from "./observe/index"
//对用户传进来的选项进行操作
export function initState(vm) {
const opts = vm.$options //获取所有的选项
if (opts.data) {
initData(vm)
}
}
//把data上的数据代理到vm身上,这样就可以通过this.xxx获取data中的数据,有个疑问就是这样会不会影响到性能?或者说真的有这个必要吗?通过this._data获取数据到底有什么不好的地方,暂时不清楚,学清楚了再来看吧
function proxy(vm, target, key) {
Object.defineProperty(vm, key, {
get() {
return vm[target][key]
},
set(newValue) {
if (newValue === vm[target][key]) return
vm[target][key] = newValue
}
})
}
//对data中的数据进行操作
function initData(vm) {
let data = vm.$options.data //data可能是函数或对象
data = typeof data === 'function' ? data.call(this) : data
// 对数据进行响应式的劫持
vm._data = data
observe(data) //给data中的每个属性都添加上get和set
for (let key in data)
proxy(vm, '_data', key) //将vm._data用vm代理
}
2.在observe中对data中的属性进行劫持,遍历属性重新定义,有点消耗性能
//Observe/index.js
class Observer {
constructor(data) {
// Object.defineProperty只能劫持已经存在的数据(Vue2中单独写了一些api完成,例如 $set,$delete)
this.walk(data)
}
walk(data) {
// 重新定义属性,性能会差一些,Vue3改用了Proxy
Object.keys(data).forEach(key => defineReactive(data, key, data[key]))
}
}
//进行属性劫持的函数
export function defineReactive(target, key, value) {//闭包,value无法销毁,属性劫持
observe(value) //如果这个value还是一个对象的话,就对value再进行一次属性劫持
Object.defineProperty(target, key, {
// 此处的get并没有参数,我真是个小可爱,写了个value,导致之后读取的值全部都是undefined
get() { //取值 执行get
return value
},
set(newValue) { //赋值 执行set
if (newValue === value) return
observe(newValue) //赋值的时候如果赋值的是一个对象,再次进行劫持
value = newValue
}
})
}
export function observe(data) {
if (typeof data !== 'object' || data === null) {
// 只对对象进行劫持
return
}
// 如果数据已经劫持过了,就不在劫持了(增添一个判断数据是否已被劫持的类)
return new Observer(data)
}