]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(runtime-core): fix globalProperties in check on instance render proxy
authorEvan You <yyx990803@gmail.com>
Mon, 6 Apr 2020 15:41:28 +0000 (11:41 -0400)
committerEvan You <yyx990803@gmail.com>
Mon, 6 Apr 2020 15:41:28 +0000 (11:41 -0400)
packages/runtime-core/__tests__/componentProxy.spec.ts
packages/runtime-core/src/componentProxy.ts

index a6e2181abb6f20340c27a978f4fc5827bdd22c50..c2e54990521c9e8cf6c705ddeba29abb1a6fedf7 100644 (file)
@@ -1,4 +1,10 @@
-import { h, render, getCurrentInstance, nodeOps } from '@vue/runtime-test'
+import {
+  h,
+  render,
+  getCurrentInstance,
+  nodeOps,
+  createApp
+} from '@vue/runtime-test'
 import { mockWarn } from '@vue/shared'
 import { ComponentInternalInstance } from '../src/component'
 
@@ -137,6 +143,33 @@ describe('component: proxy', () => {
     expect(instance!.sink.foo).toBe(1)
   })
 
+  test('globalProperties', () => {
+    let instance: ComponentInternalInstance
+    let instanceProxy: any
+    const Comp = {
+      setup() {
+        return () => null
+      },
+      mounted() {
+        instance = getCurrentInstance()!
+        instanceProxy = this
+      }
+    }
+
+    const app = createApp(Comp)
+    app.config.globalProperties.foo = 1
+    app.mount(nodeOps.createElement('div'))
+
+    expect(instanceProxy.foo).toBe(1)
+
+    // set should overwrite globalProperties with local
+    instanceProxy.foo = 2
+    expect(instanceProxy.foo).toBe(2)
+    expect(instance!.sink.foo).toBe(2)
+    // should not affect global
+    expect(app.config.globalProperties.foo).toBe(1)
+  })
+
   test('has check', () => {
     let instanceProxy: any
     const Comp = {
@@ -158,7 +191,11 @@ describe('component: proxy', () => {
         instanceProxy = this
       }
     }
-    render(h(Comp, { msg: 'hello' }), nodeOps.createElement('div'))
+
+    const app = createApp(Comp, { msg: 'hello' })
+    app.config.globalProperties.global = 1
+
+    app.mount(nodeOps.createElement('div'))
 
     // props
     expect('msg' in instanceProxy).toBe(true)
@@ -168,6 +205,8 @@ describe('component: proxy', () => {
     expect('bar' in instanceProxy).toBe(true)
     // public properties
     expect('$el' in instanceProxy).toBe(true)
+    // global properties
+    expect('global' in instanceProxy).toBe(true)
 
     // non-existent
     expect('$foobar' in instanceProxy).toBe(false)
@@ -178,6 +217,7 @@ describe('component: proxy', () => {
     expect('baz' in instanceProxy).toBe(true)
 
     // dev mode ownKeys check for console inspection
+    // should only expose own keys
     expect(Object.keys(instanceProxy)).toMatchObject([
       'msg',
       'bar',
index 032e42fc480cb441281a91ef8f77aabe6ee14df3..9aabfc1ad07581d5f9e8322b70ca33f616134ebd 100644 (file)
@@ -159,22 +159,6 @@ export const PublicInstanceProxyHandlers: ProxyHandler<any> = {
     }
   },
 
-  has(
-    {
-      _: { data, accessCache, renderContext, type, sink }
-    }: ComponentPublicProxyTarget,
-    key: string
-  ) {
-    return (
-      accessCache![key] !== undefined ||
-      (data !== EMPTY_OBJ && hasOwn(data, key)) ||
-      hasOwn(renderContext, key) ||
-      (type.props && hasOwn(normalizePropsOptions(type.props)[0], key)) ||
-      hasOwn(publicPropertiesMap, key) ||
-      hasOwn(sink, key)
-    )
-  },
-
   set(
     { _: instance }: ComponentPublicProxyTarget,
     key: string,
@@ -207,6 +191,35 @@ export const PublicInstanceProxyHandlers: ProxyHandler<any> = {
       }
     }
     return true
+  },
+
+  has(
+    {
+      _: { data, accessCache, renderContext, type, sink, appContext }
+    }: ComponentPublicProxyTarget,
+    key: string
+  ) {
+    return (
+      accessCache![key] !== undefined ||
+      (data !== EMPTY_OBJ && hasOwn(data, key)) ||
+      hasOwn(renderContext, key) ||
+      (type.props && hasOwn(normalizePropsOptions(type.props)[0], key)) ||
+      hasOwn(publicPropertiesMap, key) ||
+      hasOwn(sink, key) ||
+      hasOwn(appContext.config.globalProperties, key)
+    )
+  }
+}
+
+if (__DEV__ && !__TEST__) {
+  PublicInstanceProxyHandlers.ownKeys = (
+    target: ComponentPublicProxyTarget
+  ) => {
+    warn(
+      `Avoid app logic that relies on enumerating keys on a component instance. ` +
+        `The keys will be empty in production mode to avoid performance overhead.`
+    )
+    return Reflect.ownKeys(target)
   }
 }
 
@@ -232,13 +245,20 @@ export function createDevProxyTarget(instance: ComponentInternalInstance) {
 
   // expose internal instance for proxy handlers
   Object.defineProperty(target, `_`, {
+    configurable: true,
+    enumerable: false,
     get: () => instance
   })
 
   // expose public properties
   Object.keys(publicPropertiesMap).forEach(key => {
     Object.defineProperty(target, key, {
-      get: () => publicPropertiesMap[key](instance)
+      configurable: true,
+      enumerable: false,
+      get: () => publicPropertiesMap[key](instance),
+      // intercepted by the proxy so no need for implementation,
+      // but needed to prevent set errors
+      set: NOOP
     })
   })
 
@@ -246,7 +266,10 @@ export function createDevProxyTarget(instance: ComponentInternalInstance) {
   const { globalProperties } = instance.appContext.config
   Object.keys(globalProperties).forEach(key => {
     Object.defineProperty(target, key, {
-      get: () => globalProperties[key]
+      configurable: true,
+      enumerable: false,
+      get: () => globalProperties[key],
+      set: NOOP
     })
   })
 
@@ -264,9 +287,8 @@ export function exposePropsOnDevProxyTarget(
     Object.keys(normalizePropsOptions(propsOptions)[0]).forEach(key => {
       Object.defineProperty(proxyTarget, key, {
         enumerable: true,
+        configurable: true,
         get: () => instance.props[key],
-        // intercepted by the proxy so no need for implementation,
-        // but needed to prevent set errors
         set: NOOP
       })
     })
@@ -280,9 +302,8 @@ export function exposeRenderContextOnDevProxyTarget(
   Object.keys(toRaw(renderContext)).forEach(key => {
     Object.defineProperty(proxyTarget, key, {
       enumerable: true,
+      configurable: true,
       get: () => renderContext[key],
-      // intercepted by the proxy so no need for implementation,
-      // but needed to prevent set errors
       set: NOOP
     })
   })