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
转载于:https://www.cnblogs.com/fe-huahai/p/9291131.html
vue v2.5.0源码-初始化流程相关推荐
- vue v2.5.0源码-双向数据绑定
把option.data转变为vm._data的可观察对象 new Vue => this._init() => initState(vm) => initData(vm) => ...
- 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 ...
- 魔众Markdown管理系统v2.1.0源码
简介: 魔众Markdown管理系统采用PHP+Mysql架构,是一款对SEO非常友好.功能全面.安全稳定.支持多终端展示并且使用起来极其简单的在线Markdown管理系统. 魔众Markdown管理 ...
- 小说精品屋plus v2.7.0源码
介绍: 小说精品屋-plus是在小说精品屋的基础上,去除了漫画和弹幕模块,专注于小说,是一个多端(PC.移动)阅读.功能完善的小说原创/爬虫网站项目,既包含了作家专区供原创作者上传小说,又提供了爬虫工 ...
- 【Vue.js 3.0源码】KeepAlive 组件:如何让组件在内存中缓存和调度?
自我介绍:大家好,我是吉帅振的网络日志(其他平台账号名字相同),互联网前端开发工程师,工作5年,去过上海和北京,经历创业公司,加入过阿里本地生活团队,现在郑州北游教育从事编程培训. 一.前言 多个平行 ...
- 开源数据库模型建模工具PDMan v2.2.0源码
简介: PDMan是一款开源的数据库模型建模工具,支持Windows,Mac,Linux等操作系统,是PowerDesigner之外,更好的免费的替代方案.他具有颜值高,使用简单的特点.包含数据库建模 ...
- android6.0源码分析之Camera API2.0下的初始化流程分析
1.Camera2初始化的应用层流程分析 Camera2的初始化流程与Camera1.0有所区别,本文将就Camera2的内置应用来分析Camera2.0的初始化过程.Camera2.0首先启动的是C ...
- 【Debug跟踪Hadoop3.0.0源码之MapReduce Job提交流程】第三节 Job提交前的初始化
[Debug跟踪Hadoop3.0.0源码之MapReduce Job提交流程]第三节 Job提交前的初始化 回顾 Job提交前的初始化 后记 跳转 回顾 上一节中我们对 jobSubmitter(提 ...
- vue修改节点class_Vue2.0 源码解读系列 来自 Vue 的神秘礼盒
鄢栋,微医云服务团队前端工程师.有志成为一名全栈开发工程师甚至架构师,路漫漫,吾求索.生活中通过健身释放压力,思考问题. 目前 Vue3.0 打的很火热,都已经出了很多 Vue3.0 源码解析系列的博 ...
最新文章
- PS多形式的部分之间复制“笨办法”
- web服务器(IIS)的操作步骤
- mybatis中的mapper设计与原理
- [YTU]_2613( 距离产生美)
- EOS 消息设计(1)消息定义
- 每日两道前端面试题20190221
- java名 java_Java Syncrhonisers
- stm32l0的停止模式怎么唤醒_手把手教你怎么利用旧电脑搭建NAS组建自己的黑群晖...
- OpenShift 4 - Fedora CoreOS (5) - CoreOS的常规操作
- aria2c下载迅雷离线
- Java多线程详解(线程池)
- 12.卷1(套接字联网API)---IPv4与IPv6的互操作性
- 知乎企业认证怎么弄?知乎企业号怎么申请?详解来了
- uniapp-小程序与H5压缩图片上传
- Chrome 去广告插件'Adblock Plus' ,以及插件安装失败方法
- 第17章 国际贸易与资本流动
- python合并word表格单元格_Python实战009:读取Word文档中的表格数据及表格合并问题解决...
- siki学院Vector2 Vector3 Rigidbody Application笔记
- JS人民币小写金额转换为大写(没毛病)
- OC代码转Swift代码
热门文章
- 斯坦福《机器学习》Lesson4感想--1、Logistic回归中的牛顿方法
- 一周试用yii开发一个带各种该有功能的web程序(二)
- 160309_Qt Essentials
- No resource found that matches the given name 'Theme.AppCompat.Light'.
- 将字符串转换为全角或半角
- 【Scala】Scala的安装以及创建Scala项目的详细步骤
- webkit内核Android,Opera发布全新基于WebKit内核的安卓版浏览器
- C语言把文件空格删去,关于文件操作,碰到空格就换行
- android tools add native support,使用NDK进行开发android
- boolean类型_10、typescript的高级类型