VUE源码解析-2- initMixin()

写在前面的话:

因为源码中包含很多校验或者补丁的方法,这些代码暂时不考虑,以后的文章中只分析主干流程核心代码。

 

上一篇我们找到的 Vue 的构造函数所在文件 /src/core/instance/index.js

我们看到这个文件代码非常简单,先是定义vue的构造函数,里边除去校验,只剩下 调用 this._init(options)(_inint来自下边的 initMixin)。接着是5个方法。他们的作用分别是:

这篇我们主要分析 Vue的 initMixin 方法。

initMixin 主体

打开 src/core/instance/init.js,initMixin 中主要是 初始化内部组件initInternalComponent 、 合并参数resolveConstructorOptions,并做了生命周期、事件、渲染等初始化(详见下边代码处注释)

/* @flow */

import config from '../config'
import { initProxy } from './proxy'
import { initState } from './state'
import { initRender } from './render'
import { initEvents } from './events'
import { mark, measure } from '../util/perf'
import { initLifecycle, callHook } from './lifecycle'
import { initProvide, initInjections } from './inject'
import { extend, mergeOptions, formatComponentName } from '../util/index'

let uid = 0

export function function initMixin(Vue) {
        Vue.prototype._init = function (options) { //初始化函数

            ...
            // a flag to avoid this being observed 一个避免被观察到的标志
            vm._isVue = true;
            // merge options 合并选项 参数
            if (options && options._isComponent) { //判断是否是组件
                // optimize internal component instantiation
                // since dynamic options merging is pretty slow, and none of the
                // internal component options needs special treatment.
                //优化内部组件实例化
                //因为动态选项合并非常慢,没有一个是内部组件选项需要特殊处理。
                //初始化内部组件
                initInternalComponent(vm, options);
            } else {
                //合并参数 将两个对象合成一个对象 将父值对象和子值对象合并在一起,并且优先取值子值,如果没有则取子值
                vm.$options = mergeOptions(
                    resolveConstructorOptions(vm.constructor), //  //解析constructor上的options属性的
                    options || {},
                    vm
                );
            }
            /* istanbul ignore else */
            
            //初始化 代理 监听
            if (process.env.NODE_ENV !== 'production') {
              initProxy(vm)
            } else {
              vm._renderProxy = vm
            }

            // expose real self 暴露真实的self
            vm._self = vm;
            initLifecycle(vm); //初始化生命周期 标志
            initEvents(vm); //初始化事件
            initRender(vm); // 初始化渲染
            callHook(vm, 'beforeCreate'); //触发beforeCreate钩子函数
            initInjections(vm); // resolve injections before data/props 在数据/道具之前解决注入问题 //初始化 inject
            initState(vm);  //    //初始化状态
            initProvide(vm); // resolve provide after data/props  解决后提供数据/道具  provide 选项应该是一个对象或返回一个对象的函数。该对象包含可注入其子孙的属性,用于组件之间通信。
            callHook(vm, 'created'); //触发created钩子函数

            
           ...

            if (vm.$options.el) {
                // Vue 的$mount()为手动挂载,
                // 在项目中可用于延时挂载(例如在挂载之前要进行一些其他操作、判断等),之后要手动挂载上。
                // new Vue时,el和$mount并没有本质上的不同。
                vm.$mount(vm.$options.el);
            }
        };

    }

// initInternalComponent 初始化内部组件 

function initInternalComponent(vm,  //vue实例
                                   options //选项参数
    ) {
        var opts = vm.$options = Object.create(vm.constructor.options); //vm的参数
        // doing this because it's faster than dynamic enumeration. 这样做是因为它比动态枚举快。
        // var options = {
        //     _isComponent: true, //是否是组件
        //     parent: parent, //组件的父节点
        //     _parentVnode: vnode, //组件的 虚拟vonde 父节点
        //     _parentElm: parentElm || null, //父节点的dom el
        //     _refElm: refElm || null //当前节点 el
        // }
        var parentVnode = options._parentVnode;
        opts.parent = options.parent; //组件的父节点
        opts._parentVnode = parentVnode; //组件的 虚拟vonde 父节点
        opts._parentElm = options._parentElm; //父节点的dom el
        opts._refElm = options._refElm; //当前节点 el

        var vnodeComponentOptions = parentVnode.componentOptions; //组件参数
        opts.propsData = vnodeComponentOptions.propsData; //组件数据
        opts._parentListeners = vnodeComponentOptions.listeners;//组件 事件
        opts._renderChildren = vnodeComponentOptions.children;  //组件子节点
        opts._componentTag = vnodeComponentOptions.tag; //组件的标签

        if (options.render) { //渲染函数
            opts.render = options.render; //渲染函数
            opts.staticRenderFns = options.staticRenderFns; //静态渲染函数
        }
    }

// resolveConstructorOptions:解析new Vue constructor上的options拓展参数属性的 合并 过滤去重数据

function resolveConstructorOptions(Ctor) {
        var options = Ctor.options;
        // 有super属性,说明Ctor是Vue.extend构建的子类 继承的子类
        if (Ctor.super) { //超类
            var superOptions = resolveConstructorOptions(Ctor.super); //回调超类 表示继承父类
            var cachedSuperOptions = Ctor.superOptions; // Vue构造函数上的options,如directives,filters,....
            if (superOptions !== cachedSuperOptions) { //判断如果 超类的options不等于子类的options 的时候
                // super option changed,
                // need to resolve new options.
                //超级选项改变,
                //需要解决新的选项。
                Ctor.superOptions = superOptions; //让他的超类选项赋值Ctor.superOptions
                // check if there are any late-modified/attached options (#4976) 检查是否有任何后期修改/附加选项(#4976)
                // 解决修改选项 转义数据 合并 数据
                var modifiedOptions = resolveModifiedOptions(Ctor);
                // update base extend options 更新基本扩展选项
                if (modifiedOptions) {
                    //extendOptions合并拓展参数
                    extend(Ctor.extendOptions, modifiedOptions);
                }
                // 优先取Ctor.extendOptions 将两个对象合成一个对象 将父值对象和子值对象合并在一起,并且优先取值子值,如果没有则取子值
                options = Ctor.options = mergeOptions(superOptions, Ctor.extendOptions);
                if (options.name) { //如果参数含有name 组件name

                    options.components[options.name] = Ctor;
                }
            }
        }
        return options //返回参数
    }

// resolveModifiedOptions: 解决修改options 转义数据 合并 数据

function resolveModifiedOptions(Ctor) {
        var modified;
        var latest = Ctor.options; //获取选项
        var extended = Ctor.extendOptions;  //获取拓展的选项
        var sealed = Ctor.sealedOptions;  //获取子类选项
        for (var key in latest) {  //遍历最新选项
            if (latest[key] !== sealed[key]) {  //如果选项不等于子类选项
                if (!modified) {
                    modified = {};
                }
                //合并参数
                modified[key] = dedupe(latest[key], extended[key], sealed[key]);
            }
        }
        //返回合并后的参数
        return modified
    }

1. initLifecycle

初始化生命周期

目录: src/core/instance/lifecycle.js

//初始化生命周期
    function initLifecycle(vm) {
        var options = vm.$options;

        // locate first non-abstract parent
        //定位第一个非抽象父节点
        var parent = options.parent;
        if (parent && !options.abstract) {
            //判断parent父亲节点是否存在,并且判断抽象节点是否存在
            while (parent.$options.abstract && parent.$parent) {
                //如果有父亲抽象节点,则把父层或爷爷节点   给当前节点的父亲节点
                parent = parent.$parent;
            }
            //子节点添加 vm
            parent.$children.push(vm);
        }
        //添加$parent 参数
        vm.$parent = parent;

        //判断parent 是否是顶层 root 如果是 则$root赋值给$root
        vm.$root = parent ? parent.$root : vm;

        // 情况 $children 节点
        vm.$children = [];
        //获取节点的key
        vm.$refs = {};

        vm._watcher = null; //观察者
        vm._inactive = null; //禁用的组件状态标志
        vm._directInactive = false;  // 不活跃 禁用的组件标志
        vm._isMounted = false; //标志是否 触发过 钩子Mounted
        vm._isDestroyed = false; //是否已经销毁的组件标志
        vm._isBeingDestroyed = false; //是否已经销毁的组件标志 如果为true 则不触发 beforeDestroy 钩子函数 和destroyed 钩子函数
    }

2.initEvents(vm)

初始化事件

目录:src/core/instance/events.js

function initEvents(vm) {
        vm._events = Object.create(null);
        vm._hasHookEvent = false;
        // init parent attached events  初始化 父亲事件
        var listeners = vm.$options._parentListeners;
        if (listeners) {
            //更新组件事件
            updateComponentListeners(vm, listeners);
        }
    }

3.initRender(vm)

初始化渲染

目录:src/core/instance/render.js

function initRender(vm) {
        //vm 是Vue 对象
        vm._vnode = null; // the root of the child tree 上一个 vonde
        vm._staticTrees = null; // v-once cached trees v-once缓存的树
        var options = vm.$options; //获取参数
        var parentVnode = vm.$vnode = options._parentVnode; // the placeholder node in parent tree 父树中的占位符节点
        var renderContext = parentVnode && parentVnode.context; // this 上下文
        //判断children 有没有分发式插槽 并且过滤掉空的插槽,并且收集插槽
        vm.$slots = resolveSlots(options._renderChildren, renderContext);
        vm.$scopedSlots = emptyObject;
        // bind the createElement fn to this instance
        // so that we get proper render context inside it.
        // args order: tag, data, children, normalizationType, alwaysNormalize
        // internal version is used by render functions compiled from templates
        //将createElement fn绑定到这个实例
        //这样我们就得到了合适的渲染上下文。
        // args order: tag, data, children, normalizationType, alwaysNormalize
        //内部版本由模板编译的呈现函数使用
        //创建虚拟dom的数据结构
        vm._c = function (a, b, c, d) {
            console.log(a)
            console.log(b)
            console.log(c)
            console.log(d)

            return createElement(
                                    vm, //vm  new Vue 实例化的对象
                                    a, //有可能是vonde或者指令
                                    b,
                                    c,
                                    d,
                                    false
                                 );
        };
        // normalization is always applied for the public version, used in
        //的公共版本总是应用规范化
        // user-written render functions.
        //用户编写的渲染功能。
        vm.$createElement = function (a, b, c, d) {

            return createElement(vm, a, b, c, d, true);
        };

        // $attrs & $listeners are exposed for easier HOC creation.
        // they need to be reactive so that HOCs using them are always updated
        // $attrs和$listener将被公开,以便更容易地进行临时创建。
        //它们需要是反应性的,以便使用它们的HOCs总是更新的
        var parentData = parentVnode && parentVnode.data; //获取父vnode

        /* istanbul ignore else */
        {
            // 通过defineProperty的set方法去通知notify()订阅者subscribers有新的值修改
            defineReactive(
                vm,
                '$attrs',
                parentData && parentData.attrs || emptyObject,
                function () {
                    !isUpdatingChildComponent && warn("$attrs is readonly.", vm);
                },
                true
            );
            // 通过defineProperty的set方法去通知notify()订阅者subscribers有新的值修改
            defineReactive(vm, '$listeners', options._parentListeners || emptyObject, function () {
                !isUpdatingChildComponent && warn("$listeners is readonly.", vm);
            }, true);
        }
    }

4. initInjections(vm)

resolve injections before data/props 在数据/道具之前解决注入问题 //初始化 inject

目录:src/core/instance/inject.js

function initInjections(vm) {
        //provide 和 inject 主要为高阶插件/组件库提供用例。并不推荐直接用于应用程序代码中。
        //这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在起上下游关系成立的时间里始终生效。如果你熟悉 React,这与 React 的上下文特性很相似。
        //更多详情信息https://cn.vuejs.org/v2/api/#provide-inject
        var result = resolveInject(vm.$options.inject, vm);
        if (result) {
            toggleObserving(false);
            Object.keys(result).forEach(function (key) { //注入的值不能修改,相当于props属性一样
                /* istanbul ignore else */
                {
                    // 通过defineProperty的set方法去通知notify()订阅者subscribers有新的值修改
                    //  * 添加观察者 get set方法
                    defineReactive(
                        vm,
                        key,
                        result[key],
                        function () {
                            warn(
                                "Avoid mutating an injected value directly since the changes will be " +
                                "overwritten whenever the provided component re-renders. " +
                                "injection being mutated: \"" + key + "\"",
                                vm
                            );
                        });
                }
            });
            toggleObserving(true);
        }
    }

5. initState(vm)

初始化状态

目录:src/core/instance/state.js

//初始化状态
    function initState(vm) {
        vm._watchers = []; //初始化观察者队列
        var opts = vm.$options; //初始化参数
        //判断是否有props属性,如果有则添加观察者
        if (opts.props) {
            //初始化props 检验props 数据格式是否是规范的如果是规范的则添加到观察者队列中
            initProps(vm, opts.props);
        }


        if (opts.methods) { //事件
            //   初始化事件Methods 把事件 冒泡到 vm[key] 虚拟dom  最外层中
            initMethods(vm, opts.methods);
        }
        if (opts.data) { //初始化数据
            // 初始化数据 获取options.data 的数据 将他们添加到 监听者中
            console.log(vm)

            initData(vm);
            console.log(vm)

        } else {
            console.log('vm._data')
            console.log(vm._data)


            //  判断value 是否有__ob__    实例化 dep对象,获取dep对象  为 value添加__ob__ 属性,把vm._data添加到观察者中  返回 new Observer 实例化的对象
            observe(vm._data = {}, true /* asRootData */);

        }
        if (opts.computed) { //计算属性
            //初始化计算属性 并且判断属性的key 是否 在 data ,将 计算属性的key 添加入监听者中
            initComputed(vm, opts.computed);
        }
        //options 中的 watch
        if (opts.watch && opts.watch !== nativeWatch) {
            //初始化Watch
            initWatch(vm, opts.watch);
        }
    }

6. initProvide(vm)

resolve provide after data/props 解决后提供数据/道具 provide 选项应该是一个对象或返回一个对象的函数。该对象包含可注入其子孙的属性,用于组件之间通信。

目录: src/core/instance/inject.js

/*
     provide 选项应该是一个对象或返回一个对象的函数。该对象包含可注入其子孙的属性,用于组件之间通信。
     *  */
    function initProvide(vm) {
        var provide = vm.$options.provide; //provide 选项应该是一个对象或返回一个对象的函数。该对象包含可注入其子孙的属性。
        if (provide) {  //判断provide 存在么
            vm._provided = typeof provide === 'function' //判断是否是函数如果是函数则执行
                ? provide.call(vm)
                : provide;
        }
    }

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

前端卡卡西呀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值