new Vue的具体流程

new Vue做了啥?

new Vue({
  el: '#app',
  render: h => h(App),
  data() {
    return {
      message: 'hello vue'
    }
  }
}).$mount('#app')

打开源码,先找到Vue的构造函数,在vue/src/core/instance/index.js里面,


function Vue

可以看到这个function Vue(){}很简单,几行代码,首先判断用户是否没有用 new Vue(); 是就警告用户必须new来实例化,不能直接

const app = Vue({
  ...
})

必须要new

const app = new Vue({
  ...
})

然后, 调用原型方法 this._init(options)

_init方法在哪里?
找一下,在上图的initMixin(Vue)里面,
打开initMixin方法来源 :vue/src/core/instance/init.js


initMixin.png

这里做了啥?其实就是合并传入的选项和一些初始化工作,主要包括:


initMixin2.png
initLifecycle(vm) // 初始化生命周期
initEvents(vm) // 初始化事件
initRender(vm) // 初始化渲染
callHook(vm, 'beforeCreate') // 调用beforeCreate钩子
initInjections(vm) // resolve injections before data/props
initState(vm) // 初始化状态
initProvide(vm) // resolve provide after data/props
callHook(vm, 'created') // 调用created钩子

最后就是mount挂载真实dom了

 if (vm.$options.el) {
      vm.$mount(vm.$options.el)
    }

这么多方法一排排下来,难道我要一个个去看?非也,看源码很忌讳这样子看,因为你一个个看的话,函数里面套函数,你就蒙了,函数调用栈这么深,你相当于深度遍历,吃力不讨好。所以最好带着目的看,回到开始的代码new Vue({...})

new Vue({
  el: '#app',
  render: h => h(App),
  data() {
    return {
      message: 'hello vue'
    }
  }
}).$mount('#app')

el是挂载dom,后面看,然后是data,所以看一下它对传入的data做了啥。嗯,说干就干,我们找到initState(vm) 初始化状态 看看它是如何处理data的,找到vue/src/core/instance/state.js

initState

又是初始化props,methods,data,computed,watch等,细心的同学发现这里的props,methods,data,computed,watch顺序是有讲究的,在实际项目开发中,后面的可以通过this.xxx访问前面的提供方法、属性。我们主要看initData如下图
initData.png

敲黑板:面试常考:为什么data推荐函数,而不是对象形式?
data = vm._data = typeof data === 'function'
? getData(data, vm)
: data || {}

源码告诉你这里可以是函数也可以对象,推荐函数是为了解决组件实例公用同一个data的问题。所以,就vue来说,如果是根组件,你可以直接用data:{key:val}形式,非根组件就应该用data(){return {key:val}}形式。

然后继续回到源码这里是 data = vm._data,data的结果挂到了vm._data上,_data就是用户定义的data了, 注意业内规范是下划线_ 开头的变量都是私有属性,作者自己用的,用户最好不用访问它。然后看getData函数做了啥?


getData.png

return data.call(vm, vm)

很简单其实就是执行了用户传入的选项中的data方法,这里用call来绑定this到本实例组件,还不懂call啥意思,可以参考我的另外一篇文章 js模拟实现call、apply、bind函数,所以说看源码要原生js基础牢固过得去才行。

接着往下看:


state.png

1处,拿data里面的key和props的key,methods的key,判断如果有三者之间有重名的就报错。所以,我们在定义数据的时候不能重名。再看keys循环里面的2处,这里调用方法 proxy(vm, _data, key),其实就是把data的数据代理到实例上,所以我们才能够在其他地方直接this.message访问data(){return {message:'hello, vue'}}的message。而不是this._data.message。
具体实现看proxy方法:


proxy.png

所以,当我们this.message时就是走了 return this[sourceKey][key], sourceKey是_data, key是message,看上面图提到2处的proxy(vm, _data, key)。从设计模式的角度来看,这里就是典型的代理模式,用户不直接访问this._data.message,而是访问this.message。访问时,通过Object.defineProperty(target, key, sharedPropertyDefinition)来进行拦截代理。vue响应式原理也是通过Object.defineProperty进行拦截, 这些以后再扯,至此,data这块算过了,暂时先不看它的响应式原理, 接下来看mount。

$mount做了啥?

其实看$mount可以从runtime-only版本和runtime-compiler版本这两个方面入手,
入口文件分别在vue/src/platfoms/web/entry-runtime-with-compiler.js 和
vue/src/platfoms/web/entry-runtime.js
但是他们都是引入了同级下的runtime/index


runtime/index

runtime/index这里定义了mount方法,runtime-only版本直接使用它,而runtime-with-compiler就引入它再包装一层,这一层的目的就是编译template模板为render函数。看Vue.prototype.$mount干了啥?
还是调用了mountComponent 方法,在vue/core/instance/lifecycle里面,去找它,如下图


image.png

我们主要看let updateComponent这里,updateComponent根据不同环境赋值不同,但是它的核心作用就是一个更新组件的过程,内部调用了vm._update(vm._render(), hydrating),接着往下看,就是

new Watcher(vm, updateComponent, noop, {
    before () {
      if (vm._isMounted && !vm._isDestroyed) {
        callHook(vm, 'beforeUpdate')
      }
    }
  }, true /* isRenderWatcher */)

这是实例化一个渲染观察者,继续看Watcher是干嘛,找到它,其实它就是一个观察者模式,按照上面的参数传入的话,先做一些依赖收集的工作,最后赋值this.value就是执行updateComponent这个方法,如下图顺序123执行,1处的expOrFn就是updateComponent


Watcher

watcher get

而上面也提到updateComponent内部执行vm._update(vm._render(), hydrating),顾名思义其实就是:
1、vm._render()创建生成virtual dom;
2、vm._update把virtual dom渲染成真实dom

virtua dom 具体怎么生成真实dom的,继续看vm._update这个方法,在vue/src/core/instance/lifecycle.js里面,可以看到这个vue.protype._update不仅仅是初始化渲染,也可能是更新渲染,如图红框里面的if else


_update

继续看__patch__方法干了啥,如下图,这里又做了判断,浏览器端才赋值patch,否则就是noop,noop是一个空函数,啥也不干。


__patch__

我们继续找patch,如下图

patch

看到它又是一个动态生成的函数,靠createPatchFunction生成,这是一个高阶函数,这里传入参数{ nodeOps, modules }可以看出实际上createPatchFunction是可以生成不同的patch函数的,就目前来看,这里是传入web平台的参数,那么里面的返回的patch就是专门针对浏览器处理的,还有可能就是传入weex平台的,这里就不展开了。看一下createPatchFunction的实现,打开vdom/patch.js可以看到这个方法很大 【70 - 804行】,里面定义了很多辅助方法,最后return了一个patch方法。


image.png

实际上就是看这个patch方法的逻辑,里面的方法调方法,最后我们找到insert这个方法,这里就是生成真实dom


insert

image.png

里面的nodeOps.insertBefore(parent, elm, ref)、nodeOps.appendChild(parent, elm)把virtual dom 通过原始的 parentNode.insertBefore方法插入dom,我们看nodeOps里面怎么实现的,回到最开始的createPatchFunction({ nodeOps, modules })这里,顺藤摸瓜,找到了vue/src/platforms/web/runtime/node-ops.js,可以看到里面就是一些对原生dom操作的封装而已,很简单,至此我们就摸清了整个mount流程了。

nodeOps

总结一下

所以,new Vue做了啥?
1、对Vue进行_init初始化
2、$mount,里面做了compiler[可选]、生成render函数、render函数创建vnode、vnode里面再进行patch操作,patch操作里面就是更新virtual dom和插入真实dom了。

new vue流程

更多细节得自己看一遍才更有体会,看博客再多没意义,只有你看了源码,再对照别人的博客,互相证道才有更大的收获,谢谢观看。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,881评论 4 368
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,052评论 1 301
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 110,598评论 0 250
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,407评论 0 217
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,823评论 3 294
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,872评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,037评论 2 317
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,778评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,505评论 1 247
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,745评论 2 253
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,233评论 1 264
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,568评论 3 260
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,231评论 3 241
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,141评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,939评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,954评论 2 283
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,784评论 2 275