实例化 Vue 都发生了什么?

141 阅读2分钟

实例化 Vue

    <div id="app">原来的dom {{message}}</div>
    
    // 非单文件组件
    new Vue({
        el:"#app",
        data:{
            msg:"hello world!"
        },
        // 不存在 template 时,vue会对原来的dom进行编译,
        // 存在 template 时,template 编译完成会替换原来的dom
        template:`
            <h1>模板里的dom {{message}}</h1>
        `
    });
    
    // 单文件组件
    new Vue({
        render:h=>h(App);
    }).$mount("#app");

image.png

Vue 构造函数

    import { initMixin } from "./init";
    
    import { stateMinxin } from "./state";
    import { renderMixin } from "./render";
    import { eventsMixin } from "./events";
    import { lifecycleMixin } from "./lifecycle"

    function Vue(options) {
        // 不使用 new 操作符调用 Vue 时的警告

        this._init(options);
    }

    initMixin(Vue);  // * 向 Vue 的原型上挂载 _init 方法,进行初始化  

    stateMinxin(Vue);  // 向 Vue 的原型上挂载 $set、$watch、$delete 实例方法
    eventsMixin(Vue);  // 向 Vue 原型上挂载 $on、$emit、$off、$once 方法
    lifecycleMixin(Vue);  // 挂载与生命周期相关的方法,$forceUpdate 和 $destroy 
    renderMixin(Vue);  // 目前只了解到,向 Vue 原型上挂载$nextTick

    export default Vue;

Vue.js通过调用 initMixin 方法将 _init 挂载到Vue 构造函数的原型上,然后调用this._init(options)来执行生命周期的初始化流程,_init 的挂载和内部实现如下:

    export function initMixin(Vue) {
    Vue.prototype._init = function (options) {
        // 操作 options
        initLifecycle(vm);  // 向实例中挂载属性
        initEvents(vm);
        initRender(vm);
        callHook(vm, 'beforeCreate');   // 从Vue实例的配置项中获取并触发用户设置的生命周期钩子
        initInjections(vm) // 在data、props前初始化inject
        initState(vm)  // 初始化状态props、methods、data、computed、watch
        initProvide(vm) // 在data、props后初始化provide
        callHook(vm, 'created')

        // 如果用户在实例化Vue.js时传递了el选项,则自动开启模板编译阶段与挂载阶段
        // 如果没有传递el选项,则不进入下一个生命周期流程,用户需要执行vm.$mount方法,手动开启模板编译阶段与挂载阶段

        if (vm.$options.el) {
            vm.$mount(vm.$options.el)
        }
    }
}

initLifecycle 函数

向 vue 实例中挂载属性。该函数接收 vue 实例作为参数。只需要向 vue 实例设置属性就可以挂载到 vue 实例上,包括以 美元符号 开头提供给用户使用的属性($$ref、$parent),以 _ 开头提供给内部使用的属性(_data、_watcher)。

initEvents 函数

初始化的事件,指的是父组件在模板中使用 v-on 监听子组件内触发的事件。在模板编译阶段,被编译的标签为组件标签时,会实例化子组件,同时将标签上注册的事件解析成 object 并通过参数传递给子组件。下面要讲的 props 状态也是如此。

initState 函数

  1. 初始化状态,当在创建 vue 实例时,只要向其中配置了哪些状态会在 initState 函数中进行初始化,没有配置就不需要初始化。
  2. 初始化的顺序也是精心设计的,先初始化props ,后初始化data ,这样就可以在data中使用props 中 的数据了。在watch 中既可以观察props ,也可以观察data。

image.png

规格化 props

props 的注意点:

    // 如果在父组件的模板中使用这样的语法:
    <child user-name="berwin" user-age="18" user-gender="男"></child>
    // 错误写法
    props:['user-name','user-age','user-gender']
    // Vue 内部使调用 camelize 函数将 props 名称驼峰化,即可以将 a-b 这样的名称转换成aB 。
    // 正确写法
    props:['userName','userAge','userGender']

当 props 通过数组指定需要哪些属性。但在 Vue 内部,数组格式的 props 将被规格化成对象格式。

    // 规格化前
    // props:['userName','userAge','userGender']
    
    // 部分源码
    if (Array.isArray(props)) { 
        i = props.length 
        while (i--) { 
            val = props[i] 
            if (typeof val === 'string') { 
            name = camelize(val) 
            res[name] = { type: null } 
        }
    }
    options.props = res;
    
    // 规格化后
    props:{
        userName:{ type : null },
        userAge:{ type : null },
        userGender:{ type : null }
    }

初始化 Methods

初始化 Methods 需要,循环选项中的 methods 对象,依次对每一个属性进行检验,然后挂载到 vue实例上。 检验条件有三个(不满足其中一个控制台就会警告):

    1、同时有key和value;
    2、不与 props 中的属性重名;
    3、不以 $ 和 _ 开头。

挂载:

    vm[key] = methods[key] == null ? noop : bind(methods[key], vm)

未完待续 . . .