vue v2.5.0源码-初始化流程

vue的生命周期

代码

运行结果

源码分析

 

1 function Vue (options) {
2   this._init(options)
3 }

 

 1 Vue.prototype._init = function (options?: Object) {
 2     const vm: Component = this
 3 
 4     //监听对象变化时用于过滤vm
 5     vm._isVue = true
 6     // 合并对象
 7     vm.$options = mergeOptions(
 8       resolveConstructorOptions(vm.constructor),
 9       options || {},
10       vm
11     )
12     // expose real self
13     vm._self = vm
14 
15     initLifecycle(vm)
16     //给vm添加了一些虚拟dom、slot等相关的属性和方法。
17     initRender(vm)
18     //调用beforeCreate钩子函数。
19     callHook(vm, 'beforeCreate')
20     //初始化数据 props,methods,data,computed,watch
21     initState(vm)
22     //调用created钩子函数。
23     callHook(vm, 'created')
24 
25     if (vm.$options.el) {
26       vm.$mount(vm.$options.el)
27     }
28   }

  

beforeCreate阶段和create阶段

create阶段,基本就是对传入数据的格式化、数据的双向绑定、以及一些属性的初始化。

1 export function resolveConstructorOptions (Ctor: Class<Component>) {
2   let options = Ctor.options
3   return options
4 }

 

合并策略存储在optionMergeStrategies对象中,strats[key]就是key属性的合并方法。

1 /**
2  * Option overwriting strategies are functions that handle
3  * how to merge a parent option value and a child option
4  * value into the final value.
5  */
6 const strats = config.optionMergeStrategies

 

合并属性

 1 /**
 2  * Merge two option objects into a new one.
 3  */
 4 function mergeOptions (
 5   parent,
 6   child,
 7   vm
 8 ) {
 9   var options = {};
10   var key;
11   for (key in parent) {
12     mergeField(key);
13   }
14   for (key in child) {
15     if (!hasOwn(parent, key)) {
16       mergeField(key);
17     }
18   }
19   function mergeField (key) {
20     var strat = strats[key] || defaultStrat;
21     options[key] = strat(parent[key], child[key], vm, key);
22   }
23   return options
24 }
 1 function mergeAssets (
 2   parentVal: ?Object,
 3   childVal: ?Object,
 4   vm?: Component,
 5   key: string
 6 ): Object {
 7   const res = Object.create(parentVal || null)
 8   return res
 9 }
10 //ASSET_TYPES=['components','directives','filters'];
11 ASSET_TYPES.forEach(function (type) {
12   strats[type + 's'] = mergeAssets
13 })

 

data属性合并策略。

1 strats.data = function (
2   parentVal: any,
3   childVal: any,
4   vm?: Component
5 ): ?Function {
6   return mergeDataOrFn(parentVal, childVal, vm)
7 }
 1 /**
 2  * Data
 3  */
 4 export function mergeDataOrFn (
 5   parentVal: any,
 6   childVal: any,
 7   vm?: Component
 8 ): ?Function {
 9   return function mergedInstanceDataFn () {
10     // instance merge
11     const instanceData = typeof childVal === 'function'
12       ? childVal.call(vm)
13       : childVal
14     const defaultData = typeof parentVal === 'function'
15       ? parentVal.call(vm)
16       : parentVal
17     if (instanceData) {
18       return mergeData(instanceData, defaultData)
19     } else {
20       return defaultData
21     }
22   }
23 }
 1 /**
 2  * Helper that recursively merges two data objects together.
 3  */
 4 function mergeData (to: Object, from: ?Object): Object {
 5   if (!from) return to
 6   let key, toVal, fromVal
 7   const keys = Object.keys(from)
 8   for (let i = 0; i < keys.length; i++) {
 9     key = keys[i]
10     toVal = to[key]
11     fromVal = from[key]
12     if (!hasOwn(to, key)) {
13       set(to, key, fromVal)
14     } else if (isPlainObject(toVal) && isPlainObject(fromVal)) {
15       mergeData(toVal, fromVal)
16     }
17   }
18   return to
19 }

mergeOption后vm.$options对象如下所示:

 

给vm对象添加了$parent、$root、$children属性,以及它的生命周期相关的标识。

 1 //主要给vm对象添加了$parent、$root、$children属性,以及一些其它的生命周期相关的标识。
 2 export function initLifecycle (vm: Component) {
 3   const options = vm.$options
 4 
 5   // locate first non-abstract parent
 6   let parent = options.parent
 7 
 8   vm.$parent = parent
 9   vm.$root = parent ? parent.$root : vm
10 
11   vm.$children = []
12   vm.$refs = {}
13 
14   vm._watcher = null
15   vm._inactive = null
16   vm._directInactive = false
17   vm._isMounted = false
18   vm._isDestroyed = false
19   vm._isBeingDestroyed = false
20 }

 

给vm添加了一些虚拟dom、slot等相关的属性和方法。

 1 export function initRender (vm: Component) {
 2   vm._vnode = null // the root of the child tree
 3   const options = vm.$options
 4   const parentVnode = vm.$vnode = options._parentVnode // the placeholder node in parent tree
 5   const renderContext = parentVnode && parentVnode.context
 6   vm.$slots = resolveSlots(options._renderChildren, renderContext)
 7   vm.$scopedSlots = emptyObject
 8   // bind the createElement fn to this instance
 9   // so that we get proper render context inside it.
10   // args order: tag, data, children, normalizationType, alwaysNormalize
11   // internal version is used by render functions compiled from templates
12   vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false)
13   // normalization is always applied for the public version, used in
14   // user-written render functions.
15   vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true)
16 }

 主要是操作数据,props、methods、data、props、computed、watch。

 1 export function initState (vm: Component) {
 2   vm._watchers = []
 3   const opts = vm.$options
 4   if (opts.props) initProps(vm, opts.props)
 5   if (opts.methods) initMethods(vm, opts.methods)
 6   if (opts.data) {
 7     initData(vm)
 8   } else {
 9     observe(vm._data = {}, true /* asRootData */)
10   }
11   if (opts.computed) initComputed(vm, opts.computed)
12   if (opts.watch && opts.watch !== nativeWatch) {
13     initWatch(vm, opts.watch)
14   }
15 }

beforemounted阶段和mounted阶段

 1 const mount = Vue.prototype.$mount
 2 /*
 3 * 判断是否有render函数,如果有直接处理。如果没有render函数,则生成render。
 4 *
 5 * */
 6 Vue.prototype.$mount = function (
 7   el?: string | Element,
 8   hydrating?: boolean
 9 ): Component {
10   el = el && query(el)
11 
12   const options = this.$options
13   // resolve template/el and convert to render function
14   if (!options.render) {
15     let template = options.template
16     if (template) {
17       if (typeof template === 'string') {
18         if (template.charAt(0) === '#') {
19           template = idToTemplate(template)
20           /* istanbul ignore if */
21           if (process.env.NODE_ENV !== 'production' && !template) {
22             warn(
23               `Template element not found or is empty: ${options.template}`,
24               this
25             )
26           }
27         }
28       } else if (template.nodeType) {
29         template = template.innerHTML
30       } else {
31         if (process.env.NODE_ENV !== 'production') {
32           warn('invalid template option:' + template, this)
33         }
34         return this
35       }
36     } else if (el) {
37       template = getOuterHTML(el)
38     }
39     if (template) {
40 
41       const { render, staticRenderFns } = compileToFunctions(template, {
42         shouldDecodeNewlines,
43         delimiters: options.delimiters,
44         comments: options.comments
45       }, this)
46       options.render = render
47       options.staticRenderFns = staticRenderFns
48     }
49   }
50   return mount.call(this, el, hydrating)
51 }

1. query(el)(类似为document.querySeector)判断el是不是字符串,不是字符串直接返回,是字符串转为dom。

2.判断是否有render函数,如果有,不做其他处理直接执行mount.call(this,el,hydrating)。如果没有,则获取template,template可以是#id、模板字符串、dom元素。如果没有template,则获取el及其子内容作为template。complieToFunctions是对最后生成的模板的解析,生成render。

 

 1 /**
 2  * Get outerHTML of elements, taking care
 3  * of SVG elements in IE as well.
 4  */
 5 function getOuterHTML (el: Element): string {
 6   if (el.outerHTML) {
 7     return el.outerHTML
 8   } else {
 9     const container = document.createElement('div')
10     container.appendChild(el.cloneNode(true))
11     return container.innerHTML
12   }
13 }

 

compileToFunctions中调用了compile,compile中调用了baseCompile。主要操作就是baseCompile中的三步。

 1 function baseCompile (
 2   template: string,
 3   options: CompilerOptions
 4 ): CompiledResult {
 5   const ast = parse(template.trim(), options)
 6   optimize(ast, options)
 7   const code = generate(ast, options)
 8   return {
 9     ast,
10     render: code.render,
11     staticRenderFns: code.staticRenderFns
12   }
13 }
14 
15 
16 export function createCompiler (baseOptions: CompilerOptions) {
17   const functionCompileCache: {
18     [key: string]: CompiledFunctionResult;
19   } = Object.create(null)
20 
21   function compile (
22     template: string,
23     options?: CompilerOptions
24   ): CompiledResult {
25       ...
26     const compiled = baseCompile(template, finalOptions)
27     ...
28     return compiled
29   }
30 
31   function compileToFunctions (
32     template: string,
33     options?: CompilerOptions,
34     vm?: Component
35   ): CompiledFunctionResult {
36     options = options || {}
37     ...
38     // compile
39     const compiled = compile(template, options)
40     ...
41     return (functionCompileCache[key] = res)
42   }
43 
44   return {
45     compile,
46     compileToFunctions
47   }
48 }

 

第一步: const ast = parse(template.trim(), options),解析template生成ast(抽象语法树)。例子中生成的ast如下:

{
  type: 1,
  tag: 'div',
  plain: false,
  parent: undefined,
  attrs: [{name:'id', value: '"app"'}],
  attrsList: [{name:'id', value: 'app'}],
  attrsMap: {id: 'app'},
  children: [{
    type: 1,
    tag: 'p',
    plain: true,
    parent: ast,
    attrs: [],
    attrsList: [],
    attrsMap: {},
    children: [{
      expression: "_s(message)",
      text: "{{message}}",
      type: 2
    }]
}

第二步:optimize(ast, options)主要对ast进行优化,分析出静态不变的内容部分,增加了部分属性。

{
  type: 1,
  tag: 'div',
  plain: false,
  parent: undefined,
  attrs: [{name:'id', value: '"app"'}],
  attrsList: [{name:'id', value: 'app'}],
  attrsMap: {id: 'app'},
  static: false,
  staticRoot: false,
  children: [{
    type: 1,
    tag: 'p',
    plain: true,
    parent: ast,
    attrs: [],
    attrsList: [],
    attrsMap: {},
    static: false,
    staticRoot: false,
    children: [{
      expression: "_s(message)",
      text: "{{message}}",
      type: 2,
      static: false
    }]
  }

 

因为这里只有一个动态的{{message}},所以static和staticRoot都是false。

最后一步:code=generate(ast, options),根据ast生成render函数和staticRenderFns数组。

最后生成的render如下:

render = function () {
    with(this){return _c('div',{attrs:{"id":"app"}},[_c('p',[_v(_s(message))])])}
}

 

定义的原始的$mount方法

1 // public mount method
2 Vue.prototype.$mount = function (
3   el?: string | Element,
4   hydrating?: boolean
5 ): Component {
6   el = el && inBrowser ? query(el) : undefined
7   return mountComponent(this, el, hydrating)
8 }

 

 1 /*
 2 * (1)调用beforeMount钩子函数
 3 * (2)新建一个Watcher对象,绑定在vm._watcher上
 4 * (3)调用mounted钩子函数
 5 * */
 6 export function mountComponent (
 7   vm: Component,
 8   el: ?Element,
 9   hydrating?: boolean
10 ): Component {
11   vm.$el = el
12   //调用beforeMount钩子函数
13   callHook(vm, 'beforeMount')
14 
15   let updateComponent
16   updateComponent = () => {
17     vm._update(vm._render(), hydrating)
18   }
19   //新建watcher对象,绑定在vm._watcher上
20   vm._watcher = new Watcher(vm, updateComponent, noop)
21   hydrating = false
22   // manually mounted instance, call mounted on self
23   // mounted is called for render-created child components in its inserted hook
24   if (vm.$vnode == null) {
25     vm._isMounted = true
26     callHook(vm, 'mounted')
27   }
28   return vm
29 }

vm._render()方法中,主要是调用了vm.$options.render方法,返回一个VNode对象。

 

总结

初始化流程

vm对象属性添加合并 =>beforeCreate=>数据操作(props,data,methods,computed等)=> created => template转为render函数=>beforeMount=>render函数转为VNode,新建watcher =>mounted

 

 

 

[1] https://github.com/liutao/vue2.0-source/blob/master/%E4%BB%8E%E4%B8%80%E4%B8%AA%E5%B0%8F%E6%A0%97%E5%AD%90%E6%9F%A5%E7%9C%8BVue%E7%9A%84%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F.md

posted @ 2018-07-10 19:49  springmin  阅读(320)  评论(0编辑  收藏  举报