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 Vue from '@vue/compat'
|
||||||
|
import { effect, isReactive } from '@vue/reactivity'
|
||||||
|
import {
|
||||||
|
DeprecationTypes,
|
||||||
|
deprecationData,
|
||||||
|
toggleDeprecationWarning
|
||||||
|
} from '../compatConfig'
|
||||||
|
|
||||||
describe('compat: global API', () => {
|
beforeEach(() => {
|
||||||
beforeEach(() => Vue.configureCompat({ MODE: 2 }))
|
Vue.configureCompat({ MODE: 2 })
|
||||||
afterEach(() => Vue.configureCompat({ MODE: 3 }))
|
})
|
||||||
|
|
||||||
|
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')
|
const el = document.createElement('div')
|
||||||
el.innerHTML = `{{ msg }}`
|
el.innerHTML = `{{ msg }}`
|
||||||
new Vue({
|
new Vue({
|
||||||
el,
|
el,
|
||||||
|
compatConfig: { GLOBAL_MOUNT: true },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
msg: 'hello'
|
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')
|
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_SET = 'GLOBAL_SET',
|
||||||
GLOBAL_DELETE = 'GLOBAL_DELETE',
|
GLOBAL_DELETE = 'GLOBAL_DELETE',
|
||||||
GLOBAL_OBSERVABLE = 'GLOBAL_OBSERVABLE',
|
GLOBAL_OBSERVABLE = 'GLOBAL_OBSERVABLE',
|
||||||
GLOBAL_UTIL = 'GLOBAL_UTIL',
|
GLOBAL_PRIVATE_UTIL = 'GLOBAL_PRIVATE_UTIL',
|
||||||
|
|
||||||
CONFIG_SILENT = 'CONFIG_SILENT',
|
CONFIG_SILENT = 'CONFIG_SILENT',
|
||||||
CONFIG_DEVTOOLS = 'CONFIG_DEVTOOLS',
|
CONFIG_DEVTOOLS = 'CONFIG_DEVTOOLS',
|
||||||
@ -70,7 +70,7 @@ type DeprecationData = {
|
|||||||
link?: string
|
link?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const deprecationData: Record<DeprecationTypes, DeprecationData> = {
|
export const deprecationData: Record<DeprecationTypes, DeprecationData> = {
|
||||||
[DeprecationTypes.GLOBAL_MOUNT]: {
|
[DeprecationTypes.GLOBAL_MOUNT]: {
|
||||||
message:
|
message:
|
||||||
`The global app bootstrapping API has changed: vm.$mount() and the "el" ` +
|
`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`
|
link: `https://v3.vuejs.org/api/basic-reactivity.html`
|
||||||
},
|
},
|
||||||
|
|
||||||
[DeprecationTypes.GLOBAL_UTIL]: {
|
[DeprecationTypes.GLOBAL_PRIVATE_UTIL]: {
|
||||||
message:
|
message:
|
||||||
`Vue.util has been removed. Please refactor to avoid its usage ` +
|
`Vue.util has been removed. Please refactor to avoid its usage ` +
|
||||||
`since it was an internal API even in Vue 2.`
|
`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 instanceWarned: Record<string, true> = Object.create(null)
|
||||||
const warnCount: Record<string, number> = 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(
|
export function warnDeprecation(
|
||||||
key: DeprecationTypes,
|
key: DeprecationTypes,
|
||||||
instance: ComponentInternalInstance | null,
|
instance: ComponentInternalInstance | null,
|
||||||
@ -445,6 +452,9 @@ export function warnDeprecation(
|
|||||||
if (!__DEV__) {
|
if (!__DEV__) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if (__TEST__ && !warningEnabled) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
instance = instance || getCurrentInstance()
|
instance = instance || getCurrentInstance()
|
||||||
|
|
||||||
@ -463,14 +473,14 @@ export function warnDeprecation(
|
|||||||
|
|
||||||
// skip if the same warning is emitted for the same component type
|
// skip if the same warning is emitted for the same component type
|
||||||
const componentDupKey = dupKey + compId
|
const componentDupKey = dupKey + compId
|
||||||
if (componentDupKey in instanceWarned) {
|
if (!__TEST__ && componentDupKey in instanceWarned) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
instanceWarned[componentDupKey] = true
|
instanceWarned[componentDupKey] = true
|
||||||
|
|
||||||
// same warning, but different component. skip the long message and just
|
// same warning, but different component. skip the long message and just
|
||||||
// log the key and count.
|
// log the key and count.
|
||||||
if (dupKey in warnCount) {
|
if (!__TEST__ && dupKey in warnCount) {
|
||||||
warn(`(deprecation ${key}) (${++warnCount[dupKey] + 1})`)
|
warn(`(deprecation ${key}) (${++warnCount[dupKey] + 1})`)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { isPlainObject } from '@vue/shared'
|
import { isFunction, isPlainObject } from '@vue/shared'
|
||||||
import { ComponentInternalInstance } from '../component'
|
import { ComponentInternalInstance } from '../component'
|
||||||
|
import { ComponentPublicInstance } from '../componentPublicInstance'
|
||||||
import { DeprecationTypes, warnDeprecation } from './compatConfig'
|
import { DeprecationTypes, warnDeprecation } from './compatConfig'
|
||||||
|
|
||||||
export function deepMergeData(
|
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,
|
CreateAppFunction,
|
||||||
Plugin
|
Plugin
|
||||||
} from '../apiCreateApp'
|
} from '../apiCreateApp'
|
||||||
import { defineComponent } from '../apiDefineComponent'
|
|
||||||
import {
|
import {
|
||||||
Component,
|
Component,
|
||||||
ComponentOptions,
|
ComponentOptions,
|
||||||
@ -40,7 +39,7 @@ import { devtoolsInitApp } from '../devtools'
|
|||||||
import { Directive } from '../directives'
|
import { Directive } from '../directives'
|
||||||
import { nextTick } from '../scheduler'
|
import { nextTick } from '../scheduler'
|
||||||
import { version } from '..'
|
import { version } from '..'
|
||||||
import { LegacyConfig } from './globalConfig'
|
import { LegacyConfig, legacyOptionMergeStrats } from './globalConfig'
|
||||||
import { LegacyDirective } from './customDirective'
|
import { LegacyDirective } from './customDirective'
|
||||||
import {
|
import {
|
||||||
warnDeprecation,
|
warnDeprecation,
|
||||||
@ -50,6 +49,7 @@ import {
|
|||||||
isCompatEnabled,
|
isCompatEnabled,
|
||||||
softAssertCompatEnabled
|
softAssertCompatEnabled
|
||||||
} from './compatConfig'
|
} from './compatConfig'
|
||||||
|
import { LegacyPublicInstance } from './instance'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated the default `Vue` export has been removed in Vue 3. The type for
|
* @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'`.
|
* named imports instead - e.g. `import { createApp } from 'vue'`.
|
||||||
*/
|
*/
|
||||||
export type CompatVue = Pick<App, 'version' | 'component' | 'directive'> & {
|
export type CompatVue = Pick<App, 'version' | 'component' | 'directive'> & {
|
||||||
|
configureCompat: typeof configureCompat
|
||||||
|
|
||||||
// no inference here since these types are not meant for actual use - they
|
// no inference here since these types are not meant for actual use - they
|
||||||
// are merely here to provide type checks for internal implementation and
|
// are merely here to provide type checks for internal implementation and
|
||||||
// information for migration.
|
// information for migration.
|
||||||
new (options?: ComponentOptions): ComponentPublicInstance
|
new (options?: ComponentOptions): LegacyPublicInstance
|
||||||
|
|
||||||
version: string
|
version: string
|
||||||
config: AppConfig & LegacyConfig
|
config: AppConfig & LegacyConfig
|
||||||
|
|
||||||
extend: typeof defineComponent
|
extend: (options?: ComponentOptions) => CompatVue
|
||||||
nextTick: typeof nextTick
|
nextTick: typeof nextTick
|
||||||
|
|
||||||
use(plugin: Plugin, ...options: any[]): CompatVue
|
use(plugin: Plugin, ...options: any[]): CompatVue
|
||||||
@ -102,8 +104,10 @@ export type CompatVue = Pick<App, 'version' | 'component' | 'directive'> & {
|
|||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
options: ComponentOptions
|
options: ComponentOptions
|
||||||
|
/**
|
||||||
configureCompat: typeof configureCompat
|
* @internal
|
||||||
|
*/
|
||||||
|
super: CompatVue
|
||||||
}
|
}
|
||||||
|
|
||||||
export let isCopyingConfig = false
|
export let isCopyingConfig = false
|
||||||
@ -184,32 +188,55 @@ export function createCompatVue(
|
|||||||
let cid = 1
|
let cid = 1
|
||||||
Vue.cid = cid
|
Vue.cid = cid
|
||||||
|
|
||||||
|
const extendCache = new WeakMap()
|
||||||
|
|
||||||
function extendCtor(this: any, extendOptions: ComponentOptions = {}) {
|
function extendCtor(this: any, extendOptions: ComponentOptions = {}) {
|
||||||
assertCompatEnabled(DeprecationTypes.GLOBAL_EXTEND, null)
|
assertCompatEnabled(DeprecationTypes.GLOBAL_EXTEND, null)
|
||||||
if (isFunction(extendOptions)) {
|
if (isFunction(extendOptions)) {
|
||||||
extendOptions = extendOptions.options
|
extendOptions = extendOptions.options
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (extendCache.has(extendOptions)) {
|
||||||
|
return extendCache.get(extendOptions)
|
||||||
|
}
|
||||||
|
|
||||||
const Super = this
|
const Super = this
|
||||||
function SubVue(inlineOptions?: ComponentOptions) {
|
function SubVue(inlineOptions?: ComponentOptions) {
|
||||||
if (!inlineOptions) {
|
if (!inlineOptions) {
|
||||||
return createCompatApp(extendOptions, SubVue)
|
return createCompatApp(SubVue.options, SubVue)
|
||||||
} else {
|
} else {
|
||||||
return createCompatApp(
|
return createCompatApp(
|
||||||
{
|
mergeOptions(
|
||||||
el: inlineOptions.el,
|
extend({}, SubVue.options),
|
||||||
extends: extendOptions,
|
inlineOptions,
|
||||||
mixins: [inlineOptions]
|
null,
|
||||||
},
|
legacyOptionMergeStrats as any
|
||||||
|
),
|
||||||
SubVue
|
SubVue
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
SubVue.super = Super
|
||||||
SubVue.prototype = Object.create(Vue.prototype)
|
SubVue.prototype = Object.create(Vue.prototype)
|
||||||
SubVue.prototype.constructor = SubVue
|
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(
|
SubVue.options = mergeOptions(
|
||||||
extend({}, Super.options) as ComponentOptions,
|
mergeBase,
|
||||||
extendOptions
|
extendOptions,
|
||||||
|
null,
|
||||||
|
legacyOptionMergeStrats as any
|
||||||
)
|
)
|
||||||
|
|
||||||
SubVue.options._base = SubVue
|
SubVue.options._base = SubVue
|
||||||
@ -217,6 +244,8 @@ export function createCompatVue(
|
|||||||
SubVue.mixin = Super.mixin
|
SubVue.mixin = Super.mixin
|
||||||
SubVue.use = Super.use
|
SubVue.use = Super.use
|
||||||
SubVue.cid = ++cid
|
SubVue.cid = ++cid
|
||||||
|
|
||||||
|
extendCache.set(extendOptions, SubVue)
|
||||||
return SubVue
|
return SubVue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -279,12 +308,17 @@ export function createCompatVue(
|
|||||||
warn: __DEV__ ? warn : NOOP,
|
warn: __DEV__ ? warn : NOOP,
|
||||||
extend,
|
extend,
|
||||||
mergeOptions: (parent: any, child: any, vm?: ComponentPublicInstance) =>
|
mergeOptions: (parent: any, child: any, vm?: ComponentPublicInstance) =>
|
||||||
mergeOptions(parent, child, vm && vm.$),
|
mergeOptions(
|
||||||
|
parent,
|
||||||
|
child,
|
||||||
|
vm && vm.$,
|
||||||
|
vm ? undefined : (legacyOptionMergeStrats as any)
|
||||||
|
),
|
||||||
defineReactive
|
defineReactive
|
||||||
}
|
}
|
||||||
Object.defineProperty(Vue, 'util', {
|
Object.defineProperty(Vue, 'util', {
|
||||||
get() {
|
get() {
|
||||||
assertCompatEnabled(DeprecationTypes.GLOBAL_UTIL, null)
|
assertCompatEnabled(DeprecationTypes.GLOBAL_PRIVATE_UTIL, null)
|
||||||
return util
|
return util
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -332,7 +366,7 @@ export function installCompatMount(
|
|||||||
// Note: the following assumes DOM environment since the compat build
|
// Note: the following assumes DOM environment since the compat build
|
||||||
// only targets web. It essentially includes logic for app.mount from
|
// only targets web. It essentially includes logic for app.mount from
|
||||||
// both runtime-core AND runtime-dom.
|
// both runtime-core AND runtime-dom.
|
||||||
instance.ctx._compat_mount = (selectorOrEl: string | Element) => {
|
instance.ctx._compat_mount = (selectorOrEl?: string | Element) => {
|
||||||
if (isMounted) {
|
if (isMounted) {
|
||||||
__DEV__ && warn(`Root instance is already mounted.`)
|
__DEV__ && warn(`Root instance is already mounted.`)
|
||||||
return
|
return
|
||||||
@ -351,14 +385,8 @@ export function installCompatMount(
|
|||||||
}
|
}
|
||||||
container = result
|
container = result
|
||||||
} else {
|
} else {
|
||||||
if (!selectorOrEl) {
|
// eslint-disable-next-line
|
||||||
__DEV__ &&
|
container = selectorOrEl || document.createElement('div')
|
||||||
warn(
|
|
||||||
`Failed to mount root instance: invalid mount target ${selectorOrEl}.`
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
container = selectorOrEl
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const isSVG = container instanceof SVGElement
|
const isSVG = container instanceof SVGElement
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { extend, 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 { deepMergeData } from './data'
|
import { mergeDataOption } from './data'
|
||||||
import {
|
import {
|
||||||
DeprecationTypes,
|
DeprecationTypes,
|
||||||
warnDeprecation,
|
warnDeprecation,
|
||||||
@ -80,34 +80,36 @@ export function installLegacyConfigProperties(config: AppConfig) {
|
|||||||
// Internal merge strats which are no longer needed in v3, but we need to
|
// 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
|
// expose them because some v2 plugins will reuse these internal strats to
|
||||||
// merge their custom options.
|
// merge their custom options.
|
||||||
const strats = config.optionMergeStrategies as any
|
extend(config.optionMergeStrategies, legacyOptionMergeStrats)
|
||||||
strats.data = deepMergeData
|
}
|
||||||
// lifecycle hooks
|
|
||||||
strats.beforeCreate = mergeHook
|
export const legacyOptionMergeStrats = {
|
||||||
strats.created = mergeHook
|
data: mergeDataOption,
|
||||||
strats.beforeMount = mergeHook
|
beforeCreate: mergeHook,
|
||||||
strats.mounted = mergeHook
|
created: mergeHook,
|
||||||
strats.beforeUpdate = mergeHook
|
beforeMount: mergeHook,
|
||||||
strats.updated = mergeHook
|
mounted: mergeHook,
|
||||||
strats.beforeDestroy = mergeHook
|
beforeUpdate: mergeHook,
|
||||||
strats.destroyed = mergeHook
|
updated: mergeHook,
|
||||||
strats.activated = mergeHook
|
beforeDestroy: mergeHook,
|
||||||
strats.deactivated = mergeHook
|
destroyed: mergeHook,
|
||||||
strats.errorCaptured = mergeHook
|
activated: mergeHook,
|
||||||
strats.serverPrefetch = mergeHook
|
deactivated: mergeHook,
|
||||||
|
errorCaptured: mergeHook,
|
||||||
|
serverPrefetch: mergeHook,
|
||||||
// assets
|
// assets
|
||||||
strats.components = mergeObjectOptions
|
components: mergeObjectOptions,
|
||||||
strats.directives = mergeObjectOptions
|
directives: mergeObjectOptions,
|
||||||
strats.filters = mergeObjectOptions
|
filters: mergeObjectOptions,
|
||||||
// objects
|
// objects
|
||||||
strats.props = mergeObjectOptions
|
props: mergeObjectOptions,
|
||||||
strats.methods = mergeObjectOptions
|
methods: mergeObjectOptions,
|
||||||
strats.inject = mergeObjectOptions
|
inject: mergeObjectOptions,
|
||||||
strats.computed = mergeObjectOptions
|
computed: mergeObjectOptions,
|
||||||
// watch has special merge behavior in v2, but isn't actually needed in v3.
|
// 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
|
// since we are only exposing these for compat and nobody should be relying
|
||||||
// on the watch-specific behavior, just expose the object merge strat.
|
// on the watch-specific behavior, just expose the object merge strat.
|
||||||
strats.watch = mergeObjectOptions
|
watch: mergeObjectOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
function mergeHook(
|
function mergeHook(
|
||||||
|
@ -6,7 +6,10 @@ import {
|
|||||||
toDisplayString,
|
toDisplayString,
|
||||||
toNumber
|
toNumber
|
||||||
} from '@vue/shared'
|
} from '@vue/shared'
|
||||||
import { PublicPropertiesMap } from '../componentPublicInstance'
|
import {
|
||||||
|
ComponentPublicInstance,
|
||||||
|
PublicPropertiesMap
|
||||||
|
} from '../componentPublicInstance'
|
||||||
import { getCompatChildren } from './instanceChildren'
|
import { getCompatChildren } from './instanceChildren'
|
||||||
import {
|
import {
|
||||||
DeprecationTypes,
|
DeprecationTypes,
|
||||||
@ -33,6 +36,23 @@ import {
|
|||||||
} from './renderHelpers'
|
} from './renderHelpers'
|
||||||
import { resolveFilter } from '../helpers/resolveAssets'
|
import { resolveFilter } from '../helpers/resolveAssets'
|
||||||
import { resolveMergedOptions } from '../componentOptions'
|
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) {
|
export function installCompatInstanceProperties(map: PublicPropertiesMap) {
|
||||||
const set = (target: any, key: any, val: any) => {
|
const set = (target: any, key: any, val: any) => {
|
||||||
|
@ -893,7 +893,13 @@ function callHookWithMixinAndExtends(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (selfHook) {
|
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(
|
export function mergeOptions(
|
||||||
to: any,
|
to: any,
|
||||||
from: any,
|
from: any,
|
||||||
instance?: ComponentInternalInstance
|
instance?: ComponentInternalInstance | null,
|
||||||
|
strats = instance && instance.appContext.config.optionMergeStrategies
|
||||||
) {
|
) {
|
||||||
if (__COMPAT__ && isFunction(from)) {
|
if (__COMPAT__ && isFunction(from)) {
|
||||||
from = from.options
|
from = from.options
|
||||||
}
|
}
|
||||||
|
|
||||||
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, strats)
|
||||||
mixins &&
|
mixins &&
|
||||||
mixins.forEach((m: ComponentOptionsMixin) => mergeOptions(to, m, instance))
|
mixins.forEach((m: ComponentOptionsMixin) =>
|
||||||
|
mergeOptions(to, m, instance, strats)
|
||||||
|
)
|
||||||
|
|
||||||
for (const key in from) {
|
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)
|
to[key] = strats[key](to[key], from[key], instance && instance.proxy, key)
|
||||||
} else {
|
} else {
|
||||||
to[key] = from[key]
|
to[key] = from[key]
|
||||||
|
Loading…
Reference in New Issue
Block a user