从毕业到现在也写的有两年Vue了,本着高深追求就去学习了Vue的源码,就把学习过程中的理解记录下来,这将是一个系列的文章,一次不可能写完,会不断更新,后续还会有vuex和vue-router系列
先从Vue的目录说起
image.png
- scripts目录里面包含我们打包所需要的脚本
- src里面包含的就是vue的源码目录了
- compiler模版编辑器生成 render,ast,staticRenderFns
- core vue的主要代码目录包含生命周期,静态方法,原型方法,属性,vdom,observe等一系列
- platform 打包的入口 也是我们阅读源码的入口(这里只讲web)
- server 服务端渲染相关
- sfc 解析单文件组件
- shared 项目包含的公共方法和常量
这里先讲解vue的加载流程
每行代码基本都有注释,请细看注释
分析packge.json
"build": "node scripts/build.js" //告诉我们打包的入口是scripts/build.js
分析打包
// build.js
let builds = require('./config').getAllBuilds() // 引入config
.....
build(builds) // 打包通过分析config.js
// config.js,这里选择这一个 也是我们最常用的vue.min.js
'web-full-prod': {
entry: resolve('web/entry-runtime-with-compiler.js'), // 将从这个目录读起
dest: resolve('dist/vue.min.js'),
format: 'umd',
env: 'production',
alias: { he: './entity-decoder' },
banner
},
分析web/entry-runtime-with-compiler.js
将会简化这里的代码后续慢慢补充
// web/entry-runtime-with-compiler.js
const idToTemplate = cached(id => { //将template缓存起来,避免重复解析
const el = query(id)
return el && el.innerHTML
})
const mount = Vue.prototype.$mount // 来自runtime/index.js 运行mountComponent
Vue.prototype.$mount =function(){
// 1解析template
// 2生成options.render
} // 挂载$mount 这里需要分析清楚这两个$mount
// runtime/index.js
Vue.prototype.__patch__ = inBrowser ? patch : noop //添加patch
Vue.prototype.$mount= function(){
// 运行mountComponent dom挂载已经beforeMount,beforeUpdate,mounted挂载
return mountComponent(this, el, hydrating)
}
从上面的代码里面我们知道Vue来自core/index.js
分析core/index.js
initGlobalAPI(Vue) // 这里会给Vue挂载静态方法 use,mixin,extend,component,directive,filter
分析core/instance/index.js
到这里才见到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) // 运行_init 给vue进一步初始化,已经注册beforeCreate,created
}
// 在Vue的原型上面挂在相关方法
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分析重要部分
// 这里对options初始化**mergeOptions**后续会讲
vm.$options = mergeOptions(
resolveConstructorOptions(vm.constructor),
options || {},
vm
)
// 深入初始化
initLifecycle(vm) // 初始化生命周期相关属性
initEvents(vm) // 初始化事件相关属性
initRender(vm) // 初始化render所需要的方法 vm._c vm.$createElement
callHook(vm, 'beforeCreate') // 调用beforeCreate生命周期钩子
initInjections(vm) // resolve injections before data/props
initState(vm) // 初始化state
initProvide(vm) // resolve provide after data/props
callHook(vm, 'created') // 调用created生命周期钩子
// 挂载
if (vm.$options.el) {
vm.$mount(vm.$options.el) // 这里调用的是web/entry-runtime-with-compiler.js 里面的mount
}
这里有几个面试题
1 vue的provide 和 inject注册顺序以及怎么注册?
2 vue在beforeCreate之前干了什么?created之前做了什么?
通过上面的分析我们已经对Vue有了一个大概的认识能回答从new Vue()到mount做了哪一些事?后续我们会继续分析这些‘事’都是什么
小节Vue挂载流程:
- Vue.prototype.patch 挂载patch方法
- Vue.prototype.$mount 运行mountComponent
- Vue.prototype.$mount 解析render
- initGlobalAPI(Vue) 给Vue挂载静态方
- 在Vue的原型上面挂在相关方法和属性
- 调用_init()
- 在initState里面干了什么?下章节将
- 调用 3中的$mount()对render进行判断并且生成对应的render
- 运行mountComponent