mirror of https://github.com/vuejs/core.git
fix(sfc): ensure `<script setup>` binding behavior consistency on `this` between prod and dev
close #6248
This commit is contained in:
parent
15e889afaf
commit
f73925d76a
|
@ -4,7 +4,8 @@ import {
|
|||
getCurrentInstance,
|
||||
nodeOps,
|
||||
createApp,
|
||||
shallowReadonly
|
||||
shallowReadonly,
|
||||
defineComponent
|
||||
} from '@vue/runtime-test'
|
||||
import { ComponentInternalInstance, ComponentOptions } from '../src/component'
|
||||
|
||||
|
@ -458,4 +459,24 @@ describe('component: proxy', () => {
|
|||
)} was accessed during render ` + `but is not defined on instance.`
|
||||
).toHaveBeenWarned()
|
||||
})
|
||||
|
||||
test('should prevent mutating script setup bindings', () => {
|
||||
const Comp = defineComponent({
|
||||
render() {},
|
||||
setup() {
|
||||
return {
|
||||
__isScriptSetup: true,
|
||||
foo: 1
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
expect('foo' in this).toBe(false)
|
||||
try {
|
||||
this.foo = 123
|
||||
} catch (e) {}
|
||||
}
|
||||
})
|
||||
render(h(Comp), nodeOps.createElement('div'))
|
||||
expect(`Cannot mutate <script setup> binding "foo"`).toHaveBeenWarned()
|
||||
})
|
||||
})
|
||||
|
|
|
@ -270,6 +270,9 @@ export interface ComponentRenderContext {
|
|||
|
||||
export const isReservedPrefix = (key: string) => key === '_' || key === '$'
|
||||
|
||||
const hasSetupBinding = (state: Data, key: string) =>
|
||||
state !== EMPTY_OBJ && !state.__isScriptSetup && hasOwn(state, key)
|
||||
|
||||
export const PublicInstanceProxyHandlers: ProxyHandler<any> = {
|
||||
get({ _: instance }: ComponentRenderContext, key: string) {
|
||||
const { ctx, setupState, data, props, accessCache, type, appContext } =
|
||||
|
@ -280,19 +283,6 @@ export const PublicInstanceProxyHandlers: ProxyHandler<any> = {
|
|||
return true
|
||||
}
|
||||
|
||||
// prioritize <script setup> bindings during dev.
|
||||
// this allows even properties that start with _ or $ to be used - so that
|
||||
// it aligns with the production behavior where the render fn is inlined and
|
||||
// indeed has access to all declared variables.
|
||||
if (
|
||||
__DEV__ &&
|
||||
setupState !== EMPTY_OBJ &&
|
||||
setupState.__isScriptSetup &&
|
||||
hasOwn(setupState, key)
|
||||
) {
|
||||
return setupState[key]
|
||||
}
|
||||
|
||||
// data / props / ctx
|
||||
// This getter gets called for every property access on the render context
|
||||
// during render and is a major hotspot. The most expensive part of this
|
||||
|
@ -314,7 +304,7 @@ export const PublicInstanceProxyHandlers: ProxyHandler<any> = {
|
|||
return props![key]
|
||||
// default: just fallthrough
|
||||
}
|
||||
} else if (setupState !== EMPTY_OBJ && hasOwn(setupState, key)) {
|
||||
} else if (hasSetupBinding(setupState, key)) {
|
||||
accessCache![key] = AccessTypes.SETUP
|
||||
return setupState[key]
|
||||
} else if (data !== EMPTY_OBJ && hasOwn(data, key)) {
|
||||
|
@ -403,26 +393,28 @@ export const PublicInstanceProxyHandlers: ProxyHandler<any> = {
|
|||
value: any
|
||||
): boolean {
|
||||
const { data, setupState, ctx } = instance
|
||||
if (setupState !== EMPTY_OBJ && hasOwn(setupState, key)) {
|
||||
if (hasSetupBinding(setupState, key)) {
|
||||
setupState[key] = value
|
||||
return true
|
||||
} else if (
|
||||
__DEV__ &&
|
||||
setupState.__isScriptSetup &&
|
||||
hasOwn(setupState, key)
|
||||
) {
|
||||
warn(`Cannot mutate <script setup> binding "${key}" from Options API.`)
|
||||
return false
|
||||
} else if (data !== EMPTY_OBJ && hasOwn(data, key)) {
|
||||
data[key] = value
|
||||
return true
|
||||
} else if (hasOwn(instance.props, key)) {
|
||||
__DEV__ &&
|
||||
warn(
|
||||
`Attempting to mutate prop "${key}". Props are readonly.`,
|
||||
instance
|
||||
)
|
||||
__DEV__ && warn(`Attempting to mutate prop "${key}". Props are readonly.`)
|
||||
return false
|
||||
}
|
||||
if (key[0] === '$' && key.slice(1) in instance) {
|
||||
__DEV__ &&
|
||||
warn(
|
||||
`Attempting to mutate public property "${key}". ` +
|
||||
`Properties starting with $ are reserved and readonly.`,
|
||||
instance
|
||||
`Properties starting with $ are reserved and readonly.`
|
||||
)
|
||||
return false
|
||||
} else {
|
||||
|
@ -449,7 +441,7 @@ export const PublicInstanceProxyHandlers: ProxyHandler<any> = {
|
|||
return (
|
||||
!!accessCache![key] ||
|
||||
(data !== EMPTY_OBJ && hasOwn(data, key)) ||
|
||||
(setupState !== EMPTY_OBJ && hasOwn(setupState, key)) ||
|
||||
hasSetupBinding(setupState, key) ||
|
||||
((normalizedProps = propsOptions[0]) && hasOwn(normalizedProps, key)) ||
|
||||
hasOwn(ctx, key) ||
|
||||
hasOwn(publicPropertiesMap, key) ||
|
||||
|
|
Loading…
Reference in New Issue