# Vue主线剧情之core/index

啦啦啦,进入新手村了,游戏才刚刚开始哦~

通过对core/index的分析,一点点的开启支线任务,让我们慢慢的了解这个游戏。

打包web会先进入到src/platforms/web,这里暂且忽略,这个里面也是引用的core_index,这个游戏从core_index开始说起。

# 触发core/index

主线任务:找到core/index老爷爷,并与他对话。

对话框:是否查看源码

我:否

对话框:好的,拜拜

我:wc,wq(卧槽,无情)

再次打开任务框:找到core/index老爷爷,并与他对话。

我:emm...

对话框:是否查看源码

我:是

内心:卧槽,就这一个任务,不选 不让往下走了,这特娘的任务设定,那为什么要让我选择呀~ 直接看不就完了吗?


import Vue from './instance/index'
import { initGlobalAPI } from './global-api/index'
import { isServerRendering } from 'core/util/env'
import { FunctionalRenderContext } from 'core/vdom/create-functional-component'

// 初始化全局API
initGlobalAPI(Vue)

// 挂载isServer判断方法
Object.defineProperty(Vue.prototype, '$isServer', {
  get: isServerRendering
})

// 挂载直接访问组件中的服务器端渲染上下文(SSR context)。
Object.defineProperty(Vue.prototype, '$ssrContext', {
  get () {
    // istanbul 提供注释语法,允许某些代码不计入覆盖率。
    /* istanbul ignore next */
    return this.$vnode && this.$vnode.ssrContext
  }
})

// 为vue原型定义当为ssr环境时加载FunctionalRenderContext方法
// expose FunctionalRenderContext for ssr runtime helper installation
Object.defineProperty(Vue, 'FunctionalRenderContext', {
  value: FunctionalRenderContext
})

// 版本号
Vue.version = '__VERSION__'

export default Vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

通过上面我们可以得到如下的流程:

# 探寻Vue的构造函数

我们可以看到 core/index 中最主要的就是 ./instance/indexinitGlobalAPI

我们当然先看最主要的,导出 Vue 构造函数的文件了。

import { initMixin } from './init'
import { stateMixin } from './state'
import { renderMixin } from './render'
import { eventsMixin } from './events'
import { lifecycleMixin } from './lifecycle'
import { warn } from '../util/index'

function Vue (options) {
  if (process.env.NODE_ENV !== 'production' &&
    !(this instanceof Vue)
  ) {
    warn('Vue is a constructor and should be called with the `new` keyword')
  }
  this._init(options)
}

initMixin(Vue)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)

export default Vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

由此可以看到 Vue 实际上就是一个用 Function 实现的类,接收 options 参数,通过 new 关键字初始化后,就会调用 this._init 方法。

我们看到了一大堆的 Mixin,所以可见这些重点方法全部是混入到Vue的构造函数上面的。具体的得等我们接收相关任务了才能看到。

继续主线。。寻找 this._init 方法

# this._init

这个不难,通过任务就可以看到init方法嘛,只有initMixin和init有关系,所以我们打开小地图直奔core/instance/init.js

// core/instance/init.js

...

export function initMixin (Vue: Class<Component>) {
  Vue.prototype._init = function (options?: Object) {
    const vm: Component = this
    // 当前组件的uid 用来标记当前组件
    // a uid
    vm._uid = uid++

    let startTag, endTag

    // 标记开始
    /* istanbul ignore if */
    if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
      startTag = `vue-perf-start:${vm._uid}`
      endTag = `vue-perf-end:${vm._uid}`
      mark(startTag)
    }

    // 如果是Vue的实例的话,就不用observe(观察)了
    // a flag to avoid this being observed
    vm._isVue = true

    // 合并参数
    // merge options

    // 是不是一个单独的组件
    if (options && options._isComponent) {
      // 优化内部组件实例化
      // optimize internal component instantiation
      // 因为动态的合并参数是很慢的,并且没有   (emm...这里我也不知道想说啥,反正是比动态枚举快就对了)
      // since dynamic options merging is pretty slow, and none of the
      // 内部组件选项需要特殊处理。
      // internal component options needs special treatment.
      initInternalComponent(vm, options)
    } else {
      // 公共的参数合并
      vm.$options = mergeOptions(
        resolveConstructorOptions(vm.constructor),
        options || {},
        vm
      )
    }

    // 通过判断 Proxy 为 vue的实例属性赋值
    /* istanbul ignore else */
    if (process.env.NODE_ENV !== 'production') {
      initProxy(vm)
    } else {
      vm._renderProxy = vm
    }

    // 看起来有点像 self.self === self ???
    // expose real self
    vm._self = vm

    // 初始化生命周期相关
    initLifecycle(vm)

    // 初始化事件监听相关
    initEvents(vm)

    // 初始化编译render
    initRender(vm)

    // 通知调用beforeCreate钩子
    callHook(vm, 'beforeCreate')

    // 在data/props之前注入处理一些事情
    initInjections(vm) // resolve injections before data/props

    // 初始化props、methods、data、computed与watch
    initState(vm)

    // 在data/props之后处理一些事情
    initProvide(vm) // resolve provide after data/props

    // 通知调用created钩子
    callHook(vm, 'created')

    // 标记结束
    /* istanbul ignore if */
    if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
      vm._name = formatComponentName(vm, false)
      mark(endTag)
      measure(`vue ${vm._name} init`, startTag, endTag)
    }

    // 如果el元素存在,则挂载组件方法触发组件的DOM渲染
    if (vm.$options.el) {
      vm.$mount(vm.$options.el)
    }
  }
}

...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99

吐槽一下,看到这里我才明白以前在网上看的那些文档里面,为什么非要用 var vm = new Vue(xxx) 阿西吧,我说为啥要用vm呢~

总结一下 vue._init 第一次主要干的活有:

  • 合并配置项
  • 初始化生命周期
  • 初始化事件监听
  • 初始化render
  • 调用beforecreate钩子
  • 初始化 data、props、computed、watcher 等
  • 调用created钩子
  • 最后调用vm.$mount 方法挂载 vm,把模版渲染成DOM。

再次吐槽,为啥子Vue的Mixin这么简陋,居然是直接挂载到prototype上,而且,有那么多的Mixin居然都是这样,最后的Vue.use也不例外... 啊啊啊~为什么感觉这么难受啊!!!

# vm.$mount

好了继续主线,大体流程知道了,下一章我们来看 _init 的最后一步 vm.$mount

# 致谢

感谢大家阅读我的文章,如果对我感兴趣可以点击页面右上角,帮我点个star。

作者:前端小然子

链接: https://xiaoranzife.com/guide/vue/core_index.html

来源:前端小然子的博客

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

上次更新: 2019-11-20 5:18:16 ├F10: PM┤