阅读 63

vue初始化做得那些事

先从new Vue()的小例子来看,vue初始化的运行;vue源码下载地址请转到vue源码下载及运行笔记

<!DOCTYPE html>
<html lang="en">
    <head>  
        <meta charset="UTF-8">  
        <meta http-equiv="X-UA-Compatible" content="IE=edge">  
        <meta name="viewport" content="width=device-width, initial-scale=1.0">  
        <title>源码解读</title>  
        <script src="../dist/vue.js"></script>
    </head>
    <body>  
        <div id="app">{{msg}}</div>  
        <script>    
            debugger    
            new Vue({      
                el: '#app',      
                data: {        
                    msg: '源码解读'      
                }    
            })  
        </script>
    </body>
</html>
复制代码

在浏览器中运行代码,然后debugger查看

通过上图可以看到,在调用vue时,首先会判断vue是否已经实例化,如果没有实例化在控制台会提示必须由关键字new调用。接下就是调用vue的初始化方法this._init(options)进行初始化。

追踪来源this._init()

小例子中的vue文件引入是在dist文件夹中,dist文件夹是混淆后的文件。从vue的源码构建过程可以知道所有vue的开发源码都在src/core文件加下。打开core文件夹的index.js文件。

import Vue from './instance/index'
export default Vue
复制代码

由第一行可以找到instance文件下的index.js文件。在这个文件中就看到动画中的vue实例化的判断和初始化的调用。initMixin(Vue)将Vue作为参数实现了_init()方法的挂载

import { initMixin } from './init'
function Vue (options) { 
// 如果不是生产环境且不是用new实例化vue,在控制台输出警告  
    if (process.env.NODE_ENV !== 'production' &&    !(this instanceof Vue)  ) {    
        warn('Vue is a constructor and should be called with the `new` keyword')  
    }  
// 执行初始化  
this._init(options)}
// 挂载初始化方法initMixin(Vue)
// 导出vue实例,添加全局方法export default Vue
复制代码

init文件中initMixin方法实现代码如下:

let uid = 0
export function initMixin (Vue) {  
// 在vue原型上添加初始化方法_init()方法  
// 接收原始对象类型的options参数,为非必传  
    Vue.prototype._init = function (options?: Object) {    
        // 将实例对象赋值给vm变量    
        // 这里会再次进行component类型检查确保vm接收到的是Vue类的实例    
        const vm = this    
        // a uid    
        vm._uid = uid++    
        vm._isVue = true    
        // 合并option对象    
        if (options && options._isComponent) {      
            // 内部组件的options初始化      
            initInternalComponent(vm, options)    
        } else {      
            // 执行option的合并函数,并赋值给vm的公共属性      
            // 在这里的合并函数主要是解决与继承自父类的配置对象的合并      
            vm.$options = mergeOptions(        
               resolveConstructorOptions(vm.constructor),        
               options || {},        
               vm      )    
        }    
        /* istanbul ignore else */    
        if (process.env.NODE_ENV !== 'production') {      
            initProxy(vm)    
        } else {     
             vm._renderProxy = vm    
        }      
        // 暴露实例对象    
        vm._self = vm    
        // 初始化实例的生命周期相关的属性    
        initLifecycle(vm)    
        // 初始化实例事件的相关属性和监听功能    
        initEvents(vm)    
        // 初始化渲染函数相关的属性    
        initRender(vm)    
        // 调用生命周期钩子函数beforeCreate    
        callHook(vm, 'beforeCreate')    
        // 初始化父组件注入属性    
        initInjections(vm) // resolve injections before data/props    
        // 初始化状态相关的属性    
        initState(vm)    
        // 初始化子组件属性提供器    
        initProvide(vm) // resolve provide after data/props    
        // 调用生命周期钩子函数created    
        callHook(vm, 'created')    
        // 执行DOM挂载函数    
        if (vm.$options.el) {      
            vm.$mount(vm.$options.el)    
        }  
    }
}
复制代码

至此可以看到初始化方法_init()方法的整个前后引用关系一目了然。

init()方法都做了些什么

从以上init方法的源码中可以知道,它做了以下几件事情:

  • 将this绑定到当前vm对象
  • 给每个vue实例对象添加唯一uid标识
  • 将vm的isVue属性设置为true,方便以后过滤用
  • 合并options对象
  • 暴露实例对象
  • 初始化生命周期、事件相关功能、渲染函数、调用生命周期钩子函数等
  • 替换options中传入的DOM
文章分类
前端
文章标签