Skip to content

Commit 8604075

Browse files
committed
feat(warn): warn when an existing property starting with $ is not properly accessed
Closes vuejs#8213
1 parent 5e3823a commit 8604075

File tree

2 files changed

+51
-2
lines changed

2 files changed

+51
-2
lines changed

src/core/instance/proxy.js

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,16 @@ if (process.env.NODE_ENV !== 'production') {
2424
)
2525
}
2626

27+
const warnStartingWithDollar = (target, key) => {
28+
warn(
29+
`Property "${key}" must be accessed with "$data.${key}" because ` +
30+
'properties starting with "$" or "_" are not proxied in the Vue instance to ' +
31+
'prevent conflicts with Vue internals' +
32+
'See: https://vuejs.org/v2/api/#data',
33+
target
34+
)
35+
}
36+
2737
const hasProxy =
2838
typeof Proxy !== 'undefined' && isNative(Proxy)
2939

@@ -47,7 +57,8 @@ if (process.env.NODE_ENV !== 'production') {
4757
const has = key in target
4858
const isAllowed = allowedGlobals(key) || (typeof key === 'string' && key.charAt(0) === '_')
4959
if (!has && !isAllowed) {
50-
warnNonPresent(target, key)
60+
if (key in target.$data) warnStartingWithDollar(target, key)
61+
else warnNonPresent(target, key)
5162
}
5263
return has || !isAllowed
5364
}
@@ -56,7 +67,8 @@ if (process.env.NODE_ENV !== 'production') {
5667
const getHandler = {
5768
get (target, key) {
5869
if (typeof key === 'string' && !(key in target)) {
59-
warnNonPresent(target, key)
70+
if (key in target.$data) warnStartingWithDollar(target, key)
71+
else warnNonPresent(target, key)
6072
}
6173
return target[key]
6274
}

test/unit/features/instance/render-proxy.spec.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,5 +45,42 @@ if (typeof Proxy !== 'undefined') {
4545

4646
expect(vm.$el.textContent).toBe('foo')
4747
})
48+
49+
it('should warn properties starting with $ when found', () => {
50+
new Vue({
51+
data: { $a: 'foo' },
52+
template: `<div>{{ $a }}</div>`
53+
}).$mount()
54+
expect(`Property "$a" must be accessed with "$data.$a"`).toHaveBeenWarned()
55+
})
56+
57+
it('should warn properties starting with $ when not found', () => {
58+
new Vue({
59+
template: `<div>{{ $a }}</div>`
60+
}).$mount()
61+
expect(`Property or method "$a" is not defined`).toHaveBeenWarned()
62+
expect(`Property "$a" must be accessed with "$data.$a"`).not.toHaveBeenWarned()
63+
})
64+
65+
it('should warn properties starting with $ when not found (with stripped)', () => {
66+
const render = function (h) {
67+
return h('p', this.$a)
68+
}
69+
render._withStripped = true
70+
new Vue({
71+
data: { $a: 'foo' },
72+
render
73+
}).$mount()
74+
expect(`Property "$a" must be accessed with "$data.$a"`).toHaveBeenWarned()
75+
})
76+
77+
it('should not warn properties starting with $ when using $data to access', () => {
78+
new Vue({
79+
data: { $a: 'foo' },
80+
template: `<div>{{ $data.$a }}</div>`
81+
}).$mount()
82+
expect(`Property or method "$a" is not defined`).not.toHaveBeenWarned()
83+
expect(`Property or method "$a" is not defined`).not.toHaveBeenWarned()
84+
})
4885
})
4986
}

0 commit comments

Comments
 (0)