Vue响应式简单的实现

53 阅读1分钟

参考链接 www.bilibili.com/video/BV1LE…

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>

</body>
<script>
    const ARRAY_METHOD = [
        'push',
        'pop',
        'shift',
        'unshift',
        'reverse',
        'sort',
        'splice',
    ];
    let array_methods = Object.create(Array.prototype);
    /**
     * 拦截数组指定的方法
     */
    ARRAY_METHOD.forEach(method => {
        array_methods[method] = function () {
            // 若数组更新元素,也将其参数响应式化
            for (let i = 0; i < arguments.length; i++) {
                reactive(arguments[i]);
            }
            return Array.prototype[method].apply(this, arguments);
        }
    });

    /**
     * 响应式数据
     * @param target 要操作的目标对象
     * @param key 键名
     * @param value  键值
     * @param enumerable
     */
    function defineReactive(target, key, value, enumerable) {
        // 是非数组的引用类型
        if (typeof value === 'object' && value != null && !Array.isArray(value)) {
            reactive(value);
        }
        Object.defineProperty(target, key, {
            configurable: true,
            enumerable: !!enumerable,
            get() {
                return value;
            },
            set(newVal) {
                value = newVal;
            }
        });
    }

    /**
     * 遍历目标对象 给每个属性添加响应式
     * @param o 要操作的目标对象
     */
    function reactive(o) {
        let keys = Object.keys(o);
        for (let i = 0; i < keys.length; i++) {
            let key = keys[i]; // 属性名
            let value = o[key];
            if (Array.isArray(value)) {
                Object.setPrototypeOf(value, array_methods)
                for (let j = 0; j < value.length; j++) {
                    reactive(value[j]);
                }
            } else {
                // 对象或值类型
                defineReactive(o, key, value, true);
            }
        }
    }

    let data = {
        name: "bwf",
        user: {age: 18},
        list: [{name: "xxx"}]
    }
    reactive(data)
</script>
</html>