From 8604075be632867d22b4d24c469e3204b3e8fefb Mon Sep 17 00:00:00 2001 From: Eduardo San Martin Morote Date: Mon, 21 May 2018 18:21:59 +0200 Subject: [PATCH 1/4] feat(warn): warn when an existing property starting with $ is not properly accessed Closes #8213 --- src/core/instance/proxy.js | 16 +++++++- .../features/instance/render-proxy.spec.js | 37 +++++++++++++++++++ 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/src/core/instance/proxy.js b/src/core/instance/proxy.js index 8c821baa09b..28b37756fb3 100644 --- a/src/core/instance/proxy.js +++ b/src/core/instance/proxy.js @@ -24,6 +24,16 @@ if (process.env.NODE_ENV !== 'production') { ) } + const warnStartingWithDollar = (target, key) => { + warn( + `Property "${key}" must be accessed with "$data.${key}" because ` + + 'properties starting with "$" or "_" are not proxied in the Vue instance to ' + + 'prevent conflicts with Vue internals' + + 'See: https://vuejs.org/v2/api/#data', + target + ) + } + const hasProxy = typeof Proxy !== 'undefined' && isNative(Proxy) @@ -47,7 +57,8 @@ if (process.env.NODE_ENV !== 'production') { const has = key in target const isAllowed = allowedGlobals(key) || (typeof key === 'string' && key.charAt(0) === '_') if (!has && !isAllowed) { - warnNonPresent(target, key) + if (key in target.$data) warnStartingWithDollar(target, key) + else warnNonPresent(target, key) } return has || !isAllowed } @@ -56,7 +67,8 @@ if (process.env.NODE_ENV !== 'production') { const getHandler = { get (target, key) { if (typeof key === 'string' && !(key in target)) { - warnNonPresent(target, key) + if (key in target.$data) warnStartingWithDollar(target, key) + else warnNonPresent(target, key) } return target[key] } diff --git a/test/unit/features/instance/render-proxy.spec.js b/test/unit/features/instance/render-proxy.spec.js index 9a2365ae4c1..e5ddd852b36 100644 --- a/test/unit/features/instance/render-proxy.spec.js +++ b/test/unit/features/instance/render-proxy.spec.js @@ -45,5 +45,42 @@ if (typeof Proxy !== 'undefined') { expect(vm.$el.textContent).toBe('foo') }) + + it('should warn properties starting with $ when found', () => { + new Vue({ + data: { $a: 'foo' }, + template: `
{{ $a }}
` + }).$mount() + expect(`Property "$a" must be accessed with "$data.$a"`).toHaveBeenWarned() + }) + + it('should warn properties starting with $ when not found', () => { + new Vue({ + template: `
{{ $a }}
` + }).$mount() + expect(`Property or method "$a" is not defined`).toHaveBeenWarned() + expect(`Property "$a" must be accessed with "$data.$a"`).not.toHaveBeenWarned() + }) + + it('should warn properties starting with $ when not found (with stripped)', () => { + const render = function (h) { + return h('p', this.$a) + } + render._withStripped = true + new Vue({ + data: { $a: 'foo' }, + render + }).$mount() + expect(`Property "$a" must be accessed with "$data.$a"`).toHaveBeenWarned() + }) + + it('should not warn properties starting with $ when using $data to access', () => { + new Vue({ + data: { $a: 'foo' }, + template: `
{{ $data.$a }}
` + }).$mount() + expect(`Property or method "$a" is not defined`).not.toHaveBeenWarned() + expect(`Property or method "$a" is not defined`).not.toHaveBeenWarned() + }) }) } From 8dd1b540078b7dc07292478510cb01ce46d1a26d Mon Sep 17 00:00:00 2001 From: Eduardo San Martin Morote Date: Mon, 21 May 2018 18:45:09 +0200 Subject: [PATCH 2/4] feat(warn): add case for properties starting with _ --- src/core/instance/proxy.js | 3 ++- test/unit/features/instance/render-proxy.spec.js | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/core/instance/proxy.js b/src/core/instance/proxy.js index 28b37756fb3..1c877334483 100644 --- a/src/core/instance/proxy.js +++ b/src/core/instance/proxy.js @@ -55,7 +55,8 @@ if (process.env.NODE_ENV !== 'production') { const hasHandler = { has (target, key) { const has = key in target - const isAllowed = allowedGlobals(key) || (typeof key === 'string' && key.charAt(0) === '_') + const isAllowed = allowedGlobals(key) || + (typeof key === 'string' && key.charAt(0) === '_' && !(key in target.$data)) if (!has && !isAllowed) { if (key in target.$data) warnStartingWithDollar(target, key) else warnNonPresent(target, key) diff --git a/test/unit/features/instance/render-proxy.spec.js b/test/unit/features/instance/render-proxy.spec.js index e5ddd852b36..b89c95876c3 100644 --- a/test/unit/features/instance/render-proxy.spec.js +++ b/test/unit/features/instance/render-proxy.spec.js @@ -54,6 +54,14 @@ if (typeof Proxy !== 'undefined') { expect(`Property "$a" must be accessed with "$data.$a"`).toHaveBeenWarned() }) + it('should warn properties starting with _ when found', () => { + new Vue({ + data: { _foo: 'foo' }, + template: `
{{ _foo }}
` + }).$mount() + expect(`Property "_foo" must be accessed with "$data._foo"`).toHaveBeenWarned() + }) + it('should warn properties starting with $ when not found', () => { new Vue({ template: `
{{ $a }}
` From abe72a74233572a6194d1ff9e26b3cd7447cfbe5 Mon Sep 17 00:00:00 2001 From: Eduardo San Martin Morote Date: Mon, 21 May 2018 19:25:28 +0200 Subject: [PATCH 3/4] refactor(warn): rename warnStartingWithDollar -> warnReservedPrefix --- src/core/instance/proxy.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/instance/proxy.js b/src/core/instance/proxy.js index 1c877334483..08d96513254 100644 --- a/src/core/instance/proxy.js +++ b/src/core/instance/proxy.js @@ -24,7 +24,7 @@ if (process.env.NODE_ENV !== 'production') { ) } - const warnStartingWithDollar = (target, key) => { + const warnReservedPrefix = (target, key) => { warn( `Property "${key}" must be accessed with "$data.${key}" because ` + 'properties starting with "$" or "_" are not proxied in the Vue instance to ' + @@ -58,7 +58,7 @@ if (process.env.NODE_ENV !== 'production') { const isAllowed = allowedGlobals(key) || (typeof key === 'string' && key.charAt(0) === '_' && !(key in target.$data)) if (!has && !isAllowed) { - if (key in target.$data) warnStartingWithDollar(target, key) + if (key in target.$data) warnReservedPrefix(target, key) else warnNonPresent(target, key) } return has || !isAllowed @@ -68,7 +68,7 @@ if (process.env.NODE_ENV !== 'production') { const getHandler = { get (target, key) { if (typeof key === 'string' && !(key in target)) { - if (key in target.$data) warnStartingWithDollar(target, key) + if (key in target.$data) warnReservedPrefix(target, key) else warnNonPresent(target, key) } return target[key] From 690d9b9a9b49a763dfbadca30baa37245815c92e Mon Sep 17 00:00:00 2001 From: Eduardo San Martin Morote Date: Thu, 31 May 2018 23:32:57 +0200 Subject: [PATCH 4/4] Update render-proxy.spec.js --- test/unit/features/instance/render-proxy.spec.js | 1 - 1 file changed, 1 deletion(-) diff --git a/test/unit/features/instance/render-proxy.spec.js b/test/unit/features/instance/render-proxy.spec.js index b89c95876c3..8da17172608 100644 --- a/test/unit/features/instance/render-proxy.spec.js +++ b/test/unit/features/instance/render-proxy.spec.js @@ -88,7 +88,6 @@ if (typeof Proxy !== 'undefined') { template: `
{{ $data.$a }}
` }).$mount() expect(`Property or method "$a" is not defined`).not.toHaveBeenWarned() - expect(`Property or method "$a" is not defined`).not.toHaveBeenWarned() }) }) }