wip: Vue.util compat
This commit is contained in:
		
							parent
							
								
									c55f3ed0e8
								
							
						
					
					
						commit
						62bfdae043
					
				@ -16,7 +16,7 @@ import { devtoolsInitApp, devtoolsUnmountApp } from './devtools'
 | 
				
			|||||||
import { isFunction, NO, isObject } from '@vue/shared'
 | 
					import { isFunction, NO, isObject } from '@vue/shared'
 | 
				
			||||||
import { version } from '.'
 | 
					import { version } from '.'
 | 
				
			||||||
import { installCompatMount } from './compat/global'
 | 
					import { installCompatMount } from './compat/global'
 | 
				
			||||||
import { installLegacyConfigTraps } from './compat/globalConfig'
 | 
					import { installLegacyConfigProperties } from './compat/globalConfig'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface App<HostElement = any> {
 | 
					export interface App<HostElement = any> {
 | 
				
			||||||
  version: string
 | 
					  version: string
 | 
				
			||||||
@ -307,7 +307,7 @@ export function createAppAPI<HostElement>(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    if (__COMPAT__) {
 | 
					    if (__COMPAT__) {
 | 
				
			||||||
      installCompatMount(app, context, render, hydrate)
 | 
					      installCompatMount(app, context, render, hydrate)
 | 
				
			||||||
      if (__DEV__) installLegacyConfigTraps(app.config)
 | 
					      if (__DEV__) installLegacyConfigProperties(app.config)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return app
 | 
					    return app
 | 
				
			||||||
 | 
				
			|||||||
@ -18,6 +18,7 @@ export const enum DeprecationTypes {
 | 
				
			|||||||
  GLOBAL_SET = 'GLOBAL_SET',
 | 
					  GLOBAL_SET = 'GLOBAL_SET',
 | 
				
			||||||
  GLOBAL_DELETE = 'GLOBAL_DELETE',
 | 
					  GLOBAL_DELETE = 'GLOBAL_DELETE',
 | 
				
			||||||
  GLOBAL_OBSERVABLE = 'GLOBAL_OBSERVABLE',
 | 
					  GLOBAL_OBSERVABLE = 'GLOBAL_OBSERVABLE',
 | 
				
			||||||
 | 
					  GLOBAL_UTIL = 'GLOBAL_UTIL',
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  CONFIG_SILENT = 'CONFIG_SILENT',
 | 
					  CONFIG_SILENT = 'CONFIG_SILENT',
 | 
				
			||||||
  CONFIG_DEVTOOLS = 'CONFIG_DEVTOOLS',
 | 
					  CONFIG_DEVTOOLS = 'CONFIG_DEVTOOLS',
 | 
				
			||||||
@ -113,6 +114,12 @@ const deprecationData: Record<DeprecationTypes, DeprecationData> = {
 | 
				
			|||||||
    link: `https://v3.vuejs.org/api/basic-reactivity.html`
 | 
					    link: `https://v3.vuejs.org/api/basic-reactivity.html`
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  [DeprecationTypes.GLOBAL_UTIL]: {
 | 
				
			||||||
 | 
					    message:
 | 
				
			||||||
 | 
					      `Vue.util has been removed. Please refactor to avoid its usage ` +
 | 
				
			||||||
 | 
					      `since it was an internal API even in Vue 2.`
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  [DeprecationTypes.CONFIG_SILENT]: {
 | 
					  [DeprecationTypes.CONFIG_SILENT]: {
 | 
				
			||||||
    message:
 | 
					    message:
 | 
				
			||||||
      `config.silent has been removed because it is not good practice to ` +
 | 
					      `config.silent has been removed because it is not good practice to ` +
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,19 @@
 | 
				
			|||||||
import { reactive } from '@vue/reactivity'
 | 
					import {
 | 
				
			||||||
import { isFunction } from '@vue/shared'
 | 
					  isReactive,
 | 
				
			||||||
 | 
					  reactive,
 | 
				
			||||||
 | 
					  track,
 | 
				
			||||||
 | 
					  TrackOpTypes,
 | 
				
			||||||
 | 
					  trigger,
 | 
				
			||||||
 | 
					  TriggerOpTypes
 | 
				
			||||||
 | 
					} from '@vue/reactivity'
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  isFunction,
 | 
				
			||||||
 | 
					  extend,
 | 
				
			||||||
 | 
					  NOOP,
 | 
				
			||||||
 | 
					  EMPTY_OBJ,
 | 
				
			||||||
 | 
					  isArray,
 | 
				
			||||||
 | 
					  isObject
 | 
				
			||||||
 | 
					} from '@vue/shared'
 | 
				
			||||||
import { warn } from '../warning'
 | 
					import { warn } from '../warning'
 | 
				
			||||||
import { cloneVNode, createVNode } from '../vnode'
 | 
					import { cloneVNode, createVNode } from '../vnode'
 | 
				
			||||||
import { RootRenderFunction } from '../renderer'
 | 
					import { RootRenderFunction } from '../renderer'
 | 
				
			||||||
@ -20,7 +34,7 @@ import {
 | 
				
			|||||||
  isRuntimeOnly,
 | 
					  isRuntimeOnly,
 | 
				
			||||||
  setupComponent
 | 
					  setupComponent
 | 
				
			||||||
} from '../component'
 | 
					} from '../component'
 | 
				
			||||||
import { RenderFunction } from '../componentOptions'
 | 
					import { RenderFunction, mergeOptions } from '../componentOptions'
 | 
				
			||||||
import { ComponentPublicInstance } from '../componentPublicInstance'
 | 
					import { ComponentPublicInstance } from '../componentPublicInstance'
 | 
				
			||||||
import { devtoolsInitApp } from '../devtools'
 | 
					import { devtoolsInitApp } from '../devtools'
 | 
				
			||||||
import { Directive } from '../directives'
 | 
					import { Directive } from '../directives'
 | 
				
			||||||
@ -129,17 +143,14 @@ export function createCompatVue(
 | 
				
			|||||||
    isCopyingConfig = false
 | 
					    isCopyingConfig = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // copy prototype augmentations as config.globalProperties
 | 
					    // copy prototype augmentations as config.globalProperties
 | 
				
			||||||
    const isPrototypeEnabled = isCompatEnabled(
 | 
					    if (isCompatEnabled(DeprecationTypes.GLOBAL_PROTOTYPE, null)) {
 | 
				
			||||||
      DeprecationTypes.GLOBAL_PROTOTYPE,
 | 
					      app.config.globalProperties = Ctor.prototype
 | 
				
			||||||
      null
 | 
					    }
 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
    let hasPrototypeAugmentations = false
 | 
					    let hasPrototypeAugmentations = false
 | 
				
			||||||
    for (const key in Ctor.prototype) {
 | 
					    for (const key in Ctor.prototype) {
 | 
				
			||||||
      if (key !== 'constructor') {
 | 
					      if (key !== 'constructor') {
 | 
				
			||||||
        hasPrototypeAugmentations = true
 | 
					        hasPrototypeAugmentations = true
 | 
				
			||||||
      }
 | 
					        break
 | 
				
			||||||
      if (isPrototypeEnabled) {
 | 
					 | 
				
			||||||
        app.config.globalProperties[key] = Ctor.prototype[key]
 | 
					 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (__DEV__ && hasPrototypeAugmentations) {
 | 
					    if (__DEV__ && hasPrototypeAugmentations) {
 | 
				
			||||||
@ -228,6 +239,21 @@ export function createCompatVue(
 | 
				
			|||||||
    // TODO compiler warning for filters (maybe behavior compat?)
 | 
					    // TODO compiler warning for filters (maybe behavior compat?)
 | 
				
			||||||
  }) as any
 | 
					  }) as any
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // internal utils - these are technically internal but some plugins use it.
 | 
				
			||||||
 | 
					  const util = {
 | 
				
			||||||
 | 
					    warn: __DEV__ ? warn : NOOP,
 | 
				
			||||||
 | 
					    extend,
 | 
				
			||||||
 | 
					    mergeOptions: (parent: any, child: any, vm?: ComponentPublicInstance) =>
 | 
				
			||||||
 | 
					      mergeOptions(parent, child, vm && vm.$),
 | 
				
			||||||
 | 
					    defineReactive
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  Object.defineProperty(Vue, 'util', {
 | 
				
			||||||
 | 
					    get() {
 | 
				
			||||||
 | 
					      assertCompatEnabled(DeprecationTypes.GLOBAL_UTIL, null)
 | 
				
			||||||
 | 
					      return util
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Vue.configureCompat = configureCompat
 | 
					  Vue.configureCompat = configureCompat
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return Vue
 | 
					  return Vue
 | 
				
			||||||
@ -358,3 +384,67 @@ export function installCompatMount(
 | 
				
			|||||||
    return instance.proxy!
 | 
					    return instance.proxy!
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const methodsToPatch = [
 | 
				
			||||||
 | 
					  'push',
 | 
				
			||||||
 | 
					  'pop',
 | 
				
			||||||
 | 
					  'shift',
 | 
				
			||||||
 | 
					  'unshift',
 | 
				
			||||||
 | 
					  'splice',
 | 
				
			||||||
 | 
					  'sort',
 | 
				
			||||||
 | 
					  'reverse'
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const patched = new WeakSet<object>()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function defineReactive(obj: any, key: string, val: any) {
 | 
				
			||||||
 | 
					  // it's possible for the orignial object to be mutated after being defined
 | 
				
			||||||
 | 
					  // and expecting reactivity... we are covering it here because this seems to
 | 
				
			||||||
 | 
					  // be a bit more common.
 | 
				
			||||||
 | 
					  if (isObject(val) && !isReactive(val) && !patched.has(val)) {
 | 
				
			||||||
 | 
					    const reactiveVal = reactive(val)
 | 
				
			||||||
 | 
					    if (isArray(val)) {
 | 
				
			||||||
 | 
					      methodsToPatch.forEach(m => {
 | 
				
			||||||
 | 
					        // @ts-ignore
 | 
				
			||||||
 | 
					        val[m] = (...args: any[]) => {
 | 
				
			||||||
 | 
					          // @ts-ignore
 | 
				
			||||||
 | 
					          Array.prototype[m].call(reactiveVal, ...args)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      Object.keys(val).forEach(key => {
 | 
				
			||||||
 | 
					        defineReactiveSimple(val, key, val[key])
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const i = obj.$
 | 
				
			||||||
 | 
					  if (i && obj === i.proxy) {
 | 
				
			||||||
 | 
					    // Vue instance, add it to data
 | 
				
			||||||
 | 
					    if (i.data === EMPTY_OBJ) {
 | 
				
			||||||
 | 
					      i.data = reactive({})
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    i.data[key] = val
 | 
				
			||||||
 | 
					    i.accessCache = Object.create(null)
 | 
				
			||||||
 | 
					  } else if (isReactive(obj)) {
 | 
				
			||||||
 | 
					    obj[key] = val
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    defineReactiveSimple(obj, key, val)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function defineReactiveSimple(obj: any, key: string, val: any) {
 | 
				
			||||||
 | 
					  val = isObject(val) ? reactive(val) : val
 | 
				
			||||||
 | 
					  Object.defineProperty(obj, key, {
 | 
				
			||||||
 | 
					    enumerable: true,
 | 
				
			||||||
 | 
					    configurable: true,
 | 
				
			||||||
 | 
					    get() {
 | 
				
			||||||
 | 
					      track(obj, TrackOpTypes.GET, key)
 | 
				
			||||||
 | 
					      return val
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    set(newVal) {
 | 
				
			||||||
 | 
					      val = isObject(newVal) ? reactive(newVal) : newVal
 | 
				
			||||||
 | 
					      trigger(obj, TriggerOpTypes.SET, key, newVal)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,8 @@
 | 
				
			|||||||
import { isArray, isString } from '@vue/shared'
 | 
					import { extend, isArray, isString } from '@vue/shared'
 | 
				
			||||||
import { AppConfig } from '../apiCreateApp'
 | 
					import { AppConfig } from '../apiCreateApp'
 | 
				
			||||||
import { isRuntimeOnly } from '../component'
 | 
					import { isRuntimeOnly } from '../component'
 | 
				
			||||||
import { isCompatEnabled } from './compatConfig'
 | 
					import { isCompatEnabled } from './compatConfig'
 | 
				
			||||||
 | 
					import { deepMergeData } from './data'
 | 
				
			||||||
import { DeprecationTypes, warnDeprecation } from './deprecations'
 | 
					import { DeprecationTypes, warnDeprecation } from './deprecations'
 | 
				
			||||||
import { isCopyingConfig } from './global'
 | 
					import { isCopyingConfig } from './global'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -34,7 +35,7 @@ export type LegacyConfig = {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// dev only
 | 
					// dev only
 | 
				
			||||||
export function installLegacyConfigTraps(config: AppConfig) {
 | 
					export function installLegacyConfigProperties(config: AppConfig) {
 | 
				
			||||||
  const legacyConfigOptions: Record<string, DeprecationTypes> = {
 | 
					  const legacyConfigOptions: Record<string, DeprecationTypes> = {
 | 
				
			||||||
    silent: DeprecationTypes.CONFIG_SILENT,
 | 
					    silent: DeprecationTypes.CONFIG_SILENT,
 | 
				
			||||||
    devtools: DeprecationTypes.CONFIG_DEVTOOLS,
 | 
					    devtools: DeprecationTypes.CONFIG_DEVTOOLS,
 | 
				
			||||||
@ -72,4 +73,44 @@ export function installLegacyConfigTraps(config: AppConfig) {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Internal merge strats which are no longer needed in v3, but we need to
 | 
				
			||||||
 | 
					  // expose them because some v2 plugins will reuse these internal strats to
 | 
				
			||||||
 | 
					  // merge their custom options.
 | 
				
			||||||
 | 
					  const strats = config.optionMergeStrategies as any
 | 
				
			||||||
 | 
					  strats.data = deepMergeData
 | 
				
			||||||
 | 
					  // lifecycle hooks
 | 
				
			||||||
 | 
					  strats.beforeCreate = mergeHook
 | 
				
			||||||
 | 
					  strats.created = mergeHook
 | 
				
			||||||
 | 
					  strats.beforeMount = mergeHook
 | 
				
			||||||
 | 
					  strats.mounted = mergeHook
 | 
				
			||||||
 | 
					  strats.beforeUpdate = mergeHook
 | 
				
			||||||
 | 
					  strats.updated = mergeHook
 | 
				
			||||||
 | 
					  strats.beforeDestroy = mergeHook
 | 
				
			||||||
 | 
					  strats.destroyed = mergeHook
 | 
				
			||||||
 | 
					  strats.activated = mergeHook
 | 
				
			||||||
 | 
					  strats.deactivated = mergeHook
 | 
				
			||||||
 | 
					  strats.errorCaptured = mergeHook
 | 
				
			||||||
 | 
					  strats.serverPrefetch = mergeHook
 | 
				
			||||||
 | 
					  // assets
 | 
				
			||||||
 | 
					  strats.components = mergeObjectOptions
 | 
				
			||||||
 | 
					  strats.directives = mergeObjectOptions
 | 
				
			||||||
 | 
					  strats.filters = mergeObjectOptions
 | 
				
			||||||
 | 
					  // objects
 | 
				
			||||||
 | 
					  strats.props = mergeObjectOptions
 | 
				
			||||||
 | 
					  strats.methods = mergeObjectOptions
 | 
				
			||||||
 | 
					  strats.inject = mergeObjectOptions
 | 
				
			||||||
 | 
					  strats.computed = mergeObjectOptions
 | 
				
			||||||
 | 
					  // watch has special merge behavior in v2, but isn't actually needed in v3.
 | 
				
			||||||
 | 
					  // since we are only exposing these for compat and nobody should be relying
 | 
				
			||||||
 | 
					  // on the watch-specific behavior, just expose the object merge strat.
 | 
				
			||||||
 | 
					  strats.watch = mergeObjectOptions
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function mergeHook(to: Function[] | undefined, from: Function | Function[]) {
 | 
				
			||||||
 | 
					  return Array.from(new Set([...(to || []), from]))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function mergeObjectOptions(to: Object | undefined, from: Object | undefined) {
 | 
				
			||||||
 | 
					  return to ? extend(extend(Object.create(null), to), from) : from
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -991,8 +991,12 @@ export function resolveMergedOptions(
 | 
				
			|||||||
  return (raw.__merged = options)
 | 
					  return (raw.__merged = options)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function mergeOptions(to: any, from: any, instance: ComponentInternalInstance) {
 | 
					export function mergeOptions(
 | 
				
			||||||
  const strats = instance.appContext.config.optionMergeStrategies
 | 
					  to: any,
 | 
				
			||||||
 | 
					  from: any,
 | 
				
			||||||
 | 
					  instance?: ComponentInternalInstance
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
					  const strats = instance && instance.appContext.config.optionMergeStrategies
 | 
				
			||||||
  const { mixins, extends: extendsOptions } = from
 | 
					  const { mixins, extends: extendsOptions } = from
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  extendsOptions && mergeOptions(to, extendsOptions, instance)
 | 
					  extendsOptions && mergeOptions(to, extendsOptions, instance)
 | 
				
			||||||
@ -1000,8 +1004,8 @@ function mergeOptions(to: any, from: any, instance: ComponentInternalInstance) {
 | 
				
			|||||||
    mixins.forEach((m: ComponentOptionsMixin) => mergeOptions(to, m, instance))
 | 
					    mixins.forEach((m: ComponentOptionsMixin) => mergeOptions(to, m, instance))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  for (const key in from) {
 | 
					  for (const key in from) {
 | 
				
			||||||
    if (strats && hasOwn(strats, key)) {
 | 
					    if (strats && hasOwn(to, key) && hasOwn(strats, key)) {
 | 
				
			||||||
      to[key] = strats[key](to[key], from[key], instance.proxy, key)
 | 
					      to[key] = strats[key](to[key], from[key], instance && instance.proxy, key)
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      to[key] = from[key]
 | 
					      to[key] = from[key]
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -497,20 +497,6 @@ export function createRenderContext(instance: ComponentInternalInstance) {
 | 
				
			|||||||
    })
 | 
					    })
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // expose global properties
 | 
					 | 
				
			||||||
  const { globalProperties } = instance.appContext.config
 | 
					 | 
				
			||||||
  Object.keys(globalProperties).forEach(key => {
 | 
					 | 
				
			||||||
    Object.defineProperty(target, key, {
 | 
					 | 
				
			||||||
      configurable: true,
 | 
					 | 
				
			||||||
      enumerable: false,
 | 
					 | 
				
			||||||
      get: () => {
 | 
					 | 
				
			||||||
        const val = globalProperties[key]
 | 
					 | 
				
			||||||
        return __COMPAT__ && isFunction(val) ? val.bind(instance.proxy) : val
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      set: NOOP
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
  })
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return target as ComponentRenderContext
 | 
					  return target as ComponentRenderContext
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user