• .4-Vue源码之数据劫持(2)


    开播了开播了!

      vue通过数据劫持来达到监听和操作DOM更新,上一节简述了数组变化是如何监听的,这一节先讲讲对象属性是如何劫持的。

        // Line-855
        Observer.prototype.walk = function walk(obj) {
            var keys = Object.keys(obj);
            for (var i = 0; i < keys.length; i++) {
                // Go!
                defineReactive$$1(obj, keys[i], obj[keys[i]]);
            }
        };

      上一节说到这里,现在进入defineReactive$$1来看看具体的劫持过程,函数比较长。

      函数接受4个参数,分别为数据对象、键、值、默认设置,由于这里只传了3个,先不管第4个

        // Line-925
        function defineReactive$$1(
            obj,
            key,
            val,
            customSetter
        ) {
            // 生成一个依赖管理
            var dep = new Dep();
            // getOwnPropertyDescriptor方法以对象形式返回键描述信息
            // 包括enumerable、writable等等
            var property = Object.getOwnPropertyDescriptor(obj, key);
            // 不可修改的键直接返回
            if (property && property.configurable === false) {
                return
            }
    
            // 获取对应的getter与setter
            var getter = property && property.get;
            var setter = property && property.set;
            // 值为对象时的嵌套监听 
            // 当前值为'Hello Vue'的字符串 直接返回
            var childOb = observe(val);
            Object.defineProperty(obj, key, {
                enumerable: true,
                configurable: true,
                get: function reactiveGetter() {
                    // 有getter方法直接调用
                    var value = getter ? getter.call(obj) : val;
                    // 这里暂时为null
                    if (Dep.target) {
                        dep.depend();
                        if (childOb) {
                            childOb.dep.depend();
                        }
                        if (Array.isArray(value)) {
                            dependArray(value);
                        }
                    }
                    // 直接返回值
                    return value
                },
                set: function reactiveSetter(newVal) {
                    // 获取当前值
                    var value = getter ? getter.call(obj) : val;
                    // 这是比较个啥 只有NaN才会不等于自身吧
                    if (newVal === value || (newVal !== newVal && value !== value)) {
                        return
                    }
                    // 这个参数没传 暂时不知道干啥的
                    if ("development" !== 'production' && customSetter) {
                        customSetter();
                    }
                    // 调用默认setter方法或将新值赋给当前值
                    if (setter) {
                        setter.call(obj, newVal);
                    } else {
                        val = newVal;
                    }
                    // 
                    childOb = observe(newVal);
                    // 广播变化
                    dep.notify();
                }
            });
        }

      这里很多方法跳不进去,所以没法调试看效果,等这个搞完,做数据变化调试的时候再来详细解释,目前将就看一下。

      

      由于value只有一个message键,所以一次就结束了,函数疯狂返回,然后回到了observe的构造函数:

        // Line-900
        function observe(value, asRootData) {
            if (!isObject(value)) {
                return
            }
            var ob;
            if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
                ob = value.__ob__;
            } else if (
                observerState.shouldConvert &&
                !isServerRendering() &&
                (Array.isArray(value) || isPlainObject(value)) &&
                Object.isExtensible(value) &&
                !value._isVue
            ) {
                // 跑完这里
                ob = new Observer(value);
            }
            if (asRootData && ob) {
                ob.vmCount++;
            }
            return ob
        }

      

      来看看搞了这么久,ob是个什么东西:

      

      dep是依赖收集数据,包含计数id和依赖数组subs,还有4个原型方法。

      value是被监听数据,除了数据本身,还添加了__ob__属性引用自身,自定义了get和set方法,计数的vmCount。

      原型方法包含一个遍历数组数据的observeArray与监听对象的walk方法。

      接下来的代码将vmCount加1,然后返回这个ob对象,返回到了initData函数:

        // Line-3012
        function initData(vm) {
            // ..格式化、代理
            // ...
            // 监听数据
            observe(data, true /* asRootData */ );
        }

      这个函数也到头了,返回到了initState函数:

        // Line-2948
        function initState(vm) {
            // ...
            // 从这里跳了出来
            if (opts.data) {
                initData(vm);
            } else {
                observe(vm._data = {}, true /* asRootData */ );
            }
            // 剩下的两个参数没有
            if (opts.computed) {
                initComputed(vm, opts.computed);
            }
            if (opts.watch) {
                initWatch(vm, opts.watch);
            }
        }

      好吧,这个也没啥执行的,返回到了最原始的_init初始化函数:

        // Line-3924
        Vue.prototype._init = function(options) {
            // ...各种初始化
            // ... 
            // 从这里跳出来
            initState(vm);
            initProvide(vm); // resolve provide after data/props
            callHook(vm, 'created');
    
            /* istanbul ignore if */
            if ("development" !== 'production' && config.performance && mark) {
                vm._name = formatComponentName(vm, false);
                mark(endTag);
                measure(((vm._name) + " init"), startTag, endTag);
            }
    
            if (vm.$options.el) {
                vm.$mount(vm.$options.el);
            }
        };

       接下来是initProvide函数,provide翻译成中文是准备的意思。

        // Line-3924
        function initProvide(vm) {
            // 没有这个属性 跳
            var provide = vm.$options.provide;
            if (provide) {
                vm._provided = typeof provide === 'function' ?
                    provide.call(vm) :
                    provide;
            }
        }

      然后调用钩子函数,进入created阶段,由于没有定义执行内容,所以直接跳出来,代码就不贴了。

      那个dev内容也不太清楚记录的什么,暂时先不管,下面是双绑的另一个大模块:AST。

      本节先简单结束了,数据劫持也差不多这些,下一节开始跑节点挂载。

      上图:

  • 相关阅读:
    [No0000161]IDEA初步接触
    [No0000171]wpf 类层次结构Class Hierarchy
    [No0000160]常用C# 正则表达式大全
    [No000015D]【李笑来 笔记整理】个人商业模式升级
    thinkphp 系统变量
    thinkphp不读取.env文件的键对值
    thinkphp 模板变量输出替换和赋值
    thinkphp 视图view
    thinkphp 响应对象response
    Thinkphp 请求和响应
  • 原文地址:https://www.cnblogs.com/QH-Jimmy/p/6895163.html
Copyright © 2020-2023  润新知