学习vue源码时,我们首先需要看的是package.json文件,该文件里配置了vue的依赖以及开发环境和生产环境的编译的启动脚本等其他信息。首先我们需要关注的是script。我们这里先看第一个dev脚本:
"dev": "rollup -w -c scripts/config.js --environment TARGET:web-full-dev"
我们可以看到vue是采用了rollup编译的脚本,然后对应的查看其配置文件config.js
const builds = {
...
// Runtime+compiler development build (Browser)
'web-full-dev': {
entry: resolve('web/entry-runtime-with-compiler.js'),
dest: resolve('dist/vue.js'),
format: 'umd',
env: 'development',
alias: { he: './entity-decoder' },
banner
},
...
}
在该文件中我们可以找到web-full-dev的配置项,并且知道其编译的文件为'web/entry-runtime-with-compiler.js'
那么我们就需要去找到该文件了。
我们可以发现entry-runtime-with-compiler.js的其中有一行代码是:
import Vue from './runtime/index'
然后继续跟着代码往上找,我们会发现还是嵌套了好几层,最后在'/instance/index'中找到我们vue的定义:最终其路劲如下:
/src/platforms/web/web-runtime-with-compiler.js
=> /src/platforms/web/runtime/index.js
=> /src/core/index.js
=> /src/core/instance/index.js
最终我们在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)
}
我们可以发现它并没有使用class的,只是一个普通的构造函数,通过 !(this instanceof Vue) 来强制使用new来构建。
之所以不采用class,个人理解是为了更好的把代码拆分。原型上的方法只需要通过prototype来添加。如下
initMixin(Vue) // 这里主要注册了_init
stateMixin(Vue) // $set,$delete,$watch
eventsMixin(Vue) // $on, $once, $off, $emit
lifecycleMixin(Vue) // _update, $forceUpdate,$destroy
renderMixin(Vue) // $nextTick, _render
这边都是基于在Vue上扩展方法,这样就把代码分离开发,方便维护。不需要全部写到Vue函数内部。
然后我们往回走,我们可以看到/src/core/index.js 中
initGlobalAPI(Vue)
Object.defineProperty(Vue.prototype, '$isServer', {
get: isServerRendering
})
Object.defineProperty(Vue.prototype, '$ssrContext', {
get () {
/* istanbul ignore next */
return this.$vnode && this.$vnode.ssrContext
}
})
Object.defineProperty(Vue, 'FunctionalRenderContext', {
value: FunctionalRenderContext
})
Vue.version = '__VERSION__'
该文件中主要就是注册全局API,以供我们内部或外部使用
再往上/src/platforms/web/runtime/index.js,我们可以看到
// install platform specific utils
Vue.config.mustUseProp = mustUseProp
Vue.config.isReservedTag = isReservedTag
Vue.config.isReservedAttr = isReservedAttr
Vue.config.getTagNamespace = getTagNamespace
Vue.config.isUnknownElement = isUnknownElement
// install platform runtime directives & components
extend(Vue.options.directives, platformDirectives)
extend(Vue.options.components, platformComponents)
// install platform patch function
Vue.prototype.__patch__ = inBrowser ? patch : noop // 用于把vNode显示到dom上的方法,这边要区分是浏览器环境还是weex环境
// public mount method
Vue.prototype.$mount = function () {
.....
} // 把dom挂在到页面上
这边就是注册一些全局的工具,以及patch方法
那么再往上:src/platforms/web/web-runtime-with-compiler.js, 这个文件中主要就是重写$mount
const mount = Vue.prototype.$mount
Vue.prototype.$mount = function (
){
...
}
那么为什么要重写呢?原来在runtime里面的$mount方法是没有编译功能的,而最后一个重写就是增加了编译。
在vue脚手架我们会让我们选择哪个版本:如下图
这就是2个版本的区别,是否包含编译功能。一个是完整版,一个是运行时。
vue官方属于解释了:
完整版:同时包含编译器和运行时的版本。
编译器:用来将模板字符串编译成为 JavaScript 渲染函数的代码。
运行时:用来创建 Vue 实例、渲染并处理虚拟 DOM 等的代码。基本上就是除去编译器的其它一切。
如果你需要在客户端编译模板 (比如传入一个字符串给 template 选项,或挂载到一个元素上并以其 DOM 内部的 HTML 作为模板),就将需要加上编译器,即完整版:
// 需要编译器
new Vue({
template: '<div>{{ hi }}</div>'
})
// 不需要编译器
new Vue({
render (h) {
return h('div', this.hi)
}
})
重写$mount方法就是对template就行了编译转换为render方法
好了这边就简要的介绍了vue的入口。
您的点赞是我继续努力的动力!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。