zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    元组类型内置方法
    列表类型内置方法
    字符串类型内置方法
    转:【英语学习材料】
    转:【15年学不会英语的原因】
    转:【Java动态编程(Javassist研究)】
    转:【进制转换-概念】
    c语言学习笔记
    virtualbox虚拟机桥接方式网络设置
    navicat连接mysql8报错,错误提示为1251,原因及解决步骤
  • 原文地址:https://www.cnblogs.com/fe-huahai/p/9291131.html
Copyright © 2011-2022 走看看