Evan You 10 лет назад
Родитель
Сommit
0a15d9ee27

+ 15 - 0
src/compiler/parser/index.js

@@ -117,6 +117,21 @@ export function parse (
       // tree management
       if (!root) {
         root = element
+        // check root element constraints
+        if (process.env.NODE_ENV !== 'production') {
+          if (tag === 'slot' || tag === 'template') {
+            warn(
+              `Cannot use <${tag}> as component root element because it may ` +
+              'contain multiple nodes:\n' + template
+            )
+          }
+          if (element.attrsMap.hasOwnProperty('v-for')) {
+            warn(
+              'Cannot use v-for on component root element because it renders ' +
+              'multiple elements:\n' + template
+            )
+          }
+        }
       } else if (process.env.NODE_ENV !== 'production' && !stack.length && !warned) {
         warned = true
         warn(

+ 6 - 2
src/core/instance/render.js

@@ -1,6 +1,6 @@
 /* @flow */
 
-import { emptyVNode } from '../vdom/vnode'
+import VNode, { emptyVNode } from '../vdom/vnode'
 import { normalizeChildren } from '../vdom/helpers'
 import { bind, remove, isObject, renderString } from 'shared/util'
 import { resolveAsset, nextTick } from '../util/index'
@@ -57,7 +57,11 @@ export function renderMixin (Vue: Class<Component>) {
       resolveSlots(vm, _renderChildren)
     }
     // render self
-    const vnode = render.call(vm._renderProxy) || emptyVNode
+    let vnode = render.call(vm._renderProxy)
+    // return empty vnode in case the render function errored out
+    if (!(vnode instanceof VNode)) {
+      vnode = emptyVNode
+    }
     // set parent
     vnode.parent = _parentVnode
     // restore render state

+ 15 - 0
test/unit/modules/compiler/parser.spec.js

@@ -77,6 +77,21 @@ describe('parser', () => {
     expect('Component template should contain exactly one root element').toHaveBeenWarned()
   })
 
+  it('warn <template> as root element', () => {
+    parse('<template></template>', baseOptions)
+    expect('Cannot use <template> as component root element').toHaveBeenWarned()
+  })
+
+  it('warn <slot> as root element', () => {
+    parse('<slot></slot>', baseOptions)
+    expect('Cannot use <slot> as component root element').toHaveBeenWarned()
+  })
+
+  it('warn v-for on root element', () => {
+    parse('<div v-for="item in items"></div>', baseOptions)
+    expect('Cannot use v-for on component root element').toHaveBeenWarned()
+  })
+
   it('v-pre directive', () => {
     const ast = parse('<div v-pre id="message1"><p>{{msg}}</p></div>', baseOptions)
     expect(ast.pre).toBe(true)