vue原理分析(十三)研究new Vue()中的 initState

在Vue.prototype._init 中有一些init函数,今天我们来研究这些init函数

Vue.prototype._init = function (options) {
  ......
  {
    initProxy(vm);
  }
  ......
  initLifecycle(vm);
  initEvents(vm);
  initRender(vm);
  callHook$1(vm, 'beforeCreate', undefined, false /* setContext */);
  initInjections(vm); // resolve injections before data/props
  initState(vm);
  initProvide(vm); // resolve provide after data/props
  callHook$1(vm, 'created');
  ......
}


上一篇中已经研究了 initInjections ,今天我们往下研究

initState(vm);
function initState(vm) {
    const opts = vm.$options;
    if (opts.props)
        initProps$1(vm, opts.props);
    // Composition API
    initSetup(vm);
    if (opts.methods)
        initMethods(vm, opts.methods);
    if (opts.data) {
        initData(vm);
    }
    else {
        const ob = observe((vm._data = {}));
        ob && ob.vmCount++;
    }
    if (opts.computed)
        initComputed$1(vm, opts.computed);
    if (opts.watch && opts.watch !== nativeWatch) {
        initWatch(vm, opts.watch);
    }
}


在这个地方,就是初始化state 
这里主要关注是顺序
props->methods->data->computed->watch我们按顺序来研究

function initProps$1(vm, propsOptions) {
    const propsData = vm.$options.propsData || {};
    const props = (vm._props = shallowReactive({}));
    // cache prop keys so that future props updates can iterate using Array
    // instead of dynamic object key enumeration.
    // 缓存prop的keys以便将来更新props时可以使用数组代替动态的迭代对象key
    const keys = (vm.$options._propKeys = []);
    // 判断是否是根组件
    const isRoot = !vm.$parent;
    // root instance props should be converted
    if (!isRoot) {
        // 如果不是根组件就关闭响应式处理,防止defineReactive做响应式处理
        toggleObserving(false);
    }
    for (const key in propsOptions) {
        keys.push(key);
        // 对prop数据进行校验
        const value = validateProp(key, propsOptions, propsData, vm);
        /* istanbul ignore else */
        {
            const hyphenatedKey = hyphenate(key);
            // 检查属性是否是保留属性
            if (isReservedAttribute(hyphenatedKey) ||
                config.isReservedAttr(hyphenatedKey)) {
                warn$2(`"${hyphenatedKey}" is a reserved attribute and cannot be used as component prop.`, vm);
            }
            defineReactive(props, key, value, () => {
                if (!isRoot && !isUpdatingChildComponent) {
                    warn$2(`Avoid mutating a prop directly since the value will be ` +
                        `overwritten whenever the parent component re-renders. ` +
                        `Instead, use a data or computed property based on the prop's ` +
                        `value. Prop being mutated: "${key}"`, vm);
                }
            }, true /* shallow */);
        }
        // static props are already proxied on the component's prototype
        // during Vue.extend(). We only need to proxy props defined at
        // instantiation here.
        // 在Vue.extend()过程中,静态props已经在组件的原型上被代理。我们只需要在这里实例化时代理定义的props。
        if (!(key in vm)) {
            proxy(vm, `_props`, key);
        }
    }
    // 开启响应式处理
    toggleObserving(true);
}
function initMethods(vm, methods) {
    const props = vm.$options.props;
    // 循环遍历methods
    for (const key in methods) {
        {
            // 类型不是 function ,发出警告
            if (typeof methods[key] !== 'function') {
                warn$2(`Method "${key}" has type "${typeof methods[key]}" in the component definition. ` +
                    `Did you reference the function correctly?`, vm);
            }
            // props已经有该函数名,发出警告
            if (props && hasOwn(props, key)) {
                warn$2(`Method "${key}" has already been defined as a prop.`, vm);
            }
            // 数名不能以_和$开头
            if (key in vm && isReserved(key)) {
                warn$2(`Method "${key}" conflicts with an existing Vue instance method. ` +
                    `Avoid defining component methods that start with _ or $.`);
            }
        }
        // 将methods绑定在当前实例上
        vm[key] = typeof methods[key] !== 'function' ? noop : bind$1(methods[key], vm);
    }
}
function initData(vm) {
    let data = vm.$options.data;
    // 通过getData将函数转为对象
    data = vm._data = isFunction(data) ? getData(data, vm) : data || {};
    // 一个组件的 data 选项必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝
    // 避免了实例之间相互影响
    if (!isPlainObject(data)) {
        data = {};
        warn$2('data functions should return an object:\n' +
                'https://v2.vuejs.org/v2/guide/components.html#data-Must-Be-a-Function', vm);
    }
    // proxy data on instance
    const keys = Object.keys(data);
    const props = vm.$options.props;
    const methods = vm.$options.methods;
    let i = keys.length;
    while (i--) {
        const key = keys[i];
        {
            // 检查key名是否在methods中已经使用
            if (methods && hasOwn(methods, key)) {
                warn$2(`Method "${key}" has already been defined as a data property.`, vm);
            }
        }
        // 检查key名是否在props中使用
        if (props && hasOwn(props, key)) {
            warn$2(`The data property "${key}" is already declared as a prop. ` +
                    `Use prop default value instead.`, vm);
        }
        // 检查key名是否符合规范,即不以$和_开头
        else if (!isReserved(key)) {
            // 代理数据 |本质上还是 Object.defineProperty
            proxy(vm, `_data`, key);
        }
    }
    // observe data
    // observe data | 响应式数据
    const ob = observe(data);
    ob && ob.vmCount++;
}
// 获取data内容|转为对象
function getData(data, vm) {
    // #7573 disable dep collection when invoking data getters
    pushTarget();
    try {
        return data.call(vm, vm);
    }
    catch (e) {
        handleError(e, vm, `data()`);
        return {};
    }
    finally {
        popTarget();
    }
}

const computedWatcherOptions = { lazy: true };
function initComputed$1(vm, computed) {
    // $flow-disable-line
    // 创建一个空对象
    const watchers = (vm._computedWatchers = Object.create(null));
    // computed properties are just getters during SSR
    // 计算的内容只是SSR期间的getter
    // 是否服务端渲染
    const isSSR = isServerRendering();
    for (const key in computed) {
        const userDef = computed[key];
        const getter = isFunction(userDef) ? userDef : userDef.get;
        if (getter == null) {
            warn$2(`Getter is missing for computed property "${key}".`, vm);
        }
        if (!isSSR) {
        // create internal watcher for the computed property.
        // 不是服务端渲染,就为计算属性创建内部观察程序。
        // noop 空函数:function() {}
            watchers[key] = new Watcher(vm, getter || noop, noop, computedWatcherOptions);
        }
        // component-defined computed properties are already defined on the
        // component prototype. We only need to define computed properties defined
        // at instantiation here.
        if (!(key in vm)) {
            defineComputed(vm, key, userDef);
        }
        else {
            // 检查key名是否被过早的定义在data,props,methods中
            if (key in vm.$data) {
                warn$2(`The computed property "${key}" is already defined in data.`, vm);
            }
            else if (vm.$options.props && key in vm.$options.props) {
                warn$2(`The computed property "${key}" is already defined as a prop.`, vm);
            }
            else if (vm.$options.methods && key in vm.$options.methods) {
                warn$2(`The computed property "${key}" is already defined as a method.`, vm);
            }
        }
    }
}

function defineComputed(target, key, userDef) {
    const shouldCache = !isServerRendering();
    // 是否服务端渲染
    // 定义get和set
    if (isFunction(userDef)) {
        sharedPropertyDefinition.get = shouldCache
            ? createComputedGetter(key)
            : createGetterInvoker(userDef);
        sharedPropertyDefinition.set = noop;
    }
    else {
        sharedPropertyDefinition.get = userDef.get
            ? shouldCache && userDef.cache !== false
                ? createComputedGetter(key)
                : createGetterInvoker(userDef.get)
            : noop;
        sharedPropertyDefinition.set = userDef.set || noop;
    }
    // set 是 noop, 是个空函数
    if (sharedPropertyDefinition.set === noop) {
        sharedPropertyDefinition.set = function () {
            warn$2(`Computed property "${key}" was assigned to but it has no setter.`, this);
        };
    }
    Object.defineProperty(target, key, sharedPropertyDefinition);
}
function createComputedGetter(key) {
    return function computedGetter() {
        const watcher = this._computedWatchers && this._computedWatchers[key];
        if (watcher) {
        // 是否被计算过,如果dirty为true表明未被计算过
            if (watcher.dirty) {
                // 调用watcher.get方法,值会保存在watcher.value上
                watcher.evaluate();
            }
            if (Dep.target) {
                if (Dep.target.onTrack) {
                    Dep.target.onTrack({
                        effect: Dep.target,
                        target: this,
                        type: "get" /* TrackOpTypes.GET */,
                        key
                    });
                }
                watcher.depend();
            }
            return watcher.value;
        }
    };
}
function createGetterInvoker(fn) {
    return function computedGetter() {
        return fn.call(this, this);
    };
}
function initWatch(vm, watch) {
    // 遍历 watch 
    for (const key in watch) {
        const handler = watch[key];
        // 判断是否是数组
        // 如果是数组就给数组中的每一个 循环创建watcher监听回调函数
        if (isArray(handler)) {
            for (let i = 0; i < handler.length; i++) {
                createWatcher(vm, key, handler[i]);
            }
        }
        else {
            // 创建watcher监听回调函数
            createWatcher(vm, key, handler);
        }
    }
}
function createWatcher(vm, expOrFn, handler, options) {
    // 检查是否是普通对象
    if (isPlainObject(handler)) {
        options = handler;
        // 提取handler属性赋值给handler
        handler = handler.handler;
    }
    // 是否是字符串
    if (typeof handler === 'string') {
        // 从实例中找到handler赋值给handler
        handler = vm[handler];
    }
    // 实例上的$watch方法
    return vm.$watch(expOrFn, handler, options);
}

  • 5
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值