From 296164c2076f7dab0b04c0c834d921fb8c2a2e82 Mon Sep 17 00:00:00 2001 From: Evan You Date: Mon, 24 Sep 2018 21:52:27 -0400 Subject: [PATCH] feat: dot-delimited path for watch --- packages/core/src/componentWatch.ts | 33 +++++++++++++++++++++++++++-- packages/core/src/utils.ts | 2 ++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/packages/core/src/componentWatch.ts b/packages/core/src/componentWatch.ts index 17a9eb12b..b86e5d995 100644 --- a/packages/core/src/componentWatch.ts +++ b/packages/core/src/componentWatch.ts @@ -1,4 +1,4 @@ -import { EMPTY_OBJ } from './utils' +import { EMPTY_OBJ, NOOP } from './utils' import { MountedComponent } from './component' import { ComponentWatchOptions, WatchOptions } from './componentOptions' import { autorun, stop } from '@vue/observer' @@ -36,9 +36,17 @@ export function setupWatcher( const rawGetter = typeof keyOrFn === 'string' - ? () => proxy[keyOrFn] + ? parseDotPath(keyOrFn, proxy) : () => keyOrFn.call(proxy) + if (__DEV__ && rawGetter === NOOP) { + console.warn( + `Failed watching expression: "${keyOrFn}". ` + + `Watch expressions can only be dot-delimited paths. ` + + `For more complex expressions, use $watch with a function instead.` + ) + } + const getter = options.deep ? () => traverse(rawGetter()) : rawGetter let oldValue: any @@ -85,6 +93,27 @@ export function teardownWatch(instance: MountedComponent) { } } +const bailRE = /[^\w.$]/ + +function parseDotPath(path: string, ctx: any): Function { + if (bailRE.test(path)) { + return NOOP + } + const segments = path.split('.') + if (segments.length === 1) { + return () => ctx[path] + } else { + return () => { + let obj = ctx + for (let i = 0; i < segments.length; i++) { + if (!obj) return + obj = obj[segments[i]] + } + return obj + } + } +} + function traverse(value: any, seen: Set = new Set()) { if (value === null || typeof value !== 'object' || seen.has(value)) { return diff --git a/packages/core/src/utils.ts b/packages/core/src/utils.ts index 7ea9151cd..7b5011de4 100644 --- a/packages/core/src/utils.ts +++ b/packages/core/src/utils.ts @@ -1,5 +1,7 @@ export const EMPTY_OBJ: { readonly [key: string]: any } = Object.freeze({}) +export const NOOP = () => {} + export const isReservedProp = (key: string): boolean => { switch (key) { case 'key':