wip: tests for global api compat
This commit is contained in:
parent
86703c23a6
commit
1d1af403ca
@ -1,21 +1,335 @@
|
||||
import Vue from '@vue/compat'
|
||||
import { effect, isReactive } from '@vue/reactivity'
|
||||
import {
|
||||
DeprecationTypes,
|
||||
deprecationData,
|
||||
toggleDeprecationWarning
|
||||
} from '../compatConfig'
|
||||
|
||||
describe('compat: global API', () => {
|
||||
beforeEach(() => Vue.configureCompat({ MODE: 2 }))
|
||||
afterEach(() => Vue.configureCompat({ MODE: 3 }))
|
||||
beforeEach(() => {
|
||||
Vue.configureCompat({ MODE: 2 })
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
Vue.configureCompat({ MODE: 3 })
|
||||
toggleDeprecationWarning(false)
|
||||
})
|
||||
|
||||
describe('GLOBAL_MOUNT', () => {
|
||||
test('new Vue() with el', () => {
|
||||
toggleDeprecationWarning(true)
|
||||
|
||||
test('should work', () => {
|
||||
const el = document.createElement('div')
|
||||
el.innerHTML = `{{ msg }}`
|
||||
new Vue({
|
||||
el,
|
||||
compatConfig: { GLOBAL_MOUNT: true },
|
||||
data() {
|
||||
return {
|
||||
msg: 'hello'
|
||||
}
|
||||
}
|
||||
})
|
||||
expect('global app bootstrapping API has changed').toHaveBeenWarned()
|
||||
expect(
|
||||
deprecationData[DeprecationTypes.GLOBAL_MOUNT].message
|
||||
).toHaveBeenWarned()
|
||||
expect(el.innerHTML).toBe('hello')
|
||||
})
|
||||
|
||||
test('new Vue() + $mount', () => {
|
||||
const el = document.createElement('div')
|
||||
el.innerHTML = `{{ msg }}`
|
||||
new Vue({
|
||||
data() {
|
||||
return {
|
||||
msg: 'hello'
|
||||
}
|
||||
}
|
||||
}).$mount(el)
|
||||
expect(el.innerHTML).toBe('hello')
|
||||
})
|
||||
})
|
||||
|
||||
describe('GLOBAL_MOUNT_CONTAINER', () => {
|
||||
test('should warn', () => {
|
||||
toggleDeprecationWarning(true)
|
||||
|
||||
const el = document.createElement('div')
|
||||
el.innerHTML = `test`
|
||||
el.setAttribute('v-bind:id', 'foo')
|
||||
new Vue().$mount(el)
|
||||
// warning only
|
||||
expect(
|
||||
deprecationData[DeprecationTypes.GLOBAL_MOUNT].message
|
||||
).toHaveBeenWarned()
|
||||
expect(
|
||||
deprecationData[DeprecationTypes.GLOBAL_MOUNT_CONTAINER].message
|
||||
).toHaveBeenWarned()
|
||||
})
|
||||
})
|
||||
|
||||
describe('GLOBAL_EXTEND', () => {
|
||||
// https://github.com/vuejs/vue/blob/dev/test/unit/features/global-api/extend.spec.js
|
||||
it('should correctly merge options', () => {
|
||||
toggleDeprecationWarning(true)
|
||||
|
||||
const Test = Vue.extend({
|
||||
name: 'test',
|
||||
a: 1,
|
||||
b: 2
|
||||
})
|
||||
expect(Test.options.a).toBe(1)
|
||||
expect(Test.options.b).toBe(2)
|
||||
expect(Test.super).toBe(Vue)
|
||||
const t = new Test({
|
||||
a: 2
|
||||
})
|
||||
expect(t.$options.a).toBe(2)
|
||||
expect(t.$options.b).toBe(2)
|
||||
// inheritance
|
||||
const Test2 = Test.extend({
|
||||
a: 2
|
||||
})
|
||||
expect(Test2.options.a).toBe(2)
|
||||
expect(Test2.options.b).toBe(2)
|
||||
const t2 = new Test2({
|
||||
a: 3
|
||||
})
|
||||
expect(t2.$options.a).toBe(3)
|
||||
expect(t2.$options.b).toBe(2)
|
||||
|
||||
expect(
|
||||
deprecationData[DeprecationTypes.GLOBAL_MOUNT].message
|
||||
).toHaveBeenWarned()
|
||||
expect(
|
||||
deprecationData[DeprecationTypes.GLOBAL_EXTEND].message
|
||||
).toHaveBeenWarned()
|
||||
})
|
||||
|
||||
it('should work when used as components', () => {
|
||||
const foo = Vue.extend({
|
||||
template: '<span>foo</span>'
|
||||
})
|
||||
const bar = Vue.extend({
|
||||
template: '<span>bar</span>'
|
||||
})
|
||||
const vm = new Vue({
|
||||
template: '<div><foo></foo><bar></bar></div>',
|
||||
components: { foo, bar }
|
||||
}).$mount()
|
||||
expect(vm.$el.innerHTML).toBe('<span>foo</span><span>bar</span>')
|
||||
})
|
||||
|
||||
it('should merge lifecycle hooks', () => {
|
||||
const calls: number[] = []
|
||||
const A = Vue.extend({
|
||||
created() {
|
||||
calls.push(1)
|
||||
}
|
||||
})
|
||||
const B = A.extend({
|
||||
created() {
|
||||
calls.push(2)
|
||||
}
|
||||
})
|
||||
new B({
|
||||
created() {
|
||||
calls.push(3)
|
||||
}
|
||||
})
|
||||
expect(calls).toEqual([1, 2, 3])
|
||||
})
|
||||
|
||||
it('should not merge nested mixins created with Vue.extend', () => {
|
||||
const A = Vue.extend({
|
||||
created: () => {}
|
||||
})
|
||||
const B = Vue.extend({
|
||||
mixins: [A],
|
||||
created: () => {}
|
||||
})
|
||||
const C = Vue.extend({
|
||||
extends: B,
|
||||
created: () => {}
|
||||
})
|
||||
const D = Vue.extend({
|
||||
mixins: [C],
|
||||
created: () => {}
|
||||
})
|
||||
expect(D.options.created!.length).toBe(4)
|
||||
})
|
||||
|
||||
it('should merge methods', () => {
|
||||
const A = Vue.extend({
|
||||
methods: {
|
||||
a() {
|
||||
return this.n
|
||||
}
|
||||
}
|
||||
})
|
||||
const B = A.extend({
|
||||
methods: {
|
||||
b() {
|
||||
return this.n + 1
|
||||
}
|
||||
}
|
||||
})
|
||||
const b = new B({
|
||||
data: () => ({ n: 0 }),
|
||||
methods: {
|
||||
c() {
|
||||
return this.n + 2
|
||||
}
|
||||
}
|
||||
}) as any
|
||||
expect(b.a()).toBe(0)
|
||||
expect(b.b()).toBe(1)
|
||||
expect(b.c()).toBe(2)
|
||||
})
|
||||
|
||||
it('should merge assets', () => {
|
||||
const A = Vue.extend({
|
||||
components: {
|
||||
aa: {
|
||||
template: '<div>A</div>'
|
||||
}
|
||||
}
|
||||
})
|
||||
const B = A.extend({
|
||||
components: {
|
||||
bb: {
|
||||
template: '<div>B</div>'
|
||||
}
|
||||
}
|
||||
})
|
||||
const b = new B({
|
||||
template: '<div><aa></aa><bb></bb></div>'
|
||||
}).$mount()
|
||||
expect(b.$el.innerHTML).toBe('<div>A</div><div>B</div>')
|
||||
})
|
||||
|
||||
it('caching', () => {
|
||||
const options = {
|
||||
template: '<div></div>'
|
||||
}
|
||||
const A = Vue.extend(options)
|
||||
const B = Vue.extend(options)
|
||||
expect(A).toBe(B)
|
||||
})
|
||||
|
||||
it('extended options should use different identify from parent', () => {
|
||||
const A = Vue.extend({ computed: {} })
|
||||
const B = A.extend()
|
||||
B.options.computed.b = () => 'foo'
|
||||
expect(B.options.computed).not.toBe(A.options.computed)
|
||||
expect(A.options.computed.b).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
||||
describe('GLOBAL_PROTOTYPE', () => {
|
||||
test('plain properties', () => {
|
||||
toggleDeprecationWarning(true)
|
||||
Vue.prototype.$test = 1
|
||||
const vm = new Vue() as any
|
||||
expect(vm.$test).toBe(1)
|
||||
delete Vue.prototype.$test
|
||||
expect(
|
||||
deprecationData[DeprecationTypes.GLOBAL_MOUNT].message
|
||||
).toHaveBeenWarned()
|
||||
expect(
|
||||
deprecationData[DeprecationTypes.GLOBAL_PROTOTYPE].message
|
||||
).toHaveBeenWarned()
|
||||
})
|
||||
|
||||
test('method this context', () => {
|
||||
Vue.prototype.$test = function() {
|
||||
return this.msg
|
||||
}
|
||||
const vm = new Vue({
|
||||
data() {
|
||||
return { msg: 'method' }
|
||||
}
|
||||
}) as any
|
||||
expect(vm.$test()).toBe('method')
|
||||
delete Vue.prototype.$test
|
||||
})
|
||||
|
||||
test('defined properties', () => {
|
||||
Object.defineProperty(Vue.prototype, '$test', {
|
||||
configurable: true,
|
||||
get() {
|
||||
return this.msg
|
||||
}
|
||||
})
|
||||
const vm = new Vue({
|
||||
data() {
|
||||
return { msg: 'getter' }
|
||||
}
|
||||
}) as any
|
||||
expect(vm.$test).toBe('getter')
|
||||
delete Vue.prototype.$test
|
||||
})
|
||||
|
||||
test('extended prototype', async () => {
|
||||
const Foo = Vue.extend()
|
||||
Foo.prototype.$test = 1
|
||||
const vm = new Foo() as any
|
||||
expect(vm.$test).toBe(1)
|
||||
const plain = new Vue() as any
|
||||
expect(plain.$test).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
||||
describe('GLOBAL_SET/DELETE', () => {
|
||||
test('set', () => {
|
||||
toggleDeprecationWarning(true)
|
||||
const obj: any = {}
|
||||
Vue.set(obj, 'foo', 1)
|
||||
expect(obj.foo).toBe(1)
|
||||
expect(
|
||||
deprecationData[DeprecationTypes.GLOBAL_SET].message
|
||||
).toHaveBeenWarned()
|
||||
})
|
||||
|
||||
test('delete', () => {
|
||||
toggleDeprecationWarning(true)
|
||||
const obj: any = { foo: 1 }
|
||||
Vue.delete(obj, 'foo')
|
||||
expect('foo' in obj).toBe(false)
|
||||
expect(
|
||||
deprecationData[DeprecationTypes.GLOBAL_DELETE].message
|
||||
).toHaveBeenWarned()
|
||||
})
|
||||
})
|
||||
|
||||
describe('GLOBAL_OBSERVABLE', () => {
|
||||
test('should work', () => {
|
||||
toggleDeprecationWarning(true)
|
||||
const obj = Vue.observable({})
|
||||
expect(isReactive(obj)).toBe(true)
|
||||
expect(
|
||||
deprecationData[DeprecationTypes.GLOBAL_OBSERVABLE].message
|
||||
).toHaveBeenWarned()
|
||||
})
|
||||
})
|
||||
|
||||
describe('GLOBAL_PRIVATE_UTIL', () => {
|
||||
test('defineReactive', () => {
|
||||
toggleDeprecationWarning(true)
|
||||
const obj: any = {}
|
||||
// @ts-ignore
|
||||
Vue.util.defineReactive(obj, 'test', 1)
|
||||
|
||||
let n
|
||||
effect(() => {
|
||||
n = obj.test
|
||||
})
|
||||
expect(n).toBe(1)
|
||||
obj.test++
|
||||
expect(n).toBe(2)
|
||||
|
||||
expect(
|
||||
deprecationData[DeprecationTypes.GLOBAL_PRIVATE_UTIL].message
|
||||
).toHaveBeenWarned()
|
||||
})
|
||||
})
|
||||
|
@ -17,7 +17,7 @@ export const enum DeprecationTypes {
|
||||
GLOBAL_SET = 'GLOBAL_SET',
|
||||
GLOBAL_DELETE = 'GLOBAL_DELETE',
|
||||
GLOBAL_OBSERVABLE = 'GLOBAL_OBSERVABLE',
|
||||
GLOBAL_UTIL = 'GLOBAL_UTIL',
|
||||
GLOBAL_PRIVATE_UTIL = 'GLOBAL_PRIVATE_UTIL',
|
||||
|
||||
CONFIG_SILENT = 'CONFIG_SILENT',
|
||||
CONFIG_DEVTOOLS = 'CONFIG_DEVTOOLS',
|
||||
@ -70,7 +70,7 @@ type DeprecationData = {
|
||||
link?: string
|
||||
}
|
||||
|
||||
const deprecationData: Record<DeprecationTypes, DeprecationData> = {
|
||||
export const deprecationData: Record<DeprecationTypes, DeprecationData> = {
|
||||
[DeprecationTypes.GLOBAL_MOUNT]: {
|
||||
message:
|
||||
`The global app bootstrapping API has changed: vm.$mount() and the "el" ` +
|
||||
@ -119,7 +119,7 @@ const deprecationData: Record<DeprecationTypes, DeprecationData> = {
|
||||
link: `https://v3.vuejs.org/api/basic-reactivity.html`
|
||||
},
|
||||
|
||||
[DeprecationTypes.GLOBAL_UTIL]: {
|
||||
[DeprecationTypes.GLOBAL_PRIVATE_UTIL]: {
|
||||
message:
|
||||
`Vue.util has been removed. Please refactor to avoid its usage ` +
|
||||
`since it was an internal API even in Vue 2.`
|
||||
@ -437,6 +437,13 @@ const deprecationData: Record<DeprecationTypes, DeprecationData> = {
|
||||
const instanceWarned: Record<string, true> = Object.create(null)
|
||||
const warnCount: Record<string, number> = Object.create(null)
|
||||
|
||||
// test only
|
||||
let warningEnabled = true
|
||||
|
||||
export function toggleDeprecationWarning(flag: boolean) {
|
||||
warningEnabled = flag
|
||||
}
|
||||
|
||||
export function warnDeprecation(
|
||||
key: DeprecationTypes,
|
||||
instance: ComponentInternalInstance | null,
|
||||
@ -445,6 +452,9 @@ export function warnDeprecation(
|
||||
if (!__DEV__) {
|
||||
return
|
||||
}
|
||||
if (__TEST__ && !warningEnabled) {
|
||||
return
|
||||
}
|
||||
|
||||
instance = instance || getCurrentInstance()
|
||||
|
||||
@ -463,14 +473,14 @@ export function warnDeprecation(
|
||||
|
||||
// skip if the same warning is emitted for the same component type
|
||||
const componentDupKey = dupKey + compId
|
||||
if (componentDupKey in instanceWarned) {
|
||||
if (!__TEST__ && componentDupKey in instanceWarned) {
|
||||
return
|
||||
}
|
||||
instanceWarned[componentDupKey] = true
|
||||
|
||||
// same warning, but different component. skip the long message and just
|
||||
// log the key and count.
|
||||
if (dupKey in warnCount) {
|
||||
if (!__TEST__ && dupKey in warnCount) {
|
||||
warn(`(deprecation ${key}) (${++warnCount[dupKey] + 1})`)
|
||||
return
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { isPlainObject } from '@vue/shared'
|
||||
import { isFunction, isPlainObject } from '@vue/shared'
|
||||
import { ComponentInternalInstance } from '../component'
|
||||
import { ComponentPublicInstance } from '../componentPublicInstance'
|
||||
import { DeprecationTypes, warnDeprecation } from './compatConfig'
|
||||
|
||||
export function deepMergeData(
|
||||
@ -19,3 +20,19 @@ export function deepMergeData(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function mergeDataOption(to: any, from: any) {
|
||||
if (!from) {
|
||||
return to
|
||||
}
|
||||
if (!to) {
|
||||
return from
|
||||
}
|
||||
return function mergedDataFn(this: ComponentPublicInstance) {
|
||||
return deepMergeData(
|
||||
isFunction(to) ? to.call(this, this) : to,
|
||||
isFunction(from) ? from.call(this, this) : from,
|
||||
this.$
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,6 @@ import {
|
||||
CreateAppFunction,
|
||||
Plugin
|
||||
} from '../apiCreateApp'
|
||||
import { defineComponent } from '../apiDefineComponent'
|
||||
import {
|
||||
Component,
|
||||
ComponentOptions,
|
||||
@ -40,7 +39,7 @@ import { devtoolsInitApp } from '../devtools'
|
||||
import { Directive } from '../directives'
|
||||
import { nextTick } from '../scheduler'
|
||||
import { version } from '..'
|
||||
import { LegacyConfig } from './globalConfig'
|
||||
import { LegacyConfig, legacyOptionMergeStrats } from './globalConfig'
|
||||
import { LegacyDirective } from './customDirective'
|
||||
import {
|
||||
warnDeprecation,
|
||||
@ -50,6 +49,7 @@ import {
|
||||
isCompatEnabled,
|
||||
softAssertCompatEnabled
|
||||
} from './compatConfig'
|
||||
import { LegacyPublicInstance } from './instance'
|
||||
|
||||
/**
|
||||
* @deprecated the default `Vue` export has been removed in Vue 3. The type for
|
||||
@ -57,15 +57,17 @@ import {
|
||||
* named imports instead - e.g. `import { createApp } from 'vue'`.
|
||||
*/
|
||||
export type CompatVue = Pick<App, 'version' | 'component' | 'directive'> & {
|
||||
configureCompat: typeof configureCompat
|
||||
|
||||
// no inference here since these types are not meant for actual use - they
|
||||
// are merely here to provide type checks for internal implementation and
|
||||
// information for migration.
|
||||
new (options?: ComponentOptions): ComponentPublicInstance
|
||||
new (options?: ComponentOptions): LegacyPublicInstance
|
||||
|
||||
version: string
|
||||
config: AppConfig & LegacyConfig
|
||||
|
||||
extend: typeof defineComponent
|
||||
extend: (options?: ComponentOptions) => CompatVue
|
||||
nextTick: typeof nextTick
|
||||
|
||||
use(plugin: Plugin, ...options: any[]): CompatVue
|
||||
@ -102,8 +104,10 @@ export type CompatVue = Pick<App, 'version' | 'component' | 'directive'> & {
|
||||
* @internal
|
||||
*/
|
||||
options: ComponentOptions
|
||||
|
||||
configureCompat: typeof configureCompat
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
super: CompatVue
|
||||
}
|
||||
|
||||
export let isCopyingConfig = false
|
||||
@ -184,32 +188,55 @@ export function createCompatVue(
|
||||
let cid = 1
|
||||
Vue.cid = cid
|
||||
|
||||
const extendCache = new WeakMap()
|
||||
|
||||
function extendCtor(this: any, extendOptions: ComponentOptions = {}) {
|
||||
assertCompatEnabled(DeprecationTypes.GLOBAL_EXTEND, null)
|
||||
if (isFunction(extendOptions)) {
|
||||
extendOptions = extendOptions.options
|
||||
}
|
||||
|
||||
if (extendCache.has(extendOptions)) {
|
||||
return extendCache.get(extendOptions)
|
||||
}
|
||||
|
||||
const Super = this
|
||||
function SubVue(inlineOptions?: ComponentOptions) {
|
||||
if (!inlineOptions) {
|
||||
return createCompatApp(extendOptions, SubVue)
|
||||
return createCompatApp(SubVue.options, SubVue)
|
||||
} else {
|
||||
return createCompatApp(
|
||||
{
|
||||
el: inlineOptions.el,
|
||||
extends: extendOptions,
|
||||
mixins: [inlineOptions]
|
||||
},
|
||||
mergeOptions(
|
||||
extend({}, SubVue.options),
|
||||
inlineOptions,
|
||||
null,
|
||||
legacyOptionMergeStrats as any
|
||||
),
|
||||
SubVue
|
||||
)
|
||||
}
|
||||
}
|
||||
SubVue.super = Super
|
||||
SubVue.prototype = Object.create(Vue.prototype)
|
||||
SubVue.prototype.constructor = SubVue
|
||||
|
||||
// clone non-primitive base option values for edge case of mutating
|
||||
// extended options
|
||||
const mergeBase: any = {}
|
||||
for (const key in Super.options) {
|
||||
const superValue = Super.options[key]
|
||||
mergeBase[key] = isArray(superValue)
|
||||
? superValue.slice()
|
||||
: isObject(superValue)
|
||||
? extend(Object.create(null), superValue)
|
||||
: superValue
|
||||
}
|
||||
|
||||
SubVue.options = mergeOptions(
|
||||
extend({}, Super.options) as ComponentOptions,
|
||||
extendOptions
|
||||
mergeBase,
|
||||
extendOptions,
|
||||
null,
|
||||
legacyOptionMergeStrats as any
|
||||
)
|
||||
|
||||
SubVue.options._base = SubVue
|
||||
@ -217,6 +244,8 @@ export function createCompatVue(
|
||||
SubVue.mixin = Super.mixin
|
||||
SubVue.use = Super.use
|
||||
SubVue.cid = ++cid
|
||||
|
||||
extendCache.set(extendOptions, SubVue)
|
||||
return SubVue
|
||||
}
|
||||
|
||||
@ -279,12 +308,17 @@ export function createCompatVue(
|
||||
warn: __DEV__ ? warn : NOOP,
|
||||
extend,
|
||||
mergeOptions: (parent: any, child: any, vm?: ComponentPublicInstance) =>
|
||||
mergeOptions(parent, child, vm && vm.$),
|
||||
mergeOptions(
|
||||
parent,
|
||||
child,
|
||||
vm && vm.$,
|
||||
vm ? undefined : (legacyOptionMergeStrats as any)
|
||||
),
|
||||
defineReactive
|
||||
}
|
||||
Object.defineProperty(Vue, 'util', {
|
||||
get() {
|
||||
assertCompatEnabled(DeprecationTypes.GLOBAL_UTIL, null)
|
||||
assertCompatEnabled(DeprecationTypes.GLOBAL_PRIVATE_UTIL, null)
|
||||
return util
|
||||
}
|
||||
})
|
||||
@ -332,7 +366,7 @@ export function installCompatMount(
|
||||
// Note: the following assumes DOM environment since the compat build
|
||||
// only targets web. It essentially includes logic for app.mount from
|
||||
// both runtime-core AND runtime-dom.
|
||||
instance.ctx._compat_mount = (selectorOrEl: string | Element) => {
|
||||
instance.ctx._compat_mount = (selectorOrEl?: string | Element) => {
|
||||
if (isMounted) {
|
||||
__DEV__ && warn(`Root instance is already mounted.`)
|
||||
return
|
||||
@ -351,14 +385,8 @@ export function installCompatMount(
|
||||
}
|
||||
container = result
|
||||
} else {
|
||||
if (!selectorOrEl) {
|
||||
__DEV__ &&
|
||||
warn(
|
||||
`Failed to mount root instance: invalid mount target ${selectorOrEl}.`
|
||||
)
|
||||
return
|
||||
}
|
||||
container = selectorOrEl
|
||||
// eslint-disable-next-line
|
||||
container = selectorOrEl || document.createElement('div')
|
||||
}
|
||||
|
||||
const isSVG = container instanceof SVGElement
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { extend, isArray, isString } from '@vue/shared'
|
||||
import { AppConfig } from '../apiCreateApp'
|
||||
import { isRuntimeOnly } from '../component'
|
||||
import { deepMergeData } from './data'
|
||||
import { mergeDataOption } from './data'
|
||||
import {
|
||||
DeprecationTypes,
|
||||
warnDeprecation,
|
||||
@ -80,34 +80,36 @@ export function installLegacyConfigProperties(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
|
||||
extend(config.optionMergeStrategies, legacyOptionMergeStrats)
|
||||
}
|
||||
|
||||
export const legacyOptionMergeStrats = {
|
||||
data: mergeDataOption,
|
||||
beforeCreate: mergeHook,
|
||||
created: mergeHook,
|
||||
beforeMount: mergeHook,
|
||||
mounted: mergeHook,
|
||||
beforeUpdate: mergeHook,
|
||||
updated: mergeHook,
|
||||
beforeDestroy: mergeHook,
|
||||
destroyed: mergeHook,
|
||||
activated: mergeHook,
|
||||
deactivated: mergeHook,
|
||||
errorCaptured: mergeHook,
|
||||
serverPrefetch: mergeHook,
|
||||
// assets
|
||||
strats.components = mergeObjectOptions
|
||||
strats.directives = mergeObjectOptions
|
||||
strats.filters = mergeObjectOptions
|
||||
components: mergeObjectOptions,
|
||||
directives: mergeObjectOptions,
|
||||
filters: mergeObjectOptions,
|
||||
// objects
|
||||
strats.props = mergeObjectOptions
|
||||
strats.methods = mergeObjectOptions
|
||||
strats.inject = mergeObjectOptions
|
||||
strats.computed = mergeObjectOptions
|
||||
props: mergeObjectOptions,
|
||||
methods: mergeObjectOptions,
|
||||
inject: mergeObjectOptions,
|
||||
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
|
||||
watch: mergeObjectOptions
|
||||
}
|
||||
|
||||
function mergeHook(
|
||||
|
@ -6,7 +6,10 @@ import {
|
||||
toDisplayString,
|
||||
toNumber
|
||||
} from '@vue/shared'
|
||||
import { PublicPropertiesMap } from '../componentPublicInstance'
|
||||
import {
|
||||
ComponentPublicInstance,
|
||||
PublicPropertiesMap
|
||||
} from '../componentPublicInstance'
|
||||
import { getCompatChildren } from './instanceChildren'
|
||||
import {
|
||||
DeprecationTypes,
|
||||
@ -33,6 +36,23 @@ import {
|
||||
} from './renderHelpers'
|
||||
import { resolveFilter } from '../helpers/resolveAssets'
|
||||
import { resolveMergedOptions } from '../componentOptions'
|
||||
import { Slots } from '../componentSlots'
|
||||
|
||||
export type LegacyPublicInstance = ComponentPublicInstance &
|
||||
LegacyPublicProperties
|
||||
|
||||
export interface LegacyPublicProperties {
|
||||
$set(target: object, key: string, value: any): void
|
||||
$delete(target: object, key: string): void
|
||||
$mount(el?: string | Element): this
|
||||
$destroy(): void
|
||||
$scopedSlots: Slots
|
||||
$on(event: string | string[], fn: Function): this
|
||||
$once(event: string, fn: Function): this
|
||||
$off(event?: string, fn?: Function): this
|
||||
$children: LegacyPublicProperties[]
|
||||
$listeners: Record<string, Function | Function[]>
|
||||
}
|
||||
|
||||
export function installCompatInstanceProperties(map: PublicPropertiesMap) {
|
||||
const set = (target: any, key: any, val: any) => {
|
||||
|
@ -893,7 +893,13 @@ function callHookWithMixinAndExtends(
|
||||
}
|
||||
}
|
||||
if (selfHook) {
|
||||
callWithAsyncErrorHandling(selfHook.bind(instance.proxy!), instance, type)
|
||||
callWithAsyncErrorHandling(
|
||||
__COMPAT__ && isArray(selfHook)
|
||||
? selfHook.map(h => h.bind(instance.proxy!))
|
||||
: selfHook.bind(instance.proxy!),
|
||||
instance,
|
||||
type
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1007,21 +1013,23 @@ export function resolveMergedOptions(
|
||||
export function mergeOptions(
|
||||
to: any,
|
||||
from: any,
|
||||
instance?: ComponentInternalInstance
|
||||
instance?: ComponentInternalInstance | null,
|
||||
strats = instance && instance.appContext.config.optionMergeStrategies
|
||||
) {
|
||||
if (__COMPAT__ && isFunction(from)) {
|
||||
from = from.options
|
||||
}
|
||||
|
||||
const strats = instance && instance.appContext.config.optionMergeStrategies
|
||||
const { mixins, extends: extendsOptions } = from
|
||||
|
||||
extendsOptions && mergeOptions(to, extendsOptions, instance)
|
||||
extendsOptions && mergeOptions(to, extendsOptions, instance, strats)
|
||||
mixins &&
|
||||
mixins.forEach((m: ComponentOptionsMixin) => mergeOptions(to, m, instance))
|
||||
mixins.forEach((m: ComponentOptionsMixin) =>
|
||||
mergeOptions(to, m, instance, strats)
|
||||
)
|
||||
|
||||
for (const key in from) {
|
||||
if (strats && hasOwn(to, key) && hasOwn(strats, key)) {
|
||||
if (strats && hasOwn(strats, key)) {
|
||||
to[key] = strats[key](to[key], from[key], instance && instance.proxy, key)
|
||||
} else {
|
||||
to[key] = from[key]
|
||||
|
Loading…
Reference in New Issue
Block a user