术语解释:
- runtime: 仅包含运行时,不包含编译器
- common: cjs规范
- esm: ES模块
- umd: 兼容cjs和amd,用于浏览器
初始化流程
- 1.打开
package.json
找到"dev": "rollup -w -c scripts/config.js --sourcemap --environment TARGET:web-full-dev",
其中 scripts/config.js 为入口文件 - 2.打开
scripts/config.js
找到
// Runtime+compiler CommonJS build (CommonJS)
'web-full-cjs-dev': {
entry: resolve('web/entry-runtime-with-compiler.js'),
dest: resolve('dist/vue.common.dev.js'),
format: 'cjs',
env: 'development',
alias: { he: './entity-decoder' },
banner
},
web/entry-runtime-with-compiler.js
为浏览器版本的入口文件 ,解析到的路径为 src/platforms/web/entry-runtime-with-compiler.js
- 3.打开
src/platforms/web/entry-runtime-with-compiler.js
会发现
import Vue from './runtime/index'
,找到Vue 构造函数的路劲;
以及
const mount = Vue.prototype.$mount
Vue.prototype.$mount = function (
此处根据浏览器扩展$mount方法
- 4.打开
src/platforms/web/runtime/index
会发现import Vue from 'core/index'
Vue 构造函数
以及
import { mountComponent } from 'core/instance/lifecycle'
// public mount method
Vue.prototype.$mount = function (
el?: string | Element,
hydrating?: boolean
): Component {
el = el && inBrowser ? query(el) : undefined
return mountComponent(this, el, hydrating)
}
此处为Vue原本的挂在方法,实现在core/instance/lifecycle
- 5.先打开
core/instance/lifecycle
看 mountComponent 的具体实现,import Vue from 'core/index'
Vue构造函数等会再看;
callHook(vm, 'beforeMount')
updateComponent = () => {
vm._update(vm._render(), hydrating)
}
new Watcher(vm, updateComponent, noop, {
before () {
if (vm._isMounted && !vm._isDestroyed) {
callHook(vm, 'beforeUpdate')
}
}
}, true /* isRenderWatcher */)
callHook(vm, 'mounted')
-
从代码中可以看到,挂载前,
- 5.1先执行的
beforeMount
钩子函数, - 5.2然后实现了
updateComponent
组件更新函数,updateComponent 会将虚拟Dom转换为真实Dom, - 5.3接着创建一个
Watcher
,根据昨天Watcher
的实现,里面会执行一次updateComponent
同时为响应化的数据都添加该Watcher
,方便后续数据变更,Watcher
同样执行updateComponent
, - 5.4 最后执行
mounted
钩子函数,整个挂载完成
- 5.1先执行的
-
6.整个Vue挂载完成,重新看
import Vue from 'core/index'
- 6.1
initGlobalAPI(Vue)
里面定义全局API,暂时略过 - 6.2
import Vue from './instance/index'
继续找Vue构造函数
- 6.1
-
7.打开
src\core\instance\index.js
- 7.1 终于发现Vue的构造函数
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)
}
-
整个构造函数里面只执行了一句
this._init(options)
- 7.2 下方
import { initMixin } from './init'
import { stateMixin } from './state'
import { renderMixin } from './render'
import { eventsMixin } from './events'
import { lifecycleMixin } from './lifecycle'
initMixin(Vue)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)
-
从中可以看到
initMixin
:初始化 ;stateMixin
:数据;eventsMixin
:事件;lifecycleMixin
生命周期;renderMixin
:渲染函数,执行的this._init(options)
就在initMixin
中 -
8 打开
src\core\instance\init.js
import { initLifecycle, callHook } from './lifecycle'
vm._self = vm
initLifecycle(vm) /
initEvents(vm)
initRender(vm)
callHook(vm, 'beforeCreate')
initInjections(vm) // resolve injections before data/props
initState(vm)
initProvide(vm) // resolve provide after data/props
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)
}
if (vm.$options.el) {
vm.$mount(vm.$options.el)
}
-
依次执行 生命周期初始化 -> 事件初始化 -> 渲染函数初始化->调用
beforeCreate
钩子函数 -> 初始化注入的数据->组件数据响应化处理->初始化提供给其他组件的数据 -> 调用created 钩子函数 -> 同时调用一下绑定到原型上的vm.$mount(vm.$options.el)
- 打开
src\core\instance\lifecycle.js
看看生命周期,此处生命周期,并非用户调用的钩子函数
- 打开
const options = vm.$options
// locate first non-abstract parent
let parent = options.parent
if (parent && !options.abstract) {
while (parent.$options.abstract && parent.$parent) {
parent = parent.$parent
}
parent.$children.push(vm)
}
vm.$parent = parent
vm.$root = parent ? parent.$root : vm
vm.$children = []
vm.$refs = {}
vm._watcher = null
vm._inactive = null
vm._directInactive = false
vm._isMounted = false
vm._isDestroyed = false
vm._isBeingDestroyed = false
vm.$parent = parent
;vm.$root = parent ? parent.$root : vm
;找到根节点和父节点,其他则是将Vue的属性置为空数据
看示例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="./vue.js"></script>
</head>
<body>
<div id="app">
{{foo.bar}}
</div>
<script>
const vm = new Vue({
data: {
foo: {
bar: "123"
}
},
el: "#app"
})
</script>
</body>
</html>
观察上述代码,什么时候会渲染到真实Dom之上的
1.断点打到src\core\instance\index.js
Vue的构造器
捕获.PNG
2.第二个断点打在src\core\instance\init.js
捕获.PNG
3.src\platforms\web\entry-runtime-with-compiler.js
捕获.PNG
4.src\platforms\web\runtime\index.js
捕获.PNG
5.src\core\instance\lifecycle.js
捕获.PNG
代码会先执行197行,然后内部调用``updateComponent`执行190行,就会看到界面渲染完成,整个Vue初始化完成
网友评论