vue v2.5.0源码-初始化流程
发布日期:2021-08-22 08:05:48 浏览次数:4 分类:技术文章

本文共 11245 字,大约阅读时间需要 37 分钟。

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       vm11     )12     // expose real self13     vm._self = vm14 15     initLifecycle(vm)16     //给vm添加了一些虚拟dom、slot等相关的属性和方法。17     initRender(vm)18     //调用beforeCreate钩子函数。19     callHook(vm, 'beforeCreate')20     //初始化数据 props,methods,data,computed,watch21     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
) {2 let options = Ctor.options3 return options4 }

 

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

1 /**2  * Option overwriting strategies are functions that handle3  * how to merge a parent option value and a child option4  * 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 options24 }
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'] = mergeAssets13 })

 

data属性合并策略。

1 strats.data = function (2   parentVal: any,3   childVal: any,4   vm?: Component5 ): ?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 merge11     const instanceData = typeof childVal === 'function'12       ? childVal.call(vm)13       : childVal14     const defaultData = typeof parentVal === 'function'15       ? parentVal.call(vm)16       : parentVal17     if (instanceData) {18       return mergeData(instanceData, defaultData)19     } else {20       return defaultData21     }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 to19 }

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 : vm10 11   vm.$children = []12   vm.$refs = {}13 14   vm._watcher = null15   vm._inactive = null16   vm._directInactive = false17   vm._isMounted = false18   vm._isDestroyed = false19   vm._isBeingDestroyed = false20 }

 

给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, alwaysNormalize11   // internal version is used by render functions compiled from templates12   vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false)13   // normalization is always applied for the public version, used in14   // 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.$options13   // resolve template/el and convert to render function14   if (!options.render) {15     let template = options.template16     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               this25             )26           }27         }28       } else if (template.nodeType) {29         template = template.innerHTML30       } else {31         if (process.env.NODE_ENV !== 'production') {32           warn('invalid template option:' + template, this)33         }34         return this35       }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.comments45       }, this)46       options.render = render47       options.staticRenderFns = staticRenderFns48     }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.innerHTML12   }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.staticRenderFns12   }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?: CompilerOptions24   ): CompiledResult {25       ...26     const compiled = baseCompile(template, finalOptions)27     ...28     return compiled29   }30 31   function compileToFunctions (32     template: string,33     options?: CompilerOptions,34     vm?: Component35   ): CompiledFunctionResult {36     options = options || {}37     ...38     // compile39     const compiled = compile(template, options)40     ...41     return (functionCompileCache[key] = res)42   }43 44   return {45     compile,46     compileToFunctions47   }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 method2 Vue.prototype.$mount = function (3   el?: string | Element,4   hydrating?: boolean5 ): Component {6   el = el && inBrowser ? query(el) : undefined7   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?: boolean10 ): Component {11   vm.$el = el12   //调用beforeMount钩子函数13   callHook(vm, 'beforeMount')14 15   let updateComponent16   updateComponent = () => {17     vm._update(vm._render(), hydrating)18   }19   //新建watcher对象,绑定在vm._watcher上20   vm._watcher = new Watcher(vm, updateComponent, noop)21   hydrating = false22   // manually mounted instance, call mounted on self23   // mounted is called for render-created child components in its inserted hook24   if (vm.$vnode == null) {25     vm._isMounted = true26     callHook(vm, 'mounted')27   }28   return vm29 }

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

转载于:https://www.cnblogs.com/fe-huahai/p/9291131.html

转载地址:https://blog.csdn.net/weixin_33738982/article/details/93330061 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:入门第十课 Python语句的编写之while
下一篇:oracle 11g 及 plsqldeveloper 相关操作

发表评论

最新留言

能坚持,总会有不一样的收获!
[***.219.124.196]2024年03月28日 04时55分00秒