主線
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 屬性。