術(shù)語解釋:
- runtime: 僅包含運行時丹墨,不包含編譯器
- common: cjs規(guī)范
- 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
會發(fā)現(xiàn)
import Vue from './runtime/index'
,找到Vue 構(gòu)造函數(shù)的路勁;
以及
const mount = Vue.prototype.$mount
Vue.prototype.$mount = function (
此處根據(jù)瀏覽器擴展$mount方法
- 4.打開
src/platforms/web/runtime/index
會發(fā)現(xiàn)import Vue from 'core/index'
Vue 構(gòu)造函數(shù)
以及
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原本的掛在方法恼除,實現(xiàn)在core/instance/lifecycle
- 5.先打開
core/instance/lifecycle
看 mountComponent 的具體實現(xiàn),import Vue from 'core/index'
Vue構(gòu)造函數(shù)等會再看;
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先執(zhí)行的
beforeMount
鉤子函數(shù)衙解, - 5.2然后實現(xiàn)了
updateComponent
組件更新函數(shù),updateComponent 會將虛擬Dom轉(zhuǎn)換為真實Dom, - 5.3接著創(chuàng)建一個
Watcher
,根據(jù)昨天Watcher
的實現(xiàn)焰枢,里面會執(zhí)行一次updateComponent
同時為響應(yīng)化的數(shù)據(jù)都添加該Watcher
蚓峦,方便后續(xù)數(shù)據(jù)變更舌剂,Watcher
同樣執(zhí)行updateComponent
, - 5.4 最后執(zhí)行
mounted
鉤子函數(shù),整個掛載完成
- 5.1先執(zhí)行的
-
6.整個Vue掛載完成暑椰,重新看
import Vue from 'core/index'
- 6.1
initGlobalAPI(Vue)
里面定義全局API,暫時略過 - 6.2
import Vue from './instance/index'
繼續(xù)找Vue構(gòu)造函數(shù)
- 6.1
-
7.打開
src\core\instance\index.js
- 7.1 終于發(fā)現(xiàn)Vue的構(gòu)造函數(shù)
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)
}
-
整個構(gòu)造函數(shù)里面只執(zhí)行了一句
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
:數(shù)據(jù);eventsMixin
:事件;lifecycleMixin
生命周期;renderMixin
:渲染函數(shù),執(zhí)行的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)
}
依次執(zhí)行 生命周期初始化 -> 事件初始化 -> 渲染函數(shù)初始化->調(diào)用
beforeCreate
鉤子函數(shù) -> 初始化注入的數(shù)據(jù)->組件數(shù)據(jù)響應(yīng)化處理->初始化提供給其他組件的數(shù)據(jù) -> 調(diào)用created 鉤子函數(shù) -> 同時調(diào)用一下綁定到原型上的vm.$mount(vm.$options.el)
- 打開
src\core\instance\lifecycle.js
看看生命周期霍转,此處生命周期,并非用戶調(diào)用的鉤子函數(shù)
- 打開
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
避消;找到根節(jié)點和父節(jié)點,其他則是將Vue的屬性置為空數(shù)據(jù)
看示例
<!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的構(gòu)造器
2.第二個斷點打在src\core\instance\init.js
3.src\platforms\web\entry-runtime-with-compiler.js
4.src\platforms\web\runtime\index.js
5.src\core\instance\lifecycle.js
代碼會先執(zhí)行197行沾谓,然后內(nèi)部調(diào)用``updateComponent`執(zhí)行190行,就會看到界面渲染完成戳鹅,整個Vue初始化完成