Vue.use
Vue.use = function (plugin: Function | Object) {
const installedPlugins = (this._installedPlugins || (this._installedPlugins = []))
if (installedPlugins.indexOf(plugin) > -1) {
return this
}
// additional parameters
const args = toArray(arguments, 1)
args.unshift(this)
if (typeof plugin.install === 'function') {
plugin.install.apply(plugin, args)
} else if (typeof plugin === 'function') {
plugin.apply(null, args)
}
installedPlugins.push(plugin)
return this
}
installedPlugins表示已经安装的插件,如果已经安装了则直接return。
将 this也就是Vue构造函数放到第一个参数位置,然后将这些参数传递给install方法。如果插件存在install方法就调用install方法,如果没有则调用plugin这个方法进行安装插件,然后将插件添加到已安装插件列表。
Vue.mixin
Vue.mixin = function (mixin: Object) {
this.options = mergeOptions(this.options, mixin)
return this
}
这里主要是通过mergeOptions来合并配置选项:将自己本身的配置和混入的配置合并。mergeOptions方法我们会在单独的一章进行梳理。
Vue.set
function set (target: Array<any> | Object, key: any, val: any): 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 (!ob) {
target[key] = val
return val
}
defineReactive(ob.value, key, val)
ob.dep.notify()
return val
}
如果是数组则使用数组的splice方法来响应式更新。
如果key原本就存在target对象上,直接修改值。
如果target对象上没有__ob__属性,即不是响应式对象,直接修改值。
如果key不存在target上并且target是响应式对象,那么通过defineReactive使其添加到target上,并且使其响应式。然后触发target的依赖更新。
Vue.delete
function del (target: Array<any> | Object, key: any) {
if (Array.isArray(target) && isValidArrayIndex(key)) {
target.splice(key, 1)
return
}
const ob = (target: any).__ob__
if (!hasOwn(target, key)) {
return
}
delete target[key]
if (!ob) {
return
}
ob.dep.notify()
}
如果是数组,则通过数组的splice方法去删除。
如果属性不存在对象上,直接return结束。
如果属性存在对象上,通过delete方法删除属性,然后通过target对应的__ob__上的dep来触发更新。
Vue.nextTick
const callbacks = []
let pending = fals
function nextTick (cb?: Function, ctx?: Object) {
let _resolve
callbacks.push(() => {
if (cb) {
try {
cb.call(ctx)
} catch (e) {
handleError(e, ctx, 'nextTick')
}
} else if (_resolve) {
_resolve(ctx)
}
})
if (!pending) {
pending = true
timerFunc()
}
// $flow-disable-line
if (!cb && typeof Promise !== 'undefined') {
return new Promise(resolve => {
_resolve = resolve
})
}
}
将传入的回调函数通过try catch包裹成一个新函数,然后添加到callbacks数组中。
当pending为false,即浏览器任务队列中没有flushCallbacks函数了。将pending重置为true并通过执行timerFunc将flushCallbacks放入浏览器任务队列中。
也可以这样使用:Vue.nextTick().then(()=>{}),因此当环境支持Promise时,会返回一个promise,并执行_resolve
timerFunc
let timerFunc
if (typeof Promise !== 'undefined' && isNative(Promise)) {
const p = Promise.resolve()
timerFunc = () => {
p.then(flushCallbacks)
/**
* 在有问题的UIWebViews中,Promise.then不会完全中断,但是它可能会陷入怪异的状态,
* 在这种状态下,回调被推入微任务队列,但队列没有被刷新,直到浏览器需要执行其他工作,例如处理一个计时器。
* 因此,我们可以通过添加空计时器来“强制”刷新微任务队列。
*/
if (isIOS) setTimeout(noop)
}
isUsingMicroTask = true
} else if (!isIE && typeof MutationObserver !== 'undefined' && (
isNative(MutationObserver) ||
MutationObserver.toString() === '[object MutationObserverConstructor]'
)) {
let counter = 1
const observer = new MutationObserver(flushCallbacks)
const textNode = document.createTextNode(String(counter))
observer.observe(textNode, {
characterData: true
})
timerFunc = () => {
counter = (counter + 1) % 2
textNode.data = String(counter)
}
isUsingMicroTask = true
} else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
setTimeout.
timerFunc = () => {
setImmediate(flushCallbacks)
}
} else {
timerFunc = () => {
setTimeout(flushCallbacks, 0)
}
}
当浏览器支持Promise时首选Promise.resolve.then(),在微任务队列中放入 flushCallbacks。然后考虑MutationObserver。再考虑setImmediate(宏任务)。最后用setTimeout(cb,0)兜底。不管用哪种方式最终都是调用flushCallbacks函数。
flushCallbacks
function flushCallbacks () {
pending = false
const copies = callbacks.slice(0)
callbacks.length = 0
for (let i = 0; i < copies.length; i++) {
copies[i]()
}
}
将pending置为false,表示浏览器任务队列已经执行完了flushCallbacks,循环callbacks,执行之前存放的包裹后的函数。
Vue.extend
const ASSET_TYPES = ['component','directive','filter']
Vue.extend = function (extendOptions: Object): Function {
extendOptions = extendOptions || {}
const Super = this
const SuperId = Super.cid
const cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {})
if (cachedCtors[SuperId]) {
return cachedCtors[SuperId]
}
const name = extendOptions.name || Super.options.name
if (process.env.NODE_ENV !== 'production' && name) {
validateComponentName(name)
}
const Sub = function VueComponent (options) {
this._init(options)
}
Sub.prototype = Object.create(Super.prototype)
Sub.prototype.constructor = Sub
Sub.cid = cid++
Sub.options = mergeOptions(
Super.options,
extendOptions
)
Sub['super'] = Super
if (Sub.options.props) {
initProps(Sub)
}
if (Sub.options.computed) {
initComputed(Sub)
}
Sub.extend = Super.extend
Sub.mixin = Super.mixin
Sub.use = Super.use
ASSET_TYPES.forEach(function (type) {
Sub[type] = Super[type]
})
if (name) {
Sub.options.components[name] = Sub
}
Sub.superOptions = Super.options
Sub.extendOptions = extendOptions
Sub.sealedOptions = extend({}, Sub.options)
cachedCtors[SuperId] = Sub
return Sub
}
function initProps (Comp) {
const props = Comp.options.props
for (const key in props) {
proxy(Comp.prototype, `_props`, key)
}
}
function initComputed (Comp) {
const computed = Comp.options.computed
for (const key in computed) {
defineComputed(Comp.prototype, key, computed[key])
}
}
基于Vue去扩展子类,该子类同样支持进一步的扩展,扩展时可以传递一些默认配置,就像Vue也会有一些默认配置。
Super指向this,这里的this就时Vue。利用缓存,如果存在则直接返回缓存中的构造函数,如果你在多次调用Vue.extend时使用了同一个配置项(extendOptions),这时就会启用该缓存。
定义一个Sub构造函数,和Vue构造函数一样。Sub的原型对象指向Vue的原型对象,通过原型式继承来继承Vue。选项合并,合并Vue构造函数的配置项和传递进来的组件对象到自己的配置项上来。通过Sub.super来指向自己的父类。有props就通过initProps初始化props,这里就是循环props将它代理到当前类的原型对象上,组件内通过this._props访问。有computed则通过initComputed去初始化,循环computed然后调用defineComputed,组件内可以通过this.xxx访问。
定义 extend、mixin、use 这三个静态方法,允许在Sub基础上再进一步构造子类。然后再定义 component、filter、directive 三个静态方法。
递归组件的原理:如果组件设置了 name 属性,则将自己注册到自己的 components 选项中。
在扩展时保留对父类选项的引用,保留组件本身传递进来的对象,浅拷贝保存子类合并后的配置项。最后进行缓存并返回这个子类。
Vue.Component、Vue.Directive、Vue.Filter
Vue.options._base = Vue
const ASSET_TYPES = ['component','directive','filter']
ASSET_TYPES.forEach(type => {
Vue[type] = function (
id: string,
definition: Function | Object
): Function | Object | void {
if (!definition) {
return this.options[type + 's'][id]
} else {
if (process.env.NODE_ENV !== 'production' && type === 'component') {
validateComponentName(id)
}
if (type === 'component' && isPlainObject(definition)) {
definition.name = definition.name || id
definition = this.options._base.extend(definition)
}
if (type === 'directive' && typeof definition === 'function') {
definition = { bind: definition, update: definition }
}
this.options[type + 's'][id] = definition
return definition
}
}
})
处理Vue.Component:当definition是对象是会这样处理:如果组件配置中存在name,则使用name,否则直接使用id。这里this.options._base指向的是Vue,这里实际就是通过Vue.extend()生成一个子类。
处理Vue.Directive:如果definition是一个函数,那么它将被bind和update所使用。
最后将三者分别添加到options的components,directives,filters中,用于实例化子组件时mergeOptions合并到每个子组件配置中。