vue 源碼詳解(一):原型對象和全局 API
的設(shè)計
1. 從 new Vue()
開始
我們在實際的項目中使用 Vue
的時候 , 一般都是在 main.js
中通過 new Vue({el : '#app , ...options})
生成根組件進行使用的撕彤, 相關(guān)的配置都通過 options
傳入。 Vue
的原型對象會幫我們初始化好很多屬性和方法梯捕, 我們可以通過 this.property
直接調(diào)用即可掰派; 而 Vue
這個類也通過類的靜態(tài)方法初始化了一些全局的 api维费, 我們可以通過類名直接調(diào)用怔毛, 比如 Vue.component()
员萍。 Vue
的原型對象和全局 API 是通過混入的方式融入 Vue
中的。
如下面代碼所示拣度, import Vue from './instance/index'
引入 Vue
的構(gòu)造函數(shù)碎绎,在用戶調(diào)用之前, Vue
先做了一些初始化工作抗果, 具體做了哪些工作看 <a href="#vue/src/core/instance/index.js">vue/src/core/instance/index.js(點擊跳轉(zhuǎn))
</a>中的代碼(下邊第二段):
-
function Vue (options) { ... }
定義了Vue
構(gòu)造函數(shù)筋帖, 我們調(diào)用new Vue
時,只會執(zhí)行一句代碼冤馏, 即this._init(options)
; - 定義完構(gòu)造函數(shù)日麸,依次調(diào)用
initMixin(Vue) stateMixin(Vue) eventsMixin(Vue) lifecycleMixin(Vue) renderMixin(Vue)
, 從而將Vue
的初始化函數(shù)宿接、狀態(tài)初始化函數(shù)赘淮、事件初始化函數(shù)、生命周期初始化函數(shù)睦霎、渲染函數(shù)混入到Vue
的原型對象。這才使得每個組件都有了便捷的功能走诞。初始化函數(shù)具體都做了什么工作副女, 且看后續(xù)的分析。
vue/src/core/index.js
:
import Vue from './instance/index' // 1. 引入 Vue 構(gòu)造函數(shù)
import { initGlobalAPI } from './global-api/index' // 2. 引入初始化全局 API 的依賴
import { isServerRendering } from 'core/util/env'
import { FunctionalRenderContext } from 'core/vdom/create-functional-component'
initGlobalAPI(Vue) // 3. 初始化全局 API
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
<strong id="vue/src/core/instance/index.js">vue/src/core/instance/index.js
</strong>
import { initMixin } from './init'
import { stateMixin } from './state'
import { renderMixin } from './render'
import { eventsMixin } from './events'
import { lifecycleMixin } from './lifecycle'
import { warn } from '../util/index'
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) // 1. Vue 實例初始化
}
initMixin(Vue) // 2
stateMixin(Vue) // 3
eventsMixin(Vue) // 4
lifecycleMixin(Vue) // 5
renderMixin(Vue) // 6
export default Vue
注釋 1
處蚣旱, new Vue()
時碑幅, 只執(zhí)行了一個初始化工作 this._init(options)
; 值得注意的是戴陡, 在定義完成構(gòu)造函數(shù)后,此時尚未有 new Vue
的調(diào)用沟涨, 即在實例創(chuàng)建之前恤批, 會執(zhí)行注釋 2 3 4 5 6
處的初始化工作, 讓后初始化全局 API
,至此準(zhǔn)備工作已經(jīng)就緒裹赴, 通過調(diào)用 new Vue
生成 Vue
實例時喜庞,會調(diào)用 this._init(options)
。接下來棋返,探索一下 Vue
生成實例前延都, 依次做了哪些工作。
1.1 initMixin
(vue\src\core\instance\init.js
)
let uid = 0
export function initMixin (Vue: Class<Component>) {
Vue.prototype._init = function (options?: Object) {
const vm: Component = this // 1. vm 即 this睛竣, 即 Vue 的實例對象
// a uid
vm._uid = uid++ // 每個 Vue 實例對象都可以看成一個組件晰房, 每個組件有一個 _uid 屬性來標(biāo)記唯一性
let startTag, endTag
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
startTag = `vue-perf-start:${vm._uid}`
endTag = `vue-perf-end:${vm._uid}`
mark(startTag)
}
// a flag to avoid this being observed
vm._isVue = true
// merge options
// 合并參數(shù), options 是我們調(diào)用 `new Vue({ el : 'app'chuand, ...args})` 時傳入的參數(shù)
// 合并完成后將合并結(jié)果掛載到當(dāng)前 `Vue` 實例
if (options && options._isComponent) {
// optimize internal component instantiation
// since dynamic options merging is pretty slow, and none of the
// internal component options needs special treatment.
initInternalComponent(vm, options)
} else {
vm.$options = mergeOptions( // 合并完成后將合并結(jié)果掛載到當(dāng)前 `Vue` 實例
// 這個函數(shù)會檢查當(dāng)前 Vue 實例的否早函數(shù)和其父類、祖先類上的 options 選項, 并能監(jiān)聽是否發(fā)生了變化罕邀, 將 祖先類锉矢、父類和當(dāng)前 Vue 實例的 options 合并到一起
resolveConstructorOptions(vm.constructor),
options || {},
vm
)
}
/* istanbul ignore else */
if (process.env.NODE_ENV !== 'production') {
initProxy(vm)
} else {
vm._renderProxy = vm
}
// expose real self
vm._self = vm
initLifecycle(vm) // 1. 初始化聲明周期
initEvents(vm)
initRender(vm)
callHook(vm, 'beforeCreate')
initInjections(vm) // resolve injections before data/props
initState(vm)
initProvide(vm) // resolve provide after data/props
callHook(vm, 'created')
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
vm._name = formatComponentName(vm, false)
mark(endTag)
measure(`vue ${vm._name} init`, startTag, endTag)
}
if (vm.$options.el) {
vm.$mount(vm.$options.el)
}
}
}
1.1.1 initLifecycle
上邊代碼給每個實例標(biāo)記一個唯一的 _uid
屬性, 然后標(biāo)記是否為 Vue
實例, 將用戶傳入的參數(shù)和 Vue
自有參數(shù)合并后吁津,掛載到 Vue
的 $options
屬性 。
export function initLifecycle (vm: Component) {
const options = vm.$options
// locate first non-abstract parent
// 這個注釋已經(jīng)很明了了, 就是查找當(dāng)前 vue 實例的第一個非抽象父組件
// 找到后會將當(dāng)前的組件合并到父組件的 `$children` 數(shù)組里
// 從而建立了組件的父子關(guān)系
let parent = options.parent
if (parent && !options.abstract) {
while (parent.$options.abstract && parent.$parent) {
parent = parent.$parent
}
parent.$children.push(vm)
}
vm.$parent = parent
vm.$root = parent ? parent.$root : vm
vm.$children = []
vm.$refs = {}
vm._watcher = null
vm._inactive = null // 這倆先忽略距误, 俺也不知道干嘛的
vm._directInactive = false // 這倆先忽略, 俺也不知道干嘛的
vm._isMounted = false
vm._isDestroyed = false
vm._isBeingDestroyed = false
}
如上扁位, 初始化聲明周期的時候准潭, 會簡歷當(dāng)前組件與其他組件的父子關(guān)系, 如果找到父組件域仇, 會將 $root
指針指向父組件刑然,找不到的話, 指向當(dāng)前 Vue 實例暇务。接下來 vm.$children = []
初始化子組件列表泼掠, vm.$refs = {}
初始化引用列表, vm._watcher = null
初始化觀察者列表垦细, 此時還沒有觀察者择镇,無法檢測數(shù)據(jù)變化, vm._isMounted = false
標(biāo)記當(dāng)前組件尚未掛載到 DOM
, vm._isDestroyed = false
標(biāo)記當(dāng)前組件并不是一個被銷毀的實例括改,這與垃圾回收有關(guān)系的腻豌, vm._isBeingDestroyed = false
標(biāo)記當(dāng)前組件是否正在銷毀工作。
至此, 聲明周期的初始化已經(jīng)完成了吝梅。
1.1.2 initEvents
vue/src/core/instance/events.js
:
1.2 stateMixin
: 狀態(tài)初始化
vue/src/core/instance/state.js
:
export function stateMixin (Vue: Class<Component>) {
// flow somehow has problems with directly declared definition object
// when using Object.defineProperty, so we have to procedurally build up
// the object here.
const dataDef = {}
dataDef.get = function () { return this._data }
const propsDef = {}
propsDef.get = function () { return this._props }
if (process.env.NODE_ENV !== 'production') {
dataDef.set = function () {
warn(
'Avoid replacing instance root $data. ' +
'Use nested data properties instead.',
this
)
}
propsDef.set = function () {
warn(`$props is readonly.`, this)
}
}
Object.defineProperty(Vue.prototype, '$data', dataDef) // 1
Object.defineProperty(Vue.prototype, '$props', propsDef) // 2
Vue.prototype.$set = set // 3
Vue.prototype.$delete = del // 4
Vue.prototype.$watch = function (
expOrFn: string | Function,
cb: any,
options?: Object
): Function {
const vm: Component = this
if (isPlainObject(cb)) {
return createWatcher(vm, expOrFn, cb, options)
}
options = options || {}
options.user = true
const watcher = new Watcher(vm, expOrFn, cb, options)
if (options.immediate) {
try {
cb.call(vm, watcher.value)
} catch (error) {
handleError(error, vm, `callback for immediate watcher "${watcher.expression}"`)
}
}
return function unwatchFn () {
watcher.teardown()
}
}
}
上邊代碼對實例的狀態(tài)做了初始化虱疏。 在注釋 1 2
兩個地方分別給 Vue
原型對象增加了 $data $props
兩個屬性, 這兩個屬性的值分別是當(dāng)前 vm
的 _data _props
屬性苏携, 并且設(shè)置這兩個屬性是不可以修改的做瞪。
注釋 3 4
處為 vm
添加了 set
和 delete
方法, set
和 delete
是干嘛的就不用介紹了吧右冻, Vue
對象本身也有 Vue.set
和 Vue.delete
這兩個方法装蓬, 都是來源于下邊 set
這個函數(shù), 他的作用體現(xiàn)在下邊代碼注釋的 1 2
處:
參數(shù)
target
為對象或者數(shù)組,target
有一個__ob__
屬性国旷, 這個屬性的來源是在Observer
這個類中的構(gòu)造函數(shù)矛物,其中有一句是def(value, '__ob__', this)
,value
是待觀測的對象, 也就是我們寫代碼時傳入的data
中的屬性跪但, 然后我們傳入的data
其實都被代理到__ob__
這個屬性上了履羞,以后我們操作data
中的數(shù)據(jù)或者訪問data
中的數(shù)據(jù)都會被代理到__ob__
這個屬性。
之后又在原型對象掛載了 $watcher
方法屡久, 該方法的返回值是一個銷毀 watcher
的方法忆首。 至于 watcher
是個啥, 以及 watcher
的作用被环,后邊再談糙及。
/**
* Set a property on an object. Adds the new property and
* triggers change notification if the property doesn't
* already exist.
*/
export function set (target: Array<any> | Object, key: any, val: any): any {
if (process.env.NODE_ENV !== 'production' &&
(isUndef(target) || isPrimitive(target))
) {
warn(`Cannot set reactive property on undefined, null, or primitive value: ${(target: any)}`)
}
if (Array.isArray(target) && isValidArrayIndex(key)) {
target.length = Math.max(target.length, key)
target.splice(key, 1, val)
return val
}
if (key in target && !(key in Object.prototype)) {
target[key] = val
return val
}
const ob = (target: any).__ob__
if (target._isVue || (ob && ob.vmCount)) {
process.env.NODE_ENV !== 'production' && warn(
'Avoid adding reactive properties to a Vue instance or its root $data ' +
'at runtime - declare it upfront in the data option.'
)
return val
}
if (!ob) {
target[key] = val
return val
}
defineReactive(ob.value, key, val) // 1
ob.dep.notify() // 2
return val
}
vue\src\core\util\lang.js
:
/**
* Define a property.
*/
export function def (obj: Object, key: string, val: any, enumerable?: boolean) {
Object.defineProperty(obj, key, {
value: val,
enumerable: !!enumerable,
writable: true,
configurable: true
})
}
1.3 事件初始化
其實就是在 Vue 原型對象上掛載了一些方法 ($on $once $off $emit
) , 基于發(fā)布訂閱模式筛欢,實現(xiàn)了一個事件響應(yīng)系統(tǒng)浸锨, 與 nodejs 中的 eventEmitter 是極其相似的。這就是我們常用的事件總線機制的來源版姑。
簡單解析一下下面的代碼 :
$on
是事件的訂閱柱搜, 通過他的參數(shù)(event: string | Array<string>, fn: Function)
可知, 可以一次訂閱多個事件剥险,他們共享一個處理函數(shù)聪蘸, 然后將所有的處理函數(shù)以鍵值對的形式({eventName : handler[]}
)存儲在vm._events
對象中,等待事件發(fā)布表制。一旦事件發(fā)布健爬, 就會根據(jù)事件類型(eventName
)去事件處理函數(shù)列表(handler[]
)中,讀取處理函數(shù)并執(zhí)行么介。
$emit
是事件的發(fā)布娜遵, 生產(chǎn)環(huán)境中對事件名稱(也就是類型),進行了大小寫轉(zhuǎn)換壤短, 不用區(qū)分事件名稱的大小寫了魔熏, 當(dāng)然我們編碼不能這樣粗狂的去寫哈衷咽。 然后cbs
是根據(jù)事件名稱讀取的處理函數(shù)的列表蒜绽,const args = toArray(arguments, 1)
是處理事件的參數(shù), 函數(shù)toArray
將$emit
函數(shù)的參數(shù)除掉第一個以后躲雅, 最終傳入了我們的訂閱函數(shù)中。 即
vm.$emit('render', 'a',124)
代碼最終調(diào)用結(jié)果是vm._events['render']
列表中所有的函數(shù)都以('a', 123)
為參數(shù)運行一次相赁。
$off
是將事件的訂閱函數(shù)從訂閱列表中刪除, 它提供了兩個參數(shù)(event?: string | Array<string>, fn?: Function)
, 兩個參數(shù)都是可選的钮科, 并且不能只穿第二參數(shù)。 如果實參列表為空婆赠, 則當(dāng)前vm
上訂閱的所有事件和事件的處理函數(shù)都將被刪除;如果第二參數(shù)為空休里, 則當(dāng)前vm
的vm._events[event]
中所有的處理函數(shù)將被清空; 如果第二個參數(shù)fn
不為空妙黍, 則只將vm._events[event]
事件處理列表中的fn
函數(shù)刪除悴侵。
$once
表示事件處理只執(zhí)行一次, 多次發(fā)布事件拭嫁,也只會執(zhí)行一次處理函數(shù)可免。這個函數(shù)有點小技巧。先建立一個on
函數(shù)做粤, 然后把事件處理函數(shù)fn
掛載到這個函數(shù)對象上, 函數(shù)也是對象浇借,可以有自己的屬性,這個沒有疑問吧驮宴。on
函數(shù)中只有兩句代碼vm.$off(event, on)
, 讓 vm 解除on
函數(shù)的訂閱逮刨, 這就可以保證以后不會再執(zhí)行on
函數(shù)了; 下一句fn.apply(vm, arguments)
調(diào)用fn
, 這保證了fn
被執(zhí)行了一次堵泽。 哈哈哈修己, 666.
事件的初始化就這樣講完了。
export function eventsMixin (Vue: Class<Component>) {
const hookRE = /^hook:/
Vue.prototype.$on = function (event: string | Array<string>, fn: Function): Component {
const vm: Component = this
if (Array.isArray(event)) {
for (let i = 0, l = event.length; i < l; i++) {
vm.$on(event[i], fn)
}
} else {
(vm._events[event] || (vm._events[event] = [])).push(fn)
// optimize hook:event cost by using a boolean flag marked at registration
// instead of a hash lookup
if (hookRE.test(event)) {
vm._hasHookEvent = true
}
}
return vm
}
Vue.prototype.$once = function (event: string, fn: Function): Component {
const vm: Component = this
function on () {
vm.$off(event, on)
fn.apply(vm, arguments)
}
on.fn = fn
vm.$on(event, on)
return vm
}
Vue.prototype.$off = function (event?: string | Array<string>, fn?: Function): Component {
const vm: Component = this
// all
if (!arguments.length) {
vm._events = Object.create(null)
return vm
}
// array of events
if (Array.isArray(event)) {
for (let i = 0, l = event.length; i < l; i++) {
vm.$off(event[i], fn)
}
return vm
}
// specific event
const cbs = vm._events[event]
if (!cbs) {
return vm
}
if (!fn) {
vm._events[event] = null
return vm
}
// specific handler
let cb
let i = cbs.length
while (i--) {
cb = cbs[i]
if (cb === fn || cb.fn === fn) {
cbs.splice(i, 1)
break
}
}
return vm
}
Vue.prototype.$emit = function (event: string): Component {
const vm: Component = this
if (process.env.NODE_ENV !== 'production') {
const lowerCaseEvent = event.toLowerCase()
if (lowerCaseEvent !== event && vm._events[lowerCaseEvent]) {
tip(
`Event "${lowerCaseEvent}" is emitted in component ` +
`${formatComponentName(vm)} but the handler is registered for "${event}". ` +
`Note that HTML attributes are case-insensitive and you cannot use ` +
`v-on to listen to camelCase events when using in-DOM templates. ` +
`You should probably use "${hyphenate(event)}" instead of "${event}".`
)
}
}
let cbs = vm._events[event]
if (cbs) {
cbs = cbs.length > 1 ? toArray(cbs) : cbs
const args = toArray(arguments, 1)
const info = `event handler for "${event}"`
for (let i = 0, l = cbs.length; i < l; i++) {
invokeWithErrorHandling(cbs[i], vm, args, vm, info)
}
}
return vm
}
}
1.4 lifecycleMixin
生命周期初始化
代碼如下迎罗, 在 Vue 的原型對象上增加了三個方法 _update $forceUpdate $destroy
, 依次來看下都做了什么事吧睬愤。
vm._update
通過__patch__
函數(shù)把虛擬節(jié)點vnode
編譯成真實DOM
. 并且, 組件的更新也是在這里完成虛擬節(jié)點到真實DOM
的轉(zhuǎn)換纹安。并且父組件更新后尤辱, 子組件也會更新砂豌。
vm.$forceUpdate
若果當(dāng)前組件上有觀察者, 則直接更細組件光督。
vm.$destroy
銷毀組件阳距, 如果當(dāng)前組件正在走銷毀的流程,則直接返回结借, 等待繼續(xù)銷毀筐摘。 否則, 會觸發(fā)beforeDestroy
這個聲明周期船老, 并將當(dāng)前組件標(biāo)記為正在銷毀
的狀態(tài)咖熟。 然后將當(dāng)前組件從父組件中刪除, 然后銷毀所有的 watcher柳畔, 銷毀vm._data__ob__
, 標(biāo)記組件狀態(tài)為已銷毀
,重新生成真實DOM
, 觸發(fā)destroyed
生命周期方法薪韩, 移除當(dāng)前組件訂閱的事件和事件的處理函數(shù), 將當(dāng)前組件對父組件的引用清空躬存。
vue/src/core/instance/lifecycle.js
export function lifecycleMixin (Vue: Class<Component>) {
Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {
const vm: Component = this
const prevEl = vm.$el
const prevVnode = vm._vnode
const restoreActiveInstance = setActiveInstance(vm)
vm._vnode = vnode
// Vue.prototype.__patch__ is injected in entry points
// based on the rendering backend used.
if (!prevVnode) {
// initial render
vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */)
} else {
// updates
vm.$el = vm.__patch__(prevVnode, vnode)
}
restoreActiveInstance()
// update __vue__ reference
if (prevEl) {
prevEl.__vue__ = null
}
if (vm.$el) {
vm.$el.__vue__ = vm
}
// if parent is an HOC, update its $el as well
if (vm.$vnode && vm.$parent && vm.$vnode === vm.$parent._vnode) {
vm.$parent.$el = vm.$el
}
// updated hook is called by the scheduler to ensure that children are
// updated in a parent's updated hook.
}
Vue.prototype.$forceUpdate = function () {
const vm: Component = this
if (vm._watcher) {
vm._watcher.update()
}
}
Vue.prototype.$destroy = function () {
const vm: Component = this
if (vm._isBeingDestroyed) {
return
}
callHook(vm, 'beforeDestroy')
vm._isBeingDestroyed = true
// remove self from parent
const parent = vm.$parent
if (parent && !parent._isBeingDestroyed && !vm.$options.abstract) {
remove(parent.$children, vm)
}
// teardown watchers
if (vm._watcher) {
vm._watcher.teardown()
}
let i = vm._watchers.length
while (i--) {
vm._watchers[i].teardown()
}
// remove reference from data ob
// frozen object may not have observer.
if (vm._data.__ob__) {
vm._data.__ob__.vmCount--
}
// call the last hook...
vm._isDestroyed = true
// invoke destroy hooks on current rendered tree
vm.__patch__(vm._vnode, null)
// fire destroyed hook
callHook(vm, 'destroyed')
// turn off all instance listeners.
vm.$off()
// remove __vue__ reference
if (vm.$el) {
vm.$el.__vue__ = null
}
// release circular reference (#6759)
if (vm.$vnode) {
vm.$vnode.parent = null
}
}
}
1.5 renderMixin
渲染函數(shù)初始化
也是向 Vue
的原型對象掛載一些方法宛逗。
installRenderHelpers(Vue.prototype)
向 vm 增加了模板的解析編譯所需要的一些方法;
$nextTick
即我們在寫代碼時常用的this.$nextTick()
, 它返回一個Promise
實例p
盾剩, 我們可以在p
的then
函數(shù)中訪問到更新到DOM
元素的數(shù)據(jù), 也可以向this.nextTick
傳遞一個回調(diào)函數(shù)f
告私,f
也可以訪問更新到DOM
元素的數(shù)據(jù)。
_render
方法生成虛擬節(jié)點驻粟。詳見后邊的代碼。
vue/src/core/instance/render.js
export function renderMixin (Vue: Class<Component>) {
// install runtime convenience helpers
installRenderHelpers(Vue.prototype)
Vue.prototype.$nextTick = function (fn: Function) {
return nextTick(fn, this)
}
Vue.prototype._render = function (): VNode {
const vm: Component = this
const { render, _parentVnode } = vm.$options
if (_parentVnode) {
vm.$scopedSlots = normalizeScopedSlots(
_parentVnode.data.scopedSlots,
vm.$slots,
vm.$scopedSlots
)
}
// set parent vnode. this allows render functions to have access
// to the data on the placeholder node.
vm.$vnode = _parentVnode
// render self
let vnode
try {
// There's no need to maintain a stack because all render fns are called
// separately from one another. Nested component's render fns are called
// when parent component is patched.
currentRenderingInstance = vm
vnode = render.call(vm._renderProxy, vm.$createElement)
} catch (e) {
handleError(e, vm, `render`)
// return error render result,
// or previous vnode to prevent render error causing blank component
/* istanbul ignore else */
if (process.env.NODE_ENV !== 'production' && vm.$options.renderError) {
try {
vnode = vm.$options.renderError.call(vm._renderProxy, vm.$createElement, e)
} catch (e) {
handleError(e, vm, `renderError`)
vnode = vm._vnode
}
} else {
vnode = vm._vnode
}
} finally {
currentRenderingInstance = null
}
// if the returned array contains only a single node, allow it
if (Array.isArray(vnode) && vnode.length === 1) {
vnode = vnode[0]
}
// return empty vnode in case the render function errored out
if (!(vnode instanceof VNode)) {
if (process.env.NODE_ENV !== 'production' && Array.isArray(vnode)) {
warn(
'Multiple root nodes returned from render function. Render function ' +
'should return a single root node.',
vm
)
}
vnode = createEmptyVNode()
}
// set parent
vnode.parent = _parentVnode
return vnode
}
}
2. Vue
全局 API
Vue
中全局 API
一共有一下 12
個。全局 API 是通過構(gòu)造函數(shù) Vue
直接調(diào)用的酷麦, 有一些方法在實例上也做了同步, 可以通過實例對象去調(diào)用沃饶。 比如常用的 Vue.nextTick
, 可以通過 this.$nextTick
進行調(diào)用轻黑。下面就依次分析一下每個全局 API 的使用和實現(xiàn)思路吧氓鄙。
- Vue.extend
- Vue.nextTick
- Vue.set
- Vue.delete
- Vue.directive
- Vue.filter
- Vue.component
- Vue.use
- Vue.mixin
- Vue.compile
- Vue.observable
- Vue.version
src/core/global-api/index.js