主线
Vue中数据状态改变后会采用virtual DOM的方式更新DOM,暂且不去深究virtual DOM内部具体实现,你只需要知道virtual DOM分为三个步骤:
- createElement(): 用 JavaScript对象(虚拟树) 描述 真实DOM对象(真实树)
- diff(oldNode, newNode) : 对比新旧两个虚拟树的区别,收集差异
- patch() : 将差异应用到真实DOM树
有的时候 第二步 可能与 第三步 合并成一步(Vue 中的patch就是这样)。
Vue 的构造函数
使用 new 操作符来调用 Vue,那么也就是说 Vue 应该是一个构造函数。
运行 npm run dev
命令:
"dev": "rollup -w -c scripts/config.js --environment TARGET:web-full-dev",
复制代码
rollup配置文件使用了scripts/config.js
文件,并设置环境变量 TARGET:web-full-dev
,打开看一下关键代码
// builds 对象
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
},
...
}
// 生成配置的方法
function genConfig (name) {
const opts = builds[name]
const config = {
input: opts.entry,
external: opts.external,
...
}
...
return config
}
if (process.env.TARGET) {
module.exports = genConfig(process.env.TARGET)
} else {
exports.getBuild = genConfig
exports.getAllBuilds = () => Object.keys(builds).map(genConfig)
}
复制代码
- genConfig 函数返回一个 config 对象,从这个对象上面大概可以看出入口文件为
src/platfroms/web/entry-runtime-with-compiler.js
import Vue from './runtime/index'
复制代码
- 继续到文件
src/platfroms/web/runtime/index.js
中找构造函数
import Vue from 'core/index'
复制代码
- 继续到文件
src/core/index.js
中找构造函数
import Vue from './instance/index'
复制代码
- 继续到文件
src/core/instance/index.js
中找构造函数
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)
}
initMixin(Vue)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)
export default Vue
复制代码
定义 Vue 构造函数,然后以Vue构造函数为参数,5个**Mixin(Vue)函数作用:在Vue 的原型 prototype 上挂载方法或属性,最后导出 Vue。
// initMixin(Vue) src/core/instance/init.js *****************
Vue.prototype._init = function (options?: Object) {}
// stateMixin(Vue) src/core/instance/state.js ****************
Vue.prototype.$data
Vue.prototype.$set = set
Vue.prototype.$delete = del
Vue.prototype.$watch = function(){}
// eventsMixin(Vue) src/core/instance/events.js **************
Vue.prototype.$on = function (event: string, fn: Function): Component {}
Vue.prototype.$once = function (event: string, fn: Function): Component {}
Vue.prototype.$off = function (event?: string, fn?: Function): Component {}
Vue.prototype.$emit = function (event: string): Component {}
// lifecycleMixin(Vue) src/core/instance/lifecycle.js ***************
Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {}
Vue.prototype.$forceUpdate = function () {}
Vue.prototype.$destroy = function () {}
// renderMixin(Vue) src/core/instance/render.js ************
Vue.prototype.$nextTick = function (fn: Function) {}
Vue.prototype._render = function (): VNode {}
复制代码
追溯路线往回走,那么下一个处理 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
}
})
// expose FunctionalRenderContext for ssr runtime helper installation
Object.defineProperty(Vue, 'FunctionalRenderContext', {
value: FunctionalRenderContext
})
Vue.version = '__VERSION__'
export default Vue
复制代码
从src/core/instance/index.js
中导入已经在原型上挂载了方法和属性后的 Vue,然后导入 initGlobalAPI 和 isServerRendering,之后将Vue作为参数传给 initGlobalAPI ,最后又在 Vue.prototype 上挂载了 $isServer ,在 Vue 上挂载了 version 属性。