Skip to content

响应式

数据与视图之间的联动关系

数据在被更改的时候,开发者不用手动更新视图,框架底层通过viewmodel帮我们追踪一个数据的变化,然后自动更新视图

vue2

定义对象属性的getter和setter,实现数据劫持

javascript
const oldArrayPrototype = Array.prototype;
const newArrayPrototype = Object.create(oldArrayPrototype);
['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'].forEach((methodName) => {
    newArrayPrototype[methodName] = function() {
        oldArrayPrototype[methodName].call(this, ...arguments)
        updateView();
    }
})
function observe(target) {
  if (typeof target !== "object" || target == null) {
    return;
  }
  if (Array.isArray(target)) {
    target.__proto__ = newArrayPrototype
  }
  for (let key in target) {
    // 深度监听
    observe(target[key])
    defineReactive(target, key, target[key])
  }
}

// 重新定义属性,监听起来
function defineReactive(target, key, value) {
    Object.defineProperty(target, key, {
        get() {
            return value
        },
        set(newValue) {
            if (newValue !== value) {
                value = newValue;
                console.log('数据更新了')
                observe(newValue)
                updateView()
            }
        }
    })
}

function updateView() {
    console.log('视图更新了')
}
const data = {
    name: 'woku',
    age: 28,
    info: {
        address: 'beijing'
    },
    number: [1,2,3]
}

observe(data)
data.number.push(4)
 Object.defineProperty的缺点

 深度监听,需要递归到底,一次性计算量大

 无法监听新增属性和删除属性 (Vue.set, Vue.delete)

 无法原生监听数组,需要特殊处理。

vue3

使用ES6的proxy

javascript
function reactive(data) {
    return new Proxy(data, {
        get(target, key) {
            const value = Reflect.get(target, key)
            return value != null && typeof value === 'object' ? reactive(value) : value;
        },
        set(target, key, value) {
            console.log(target, key, value)
            console.log("属性被修改了")
            // 为啥不直接用target[key] = value
            // 使用函数的方式更加符合语义化。
            return Reflect.set(target, key, value)
        }
    })
}

const $data = reactive({
    a: 1,
    b: 2,
    c: [1, 2, 3],
    d: {
        e: 4,
        f: 5
    }
})
console.log($data.a);
$data.b = 3

$data.c.push(4)

proxy不用一次性递归到底,而是当访问到某一个属性时,如果值是一个对象类型,那么才再创建一个代理对象。

例如:当访问$data.d时候,会将{e: 4, f: 5}创建代理