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

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

vue v2.5.0源码-初始化流程相关推荐

  1. vue v2.5.0源码-双向数据绑定

    把option.data转变为vm._data的可观察对象 new Vue => this._init() => initState(vm) => initData(vm) => ...

  2. LibreCAD v2.2.0源码编译,使用VS2019+Qt5.12.9+Boost1.71.0环境

    零.前言 LibreCAD is a fork of QCAD community edition version 2.0.5.0;therefore, any modifications of or ...

  3. 魔众Markdown管理系统v2.1.0源码

    简介: 魔众Markdown管理系统采用PHP+Mysql架构,是一款对SEO非常友好.功能全面.安全稳定.支持多终端展示并且使用起来极其简单的在线Markdown管理系统. 魔众Markdown管理 ...

  4. 小说精品屋plus v2.7.0源码

    介绍: 小说精品屋-plus是在小说精品屋的基础上,去除了漫画和弹幕模块,专注于小说,是一个多端(PC.移动)阅读.功能完善的小说原创/爬虫网站项目,既包含了作家专区供原创作者上传小说,又提供了爬虫工 ...

  5. 【Vue.js 3.0源码】KeepAlive 组件:如何让组件在内存中缓存和调度?

    自我介绍:大家好,我是吉帅振的网络日志(其他平台账号名字相同),互联网前端开发工程师,工作5年,去过上海和北京,经历创业公司,加入过阿里本地生活团队,现在郑州北游教育从事编程培训. 一.前言 多个平行 ...

  6. 开源数据库模型建模工具PDMan v2.2.0源码

    简介: PDMan是一款开源的数据库模型建模工具,支持Windows,Mac,Linux等操作系统,是PowerDesigner之外,更好的免费的替代方案.他具有颜值高,使用简单的特点.包含数据库建模 ...

  7. android6.0源码分析之Camera API2.0下的初始化流程分析

    1.Camera2初始化的应用层流程分析 Camera2的初始化流程与Camera1.0有所区别,本文将就Camera2的内置应用来分析Camera2.0的初始化过程.Camera2.0首先启动的是C ...

  8. 【Debug跟踪Hadoop3.0.0源码之MapReduce Job提交流程】第三节 Job提交前的初始化

    [Debug跟踪Hadoop3.0.0源码之MapReduce Job提交流程]第三节 Job提交前的初始化 回顾 Job提交前的初始化 后记 跳转 回顾 上一节中我们对 jobSubmitter(提 ...

  9. vue修改节点class_Vue2.0 源码解读系列 来自 Vue 的神秘礼盒

    鄢栋,微医云服务团队前端工程师.有志成为一名全栈开发工程师甚至架构师,路漫漫,吾求索.生活中通过健身释放压力,思考问题. 目前 Vue3.0 打的很火热,都已经出了很多 Vue3.0 源码解析系列的博 ...

最新文章

  1. PS多形式的部分之间复制“笨办法”
  2. web服务器(IIS)的操作步骤
  3. mybatis中的mapper设计与原理
  4. [YTU]_2613( 距离产生美)
  5. EOS 消息设计(1)消息定义
  6. 每日两道前端面试题20190221
  7. java名 java_Java Syncrhonisers
  8. stm32l0的停止模式怎么唤醒_手把手教你怎么利用旧电脑搭建NAS组建自己的黑群晖...
  9. OpenShift 4 - Fedora CoreOS (5) - CoreOS的常规操作
  10. aria2c下载迅雷离线
  11. Java多线程详解(线程池)
  12. 12.卷1(套接字联网API)---IPv4与IPv6的互操作性
  13. 知乎企业认证怎么弄?知乎企业号怎么申请?详解来了
  14. uniapp-小程序与H5压缩图片上传
  15. Chrome 去广告插件'Adblock Plus' ,以及插件安装失败方法
  16. 第17章 国际贸易与资本流动
  17. python合并word表格单元格_Python实战009:读取Word文档中的表格数据及表格合并问题解决...
  18. siki学院Vector2 Vector3 Rigidbody Application笔记
  19. JS人民币小写金额转换为大写(没毛病)
  20. OC代码转Swift代码

热门文章

  1. 斯坦福《机器学习》Lesson4感想--1、Logistic回归中的牛顿方法
  2. 一周试用yii开发一个带各种该有功能的web程序(二)
  3. 160309_Qt Essentials
  4. No resource found that matches the given name 'Theme.AppCompat.Light'.
  5. 将字符串转换为全角或半角
  6. 【Scala】Scala的安装以及创建Scala项目的详细步骤
  7. webkit内核Android,Opera发布全新基于WebKit内核的安卓版浏览器
  8. C语言把文件空格删去,关于文件操作,碰到空格就换行
  9. android tools add native support,使用NDK进行开发android
  10. boolean类型_10、typescript的高级类型