SOURCE

console 命令行工具 X clear

                    
>
console
/**
 * Object.defineProperty实现响应式
 * 缺点:1.深度监听,需要递归到底,一次性计算量大
 *      2.无法监听新增属性/删除属性(Vue.set、 Vue.delete)
 *      3.无法原生监听数组,需要特殊处理
 * 总结:1.基础API:Object.defineProperty
 *      2.如何监听对象(深度监听),监听数组
 *      3.Object.defineProperty的缺点
 */

// 触发更新视图
function updateView() {
    console.log('更新视图');
}

// 重新定义数组原型
const oldArrayProperty = Array.prototype
// 创建新对象,原型指向 oldArrayProperty ,再扩展新的方法不会影响原型
const arrProto = Object.create(oldArrayProperty);
['push', 'pop', 'shift', 'unshift', 'splice'].forEach(methodName => {
    arrProto[methodName] = function () {
        updateView() // 触发视图更新
        oldArrayProperty[methodName].call(this, ...arguments)
    }
})

// 重新定义属性,监听起来
function defineReactive(target, key, value) {
    // 深度监听
    observe(value)

    // 核心API
    Object.defineProperty(target, key, {
        get() {
            return value
        },
        set(newVal) {
            if (newVal !== value) {
                observe(newVal)
                // 设置新值
                // 注意:value一直在闭包中,此处设置完之后,再get时也是最新的值
                value = newVal

                // 触发更新视图
                updateView()
            }
        }
    })
}

// 监听对象属性
function observe(target) {
    // 不是对象或者数组
    if (typeof target !== 'object' || target === null) {
        return target
    }

    if (Array.isArray(target)) {
        target.__proto__ = arrProto
    }

    // 重新定义各个属性
    for (let key in target) {
        defineReactive(target, key, target[key])
    }
}

// 准备数据
const data = {
    name: 'zhangsan',
    age: 20,
    info: {
        address: 'GZ'
    },
    nums: [1, 2, 3]
}

// 监听数据
observe(data)
data.name = "jack"
// data.age = 22
data.age = { num: 1 }
data.info.address = 'SH'
data.nums.push(4)
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Observe demo</title>
</head>
<body>
    <p>响应式 demo</p>
    <script src="./observe.js"></script>
</body>
</html>