vue源码学习(一)实例初始化

    技术2022-07-10  104

    vue源码版本为2.6.11(cdn地址为: https://lib.baomitu.com/vue/2.6.11/vue.js)

    new Vue()

    一般我们用vue都采用模板语法来声明:

    <div id="app">{{message}}</div> // js var app = new Vue({ el: '#app', data: { message: 'hello world!' } });

     当new Vue()时,vue做了哪些处理? 

    vue源码里有这样一段代码:

    其中定义了Vue构造函数,然后依次调用initMixin、stateMixin、eventsMixin、lifecycleMixin、renderMixin方法,并将Vue构造函数作为参数。

    注: this instanceof Vue 用于判断this是否是Vue对象构造函数的实例。

     

    initMixin(Vue)

    我们来看 initMixin方法:实际上是在prototype上挂载_init方法

    function initMixin (Vue) { // 在Vue prototype上拓展_init方法 Vue.prototype._init = function (options) { // 缓存当前的上下文到vm变量中 var vm = this; // a uid // 设置_uid属性(一开始为0,该属性是唯一的) // 当触发init方法,新建Vue实例时(当渲染组件时也会触发)uid都会递增。 vm._uid = uid$3++; // 下面这段代码主要是用来测试代码性能的,在这个时候相当于打了一个"标记点"来测试性能。 var startTag, endTag; /* istanbul ignore if */ if (config.performance && mark) { startTag = "vue-perf-start:" + (vm._uid); endTag = "vue-perf-end:" + (vm._uid); mark(startTag); } // a flag to avoid this being observed // 加上标识防止this被observed vm._isVue = true; // merge options if (options && options._isComponent) { // 当前这个Vue实例是组件,则执行initInternalComponent方法 // 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 { // 当前Vue实例不是组件,而是实例化Vue对象时,调用mergeOptions方法,合并options vm.$options = mergeOptions( resolveConstructorOptions(vm.constructor), options || {}, vm ); } /* istanbul ignore else */ { initProxy(vm); } // expose real self vm._self = vm; // 初始化生命周期 initLifecycle(vm); // 初始化事件 initEvents(vm); // 初始化渲染 initRender(vm); // 触发钩子函数beforeCreate callHook(vm, 'beforeCreate'); initInjections(vm); // resolve injections before data/props // 初始化data数据绑定 initState(vm); initProvide(vm); // resolve provide after data/props // 触发钩子函数created callHook(vm, 'created'); /* istanbul ignore if */ if (config.performance && mark) { vm._name = formatComponentName(vm, false); mark(endTag); measure(("vue " + (vm._name) + " init"), startTag, endTag); } // 判断有无el if (vm.$options.el) { // 执行vm.$$mount(),即把el渲染成最终的DOM vm.$mount(vm.$options.el); } }; }

    initSate() 初始化data数据绑定

    _init()中通过initState()来绑定数据到vm上,

    function initState (vm) { vm._watchers = []; // 获取options var opts = vm.$options; // 初始化props if (opts.props) { initProps(vm, opts.props); } // 初始化methods if (opts.methods) { initMethods(vm, opts.methods); } // 初始化data if (opts.data) { initData(vm); } else { observe(vm._data = {}, true /* asRootData */); } // 初始化计算属性 if (opts.computed) { initComputed(vm, opts.computed); } // 初始化watch if (opts.watch && opts.watch !== nativeWatch) { initWatch(vm, opts.watch); } }

    我们来看下initData()方法:

    function initData (vm) { var data = vm.$options.data; // 先判断data是不是function类型,是则调用getData,返回data的自调用; // 不是则直接返回data // 最后将data赋值到vm._data上 data = vm._data = typeof data === 'function' ? getData(data, vm) : data || {}; var keys = Object.keys(data); var props = vm.$options.props; var methods = vm.$options.methods; var i = keys.length; // 对data、props、methods,一一做校验判断是否存在相同的key // 因为它们最终都会挂载在vm上,都是通过vm.key来调用 while (i--) { var key = keys[i]; { if (methods && hasOwn(methods, key)) { warn( ("Method \"" + key + "\" has already been defined as a data property."), vm ); } } if (props && hasOwn(props, key)) { warn( "The data property \"" + key + "\" is already declared as a prop. " + "Use prop default value instead.", vm ); } else if (!isReserved(key)) { // 把每个key都挂载在vm上 proxy(vm, "_data", key); } } // 对数据作响应式处理 observe(data, true /* asRootData */); } // proxy函数 var sharedPropertyDefinition = { enumerable: true, configurable: true, get: noop, set: noop }; function proxy (target, sourceKey, key) { sharedPropertyDefinition.get = function proxyGetter () { return this[sourceKey][key] }; sharedPropertyDefinition.set = function proxySetter (val) { this[sourceKey][key] = val; }; Object.defineProperty(target, key, sharedPropertyDefinition); }

    proxy()本质上还是通过Object.defineProperty定义/修改属性(不了解用法的可以参考这一篇),将对target的key访问加了一层get/set,即当访问vm.key时,实际上是调用了sharedPropertyDefinition.get,返回this._data.key,这样就实现了通过vm.key来调用vm._data上的属性。

    如果传入值的_isVue为ture时(即传入的值是Vue实例本身)不会新建observer实例(这里可以暂时理解新建observer实例就是让数据响应式)。 

     

    Processed: 0.010, SQL: 9