学习任何一门框架,都不可能一股脑儿的从入口代码从上到下,把代码看完,
这样其实是很枯燥的,我想也很少有人这么干,或者这么干着干着可能干不下去了。
因为肯定很无聊。
我们先从一个最最简单的小例子,来查看new Vue(options)实例,这个过程发生了什么。
vm实例上的属性又如何添加上去的,又如何渲染到浏览器页面上的。
关于vue的数据依赖和虚拟dom都是重点,必然会在以后的帖子记录。
这篇帖子就根据下例子,看看实例化一个vm实例做了啥吧。
先把小例子贴出来:
<div id="app"> <p>这是<span>静态内容</span></p> <p>{{message}}</p> </div> <script src="../../dist/vue.js"></script> <script> var vm = new Vue({ el: ‘#app‘, data: { message: ‘hi vue!‘ } }) console.log(vm) </script>
根据上篇介绍了vue的调式笔记,那我们快快进入源码吧
根据vue构造函数那篇笔记,我们知道了Vue原型上有哪些方法,_init方法就是其中一个方法
我们看到_init就把实例要做的事情都做完了,当然其中有的语句所做的事,太多了。我们先一点一点开see see吧。
看图不好玩,我把源码取出 来 好好瞧瞧
export function initMixin (Vue: Class<Component>) { Vue.prototype._init = function (options?: Object) { const vm: Component = this // a uid vm实例唯一标识 vm._uid = uid++ let startTag, endTag /* istanbul ignore if 性能统计相关 */ if (process.env.NODE_ENV !== ‘production‘ && config.performance && mark) { startTag = `vue-perf-init:${vm._uid}` endTag = `vue-perf-end:${vm._uid}` mark(startTag) } // a flag to avoid this being observed 监听对象变化时用于过滤vm vm._isVue = true // merge options _isComponent是内部创建子组件时才会添加为true的属性 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 { // mergeOptions 方法 合并构造器及构造器父级上定义的options vm.$options = mergeOptions( resolveConstructorOptions(vm.constructor), options || {}, vm ) } /* istanbul ignore else */ if (process.env.NODE_ENV !== ‘production‘) { initProxy(vm) } else { vm._renderProxy = vm } // expose real self vm._self = vm initLifecycle(vm) initEvents(vm) initRender(vm) callHook(vm, ‘beforeCreate‘) initInjections(vm) // resolve injections before data/props initState(vm) initProvide(vm) // resolve provide after data/props callHook(vm, ‘created‘) /* istanbul ignore if */ if (process.env.NODE_ENV !== ‘production‘ && config.performance && mark) { vm._name = formatComponentName(vm, false) mark(endTag) measure(`${vm._name} init`, startTag, endTag) } if (vm.$options.el) { vm.$mount(vm.$options.el) } } }
1. 给实例添加了唯一标识uid
2.性能统计相关,先忽略
3. 给实例添加_isVue,监听对象变化时用于过滤vm
4. 选项对象如果有_isComponent,就初始化内部组件,_isComponent是内部创建子组件时才会添加为true的属性
5. 小例子会走分支,mergeOptions 方法 合并构造器及构造器父级上定义的options,resolveConstructorOptions方法后面笔记会详解,
mergeOptions方法接受3个参数。我们先简单看下resolveConstructorOptions方法的定义
export function resolveConstructorOptions (Ctor: Class<Component>) { let options = Ctor.options // 有super属性,说明Ctor是通过Vue.extend()方法创建的子类 if (Ctor.super) { const superOptions = resolveConstructorOptions(Ctor.super) const cachedSuperOptions = Ctor.superOptions if (superOptions !== cachedSuperOptions) { // super option changed, // need to resolve new options. Ctor.superOptions = superOptions // check if there are any late-modified/attached options (#4976) const modifiedOptions = resolveModifiedOptions(Ctor) // update base extend options if (modifiedOptions) { extend(Ctor.extendOptions, modifiedOptions) } options = Ctor.options = mergeOptions(superOptions, Ctor.extendOptions) if (options.name) { options.components[options.name] = Ctor } } } return options }
可以看出Ctor.options其实就是Vue构造函数自身,在Vue构造函数静态属性那篇笔记,Vue是拥有options属性的,且有截图,等下会再截图看下,
接着在该函数中有个if语句,我们小例子会跳过的,直接返回options。因为有super属性,说明Ctor是通过Vue.extend()方法创建的子类。那么
options是啥呢,如下图,
回到_init方法中,mergeOptions方法的第二个参数就是我们传入的options,第三个参数就是vm实例,把参数一起截个图吧,好回忆
mergeOptions是Vue中处理属性的合并策略的地方, 先看下它的定义
export function mergeOptions ( parent: Object, child: Object, vm?: Component ): Object { if (process.env.NODE_ENV !== ‘production‘) { // 如果有options.components,则判断是否组件名是否合法 checkComponents(child) } // 格式化child的props normalizeProps(child) // 格式化child的directives normalizeDirectives(child) const extendsFrom = child.extends if (extendsFrom) { parent = typeof extendsFrom === ‘function‘ ? mergeOptions(parent, extendsFrom.options, vm) : mergeOptions(parent, extendsFrom, vm) } if (child.mixins) { for (let i = 0, l = child.mixins.length; i < l; i++) { let mixin = child.mixins[i] if (mixin.prototype instanceof Vue) { mixin = mixin.options } parent = mergeOptions(parent, mixin, vm) } } const options = {} let key for (key in parent) { mergeField(key) } for (key in child) { if (!hasOwn(parent, key)) { mergeField(key) } } function mergeField (key) { const strat = strats[key] || defaultStrat options[key] = strat(parent[key], child[key], vm, key) } return options }
明天再说吧,累了,要睡觉