Jelajahi Sumber

warn observed vnode data + handle in place mutations for vdom modules (ref #3276)

Evan You 10 tahun lalu
induk
melakukan
f0aef7556b

+ 1 - 1
src/core/observer/scheduler.js

@@ -82,7 +82,7 @@ function runSchedulerQueue (queue: Array<Watcher>) {
       if (circular[id] > config._maxUpdateCount) {
         warn(
           'You may have an infinite update loop ' + (
-            watcher === watcher.vm._watcher
+            watcher === watcher.vm && watcher.vm._watcher
               ? `in a component render function.`
               : `in watcher with expression "${watcher.expression}"`
           ),

+ 8 - 0
src/core/vdom/create-element.js

@@ -37,6 +37,14 @@ function _createElement (
     )
     return
   }
+  if (data && data.__ob__) {
+    process.env.NODE_ENV !== 'production' && warn(
+      `Avoid using observed data object as vnode data: ${JSON.stringify(data)}\n` +
+      'Always create fresh vnode data objects in each render!',
+      context
+    )
+    return
+  }
   if (!tag) {
     // in case of component :is set to falsy value
     return emptyVNode()

+ 2 - 1
src/platforms/web/runtime/modules/attrs.js

@@ -17,9 +17,10 @@ function updateAttrs (oldVnode: VNodeWithData, vnode: VNodeWithData) {
   const elm = vnode.elm
   const oldAttrs = oldVnode.data.attrs || {}
   const attrs = vnode.data.attrs || {}
+  const clonedAttrs = vnode.data.attrs = {}
 
   for (key in attrs) {
-    cur = attrs[key]
+    cur = clonedAttrs[key] = attrs[key]
     old = oldAttrs[key]
     if (old !== cur) {
       setAttr(elm, key, cur)

+ 2 - 1
src/platforms/web/runtime/modules/props.js

@@ -8,6 +8,7 @@ function updateProps (oldVnode: VNodeWithData, vnode: VNodeWithData) {
   const elm: any = vnode.elm
   const oldProps = oldVnode.data.props || {}
   const props = vnode.data.props || {}
+  const clonedProps = vnode.data.props = {}
 
   for (key in oldProps) {
     if (props[key] == null) {
@@ -15,7 +16,7 @@ function updateProps (oldVnode: VNodeWithData, vnode: VNodeWithData) {
     }
   }
   for (key in props) {
-    cur = props[key]
+    cur = clonedProps[key] = props[key]
     if (key === 'value') {
       // store value as _value as well since
       // non-string values will be stringified

+ 7 - 6
src/platforms/web/runtime/modules/style.js

@@ -1,6 +1,6 @@
 /* @flow */
 
-import { cached, camelize, toObject, extend } from 'shared/util'
+import { cached, camelize, toObject } from 'shared/util'
 
 const prefixes = ['Webkit', 'Moz', 'ms']
 
@@ -31,24 +31,25 @@ function updateStyle (oldVnode: VNodeWithData, vnode: VNodeWithData) {
 
   // handle array syntax
   if (Array.isArray(style)) {
-    style = vnode.data.style = toObject(style)
+    style = toObject(style)
   }
 
+  // clone the style for future updates,
+  // in case the user mutates the style object in-place.
+  const clonedStyle = vnode.data.style = {}
+
   for (name in oldStyle) {
     if (!style[name]) {
       elm.style[normalize(name)] = ''
     }
   }
   for (name in style) {
-    cur = style[name]
+    cur = clonedStyle[name] = style[name]
     if (cur !== oldStyle[name]) {
       // ie9 setting to null has no effect, must use empty string
       elm.style[normalize(name)] = cur || ''
     }
   }
-  // clone the style for future updates,
-  // in case the user mutates the style object in-place.
-  vnode.data.style = extend({}, style)
 }
 
 export default {