從畢業(yè)到現(xiàn)在也寫(xiě)的有兩年Vue了,本著高深追求就去學(xué)習(xí)了Vue的源碼,就把學(xué)習(xí)過(guò)程中的理解記錄下來(lái)峭沦,這將是一個(gè)系列的文章,一次不可能寫(xiě)完逃糟,會(huì)不斷更新,后續(xù)還會(huì)有vuex和vue-router系列
先從Vue的目錄說(shuō)起
image.png
- scripts目錄里面包含我們打包所需要的腳本
- src里面包含的就是vue的源碼目錄了
- compiler模版編輯器生成 render蓬豁,ast绰咽,staticRenderFns
- core vue的主要代碼目錄包含生命周期,靜態(tài)方法地粪,原型方法取募,屬性,vdom蟆技,observe等一系列
- platform 打包的入口 也是我們閱讀源碼的入口(這里只講web)
- server 服務(wù)端渲染相關(guān)
- sfc 解析單文件組件
- shared 項(xiàng)目包含的公共方法和常量
這里先講解vue的加載流程
每行代碼基本都有注釋玩敏,請(qǐng)細(xì)看注釋
分析packge.json
"build": "node scripts/build.js" //告訴我們打包的入口是scripts/build.js
分析打包
// build.js
let builds = require('./config').getAllBuilds() // 引入config
.....
build(builds) // 打包通過(guò)分析config.js
// config.js,這里選擇這一個(gè) 也是我們最常用的vue.min.js
'web-full-prod': {
entry: resolve('web/entry-runtime-with-compiler.js'), // 將從這個(gè)目錄讀起
dest: resolve('dist/vue.min.js'),
format: 'umd',
env: 'production',
alias: { he: './entity-decoder' },
banner
},
分析web/entry-runtime-with-compiler.js
將會(huì)簡(jiǎn)化這里的代碼后續(xù)慢慢補(bǔ)充
// web/entry-runtime-with-compiler.js
const idToTemplate = cached(id => { //將template緩存起來(lái)质礼,避免重復(fù)解析
const el = query(id)
return el && el.innerHTML
})
const mount = Vue.prototype.$mount // 來(lái)自runtime/index.js 運(yùn)行mountComponent
Vue.prototype.$mount =function(){
// 1解析template
// 2生成options.render
} // 掛載$mount 這里需要分析清楚這兩個(gè)$mount
// runtime/index.js
Vue.prototype.__patch__ = inBrowser ? patch : noop //添加patch
Vue.prototype.$mount= function(){
// 運(yùn)行mountComponent dom掛載已經(jīng)beforeMount旺聚,beforeUpdate,mounted掛載
return mountComponent(this, el, hydrating)
}
從上面的代碼里面我們知道Vue來(lái)自core/index.js
分析core/index.js
initGlobalAPI(Vue) // 這里會(huì)給Vue掛載靜態(tài)方法 use,mixin,extend,component,directive,filter
分析core/instance/index.js
到這里才見(jià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) // 運(yùn)行_init 給vue進(jìn)一步初始化,已經(jīng)注冊(cè)beforeCreate造挽,created
}
// 在Vue的原型上面掛在相關(guān)方法
initMixin(Vue) // 掛載_init
stateMixin(Vue) // 掛載$data碱璃,$props,$set饭入,$delete嵌器,$watch
eventsMixin(Vue) // $on,$once,$off,$emit
lifecycleMixin(Vue)// _update ,$forceUpdate,$destroy
renderMixin(Vue) //$nextTick,_render
// init.js分析重要部分
// 這里對(duì)options初始化**mergeOptions**后續(xù)會(huì)講
vm.$options = mergeOptions(
resolveConstructorOptions(vm.constructor),
options || {},
vm
)
// 深入初始化
initLifecycle(vm) // 初始化生命周期相關(guān)屬性
initEvents(vm) // 初始化事件相關(guān)屬性
initRender(vm) // 初始化render所需要的方法 vm._c vm.$createElement
callHook(vm, 'beforeCreate') // 調(diào)用beforeCreate生命周期鉤子
initInjections(vm) // resolve injections before data/props
initState(vm) // 初始化state
initProvide(vm) // resolve provide after data/props
callHook(vm, 'created') // 調(diào)用created生命周期鉤子
// 掛載
if (vm.$options.el) {
vm.$mount(vm.$options.el) // 這里調(diào)用的是web/entry-runtime-with-compiler.js 里面的mount
}
這里有幾個(gè)面試題
1 vue的provide 和 inject注冊(cè)順序以及怎么注冊(cè)?
2 vue在beforeCreate之前干了什么谐丢?created之前做了什么爽航?
通過(guò)上面的分析我們已經(jīng)對(duì)Vue有了一個(gè)大概的認(rèn)識(shí)能回答從new Vue()到mount做了哪一些事?后續(xù)我們會(huì)繼續(xù)分析這些‘事’都是什么
小節(jié)Vue掛載流程:
- Vue.prototype.patch 掛載patch方法
- Vue.prototype.$mount 運(yùn)行mountComponent
- Vue.prototype.$mount 解析render
- initGlobalAPI(Vue) 給Vue掛載靜態(tài)方
- 在Vue的原型上面掛在相關(guān)方法和屬性
- 調(diào)用_init()
- 在initState里面干了什么?下章節(jié)將
- 調(diào)用 3中的$mount()對(duì)render進(jìn)行判斷并且生成對(duì)應(yīng)的render
- 運(yùn)行mountComponent