Skip to content

Commit

Permalink
fix(compat): fix $options mutation + adjust private API initialization
Browse files Browse the repository at this point in the history
close #10626
close #10636
  • Loading branch information
yyx990803 committed Apr 15, 2024
1 parent 04af950 commit d58d133
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 43 deletions.
114 changes: 71 additions & 43 deletions packages/runtime-core/src/compat/instance.ts
Expand Up @@ -15,6 +15,7 @@ import {
DeprecationTypes,
assertCompatEnabled,
isCompatEnabled,
warnDeprecation,
} from './compatConfig'
import { off, on, once } from './instanceEventEmitter'
import { getCompatListeners } from './instanceListeners'
Expand Down Expand Up @@ -121,50 +122,77 @@ export function installCompatInstanceProperties(map: PublicPropertiesMap) {

$children: getCompatChildren,
$listeners: getCompatListeners,

// inject additional properties into $options for compat
// e.g. vuex needs this.$options.parent
$options: i => {
if (!isCompatEnabled(DeprecationTypes.PRIVATE_APIS, i)) {
return resolveMergedOptions(i)
}
if (i.resolvedOptions) {
return i.resolvedOptions
}
const res = (i.resolvedOptions = extend({}, resolveMergedOptions(i)))
Object.defineProperties(res, {
parent: {
get() {
warnDeprecation(DeprecationTypes.PRIVATE_APIS, i, '$options.parent')
return i.proxy!.$parent
},
},
propsData: {
get() {
warnDeprecation(
DeprecationTypes.PRIVATE_APIS,
i,
'$options.propsData',
)
return i.vnode.props
},
},
})
return res
},
} as PublicPropertiesMap)

/* istanbul ignore if */
if (isCompatEnabled(DeprecationTypes.PRIVATE_APIS, null)) {
extend(map, {
// needed by many libs / render fns
$vnode: i => i.vnode,

// inject additional properties into $options for compat
// e.g. vuex needs this.$options.parent
$options: i => {
const res = extend({}, resolveMergedOptions(i))
res.parent = i.proxy!.$parent
res.propsData = i.vnode.props
return res
},

// some private properties that are likely accessed...
_self: i => i.proxy,
_uid: i => i.uid,
_data: i => i.data,
_isMounted: i => i.isMounted,
_isDestroyed: i => i.isUnmounted,

// v2 render helpers
$createElement: () => compatH,
_c: () => compatH,
_o: () => legacyMarkOnce,
_n: () => looseToNumber,
_s: () => toDisplayString,
_l: () => renderList,
_t: i => legacyRenderSlot.bind(null, i),
_q: () => looseEqual,
_i: () => looseIndexOf,
_m: i => legacyRenderStatic.bind(null, i),
_f: () => resolveFilter,
_k: i => legacyCheckKeyCodes.bind(null, i),
_b: () => legacyBindObjectProps,
_v: () => createTextVNode,
_e: () => createCommentVNode,
_u: () => legacyresolveScopedSlots,
_g: () => legacyBindObjectListeners,
_d: () => legacyBindDynamicKeys,
_p: () => legacyPrependModifier,
} as PublicPropertiesMap)
const privateAPIs = {
// needed by many libs / render fns
$vnode: i => i.vnode,

// some private properties that are likely accessed...
_self: i => i.proxy,
_uid: i => i.uid,
_data: i => i.data,
_isMounted: i => i.isMounted,
_isDestroyed: i => i.isUnmounted,

// v2 render helpers
$createElement: () => compatH,
_c: () => compatH,
_o: () => legacyMarkOnce,
_n: () => looseToNumber,
_s: () => toDisplayString,
_l: () => renderList,
_t: i => legacyRenderSlot.bind(null, i),
_q: () => looseEqual,
_i: () => looseIndexOf,
_m: i => legacyRenderStatic.bind(null, i),
_f: () => resolveFilter,
_k: i => legacyCheckKeyCodes.bind(null, i),
_b: () => legacyBindObjectProps,
_v: () => createTextVNode,
_e: () => createCommentVNode,
_u: () => legacyresolveScopedSlots,
_g: () => legacyBindObjectListeners,
_d: () => legacyBindDynamicKeys,
_p: () => legacyPrependModifier,
} as PublicPropertiesMap

for (const key in privateAPIs) {
map[key] = i => {
if (isCompatEnabled(DeprecationTypes.PRIVATE_APIS, i)) {
return privateAPIs[key](i)
}
}
}
}
7 changes: 7 additions & 0 deletions packages/runtime-core/src/component.ts
Expand Up @@ -45,6 +45,7 @@ import { type Directive, validateDirectiveName } from './directives'
import {
type ComponentOptions,
type ComputedOptions,
type MergedComponentOptions,
type MethodOptions,
applyOptions,
resolveMergedOptions,
Expand Down Expand Up @@ -524,6 +525,12 @@ export interface ComponentInternalInstance {
* @internal
*/
getCssVars?: () => Record<string, string>

/**
* v2 compat only, for caching mutated $options
* @internal
*/
resolvedOptions?: MergedComponentOptions
}

const emptyAppContext = createAppContext()
Expand Down
41 changes: 41 additions & 0 deletions packages/vue-compat/__tests__/instance.spec.ts
Expand Up @@ -14,6 +14,7 @@ beforeEach(() => {
Vue.configureCompat({
MODE: 2,
GLOBAL_MOUNT: 'suppress-warning',
PRIVATE_APIS: 'suppress-warning',
})
})

Expand Down Expand Up @@ -331,3 +332,43 @@ test('INSTANCE_ATTR_CLASS_STYLE', () => {
)('Anonymous'),
).toHaveBeenWarned()
})

test('$options mutation', () => {
const Comp = {
props: ['id'],
template: '<div/>',
data() {
return {
foo: '',
}
},
created(this: any) {
expect(this.$options.parent).toBeDefined()
expect(this.$options.test).toBeUndefined()
this.$options.test = this.id
expect(this.$options.test).toBe(this.id)
},
}

new Vue({
template: `<div><Comp id="1"/><Comp id="2"/></div>`,
components: { Comp },
}).$mount()
})

test('other private APIs', () => {
new Vue({
created() {
expect(this.$createElement).toBeTruthy()
},
})

new Vue({
compatConfig: {
PRIVATE_APIS: false,
},
created() {
expect(this.$createElement).toBeUndefined()
},
})
})

0 comments on commit d58d133

Please sign in to comment.