Vue


vue2 和 vue3 双向绑定原理

问题:

我们都听过“数据绑定”这个词,其关键在于监听数据的变化,对于obj这个对象,怎么知道obj.name发生了改变呢?

var obj = { name: "Andy" }

defineProperty:

ES5 提供了 Object.defineProperty ,该方法可以在一个对象上定义新属性,或者修改现有属性

语法: Object.defineProperty(obj, prop, descriptor)

参数: obj: 要在其上定义属性的对象。 prop: 要定义或修改的属性的名称。 descriptor: 将被定义或修改的属性的描述符。


getter 和 setter:

        var obj = {}
        var value = null
        Object.defineProperty(obj, "name", {
            get: function () {
                console.log('执行了get')
                return value;
            },
            set: function (newValue) {
                console.log('执行了set')
                value = newValue;
            }
        })

        obj.name = "Andy" // 执行了set
        console.log(obj.name); // 执行了get  // Andy

封装一下:

        function Encapsulation() {
            var value = null
            var namelist = []

            Object.defineProperty(this, 'name', {
                get: function () {
                    console.log('执行了get')
                    return value;
                },
                set: function (value) {
                    console.log('执行了set')
                    value = value;
                    namelist.push({
                        name: value
                    })
                }
            })

            this.getList = function () {
                return namelist;
            }
        }

        var enc = new Encapsulation();
        enc.name; // 执行了get
        enc.name = "Andy"; // 执行了set
        enc.name = "Curry"; // 执行了set
        console.log(enc.getList()); // [{ name: Andy }, { name: Curry }]

重新封装一下:

    <span id="container">1</span>
    <button id="button">点击加 1</button>

    <script>
        function watch(obj, name, func) {
            var value = obj[name];
            Object.defineProperty(obj, name, {
                get: function () {
                    return value;
                },
                set: function (newValue) {
                    value = newValue;
                    func(value)
                }
            });
        }

        var obj = {
            value: 1
        }

        watch(obj, "value", function (newvalue) {
            document.getElementById('container').innerHTML = newvalue;
        })

        document.getElementById('button').addEventListener("click", function () {
            obj.value++
        });
    </script>

Proxy:

defineProperty 只能定义 get 和 set 行为,ES6 提供了 Proxy 构造函数,可以定义更多的行为

语法: var proxy = new Proxy(target, handler)

target 参数表示所要拦截的目标对象,handler参数用来定制拦截行为

        var proxy = new Proxy({}, {
            get: function (obj, prop) {
                console.log('执行get')
                return obj[prop];
            },
            set: function (obj, prop, value) {
                console.log('执行set')
                obj[prop] = value;
            }
        });

        proxy.name = "Andy"; // 执行set
        console.log(proxy.name); // 执行get     // Andy

除了 get 和 set ,proxy 可以拦截多达 13 种操作,比如 has(target, propKey),可以拦截 propKey in proxy 的操作,返回一个布尔值。

        // 使用 has 方法隐藏某些属性,不被 in 运算符发现
        var handler = {
            has(target, key) {
                if (key[0] === '_') {
                    return false;
                }
                return key in target;
            }
        };
        var target = {
            _prop: 'foo',
            prop: 'foo'
        };
        var proxy = new Proxy(target, handler);
        console.log('_prop' in proxy); // false

apply 方法拦截函数的调用:

        var target = function () {
            return '1+1=2';
        };
        var handler = {
            apply: function () {
                return '1+1=3';
            }
        };

        var p = new Proxy(target, handler);
        console.log(p()) // 1+1=3

ownKeys:可以拦截对象自身属性的读取操作

        let target = {
            _name: 'Andy',
            _age: '21',
            address: 'XMU'
        };

        let handler = {
            ownKeys(target) {
                return Reflect.ownKeys(target).filter(key => key[0] !== '_');
            }
        };

        let proxy = new Proxy(target, handler);

        console.log(Object.keys(proxy))     // ["address"]
        console.log(Object.values(proxy))       // ["XMU"]
        console.log(Object.entries(proxy))      // ["address", "XMU"]

最后使用 proxy 写一下 watch :

        function watch(target, func) {
            var proxy = new Proxy(target, {
                get: function (target, prop) {
                    return target[prop];
                },
                set: function (target, prop, value) {
                    target[prop] = value;
                    func(prop, value);
                }
            });
            return proxy;
        }

        var obj = {
            value: 1
        }

        var newObj = watch(obj, function (key, newvalue) {
            if (key == 'value') document.getElementById('container').innerHTML = newvalue;
        })

        document.getElementById('button').addEventListener("click", function () {
            newObj.value += 1
        });

页面列表

ITEM_HTML