Repository: vuejs/vue
Pull request: Allow 2 root nodes with v-if and v-else

Since 2 root nodes with `v-if` and `v-else` are guaranteed to return one vnode, they should be considered safe in the root of a template.


Changed files:
filename: src/compiler/parser/index.js
patch: @@ -130,29 +130,37 @@ export function parse (
         processAttrs(element)
       }
 
-      // tree management
-      if (!root) {
-        root = element
-        // check root element constraints
+      function checkRootConstraints (el) {
         if (process.env.NODE_ENV !== 'production') {
-          if (tag === 'slot' || tag === 'template') {
+          if (el.tag === 'slot' || el.tag === 'template') {
             warn(
-              `Cannot use <${tag}> as component root element because it may ` +
+              `Cannot use <${el.tag}> as component root element because it may ` +
               'contain multiple nodes:\n' + template
             )
           }
-          if (element.attrsMap.hasOwnProperty('v-for')) {
+          if (el.attrsMap.hasOwnProperty('v-for')) {
             warn(
               'Cannot use v-for on stateful component root element because ' +
               'it renders multiple elements:\n' + template
             )
           }
         }
+      }
+
+      // tree management
+      if (!root) {
+        root = element
+        checkRootConstraints(root)
       } else if (process.env.NODE_ENV !== 'production' && !stack.length && !warned) {
-        warned = true
-        warn(
-          `Component template should contain exactly one root element:\n\n${template}`
-        )
+        // allow 2 root elements with v-if and v-else
+        if ((root.attrsMap.hasOwnProperty('v-if') && element.attrsMap.hasOwnProperty('v-else'))) {
+          checkRootConstraints(element)
+        } else {
+          warned = true
+          warn(
+            `Component template should contain exactly one root element:\n\n${template}`
+          )
+        }
       }
       if (currentParent && !element.forbidden) {
         if (element.else) {

filename: test/unit/modules/compiler/parser.spec.js
patch: @@ -76,6 +76,35 @@ describe('parser', () => {
     expect('Component template should contain exactly one root element').toHaveBeenWarned()
   })
 
+  it('warn multiple root elements', () => {
+    parse('<div></div><div></div>', baseOptions)
+    expect('Component template should contain exactly one root element:\n\n<div></div><div></div>').toHaveBeenWarned()
+  })
+
+  it('not warn 2 root elements with v-if and v-else', () => {
+    parse('<div v-if="1"></div><div v-else></div>', baseOptions)
+    expect('Component template should contain exactly one root element:\n\n<div v-if="1"></div><div v-else></div>')
+      .not.toHaveBeenWarned()
+  })
+
+  it('warn 2 root elements with v-if', () => {
+    parse('<div v-if="1"></div><div v-if="2"></div>', baseOptions)
+    expect('Component template should contain exactly one root element:\n\n<div v-if="1"></div><div v-if="2"></div>')
+      .toHaveBeenWarned()
+  })
+
+  it('warn 3 root elements with v-if and v-else on first 2', () => {
+    parse('<div v-if="1"></div><div v-else></div><div></div>', baseOptions)
+    expect('Component template should contain exactly one root element:\n\n<div v-if="1"></div><div v-else></div><div></div>')
+      .toHaveBeenWarned()
+  })
+
+  it('warn 2 root elements with v-if and v-else with v-for on 2nd', () => {
+    parse('<div v-if="1"></div><div v-else v-for="i in [1]"></div>', baseOptions)
+    expect('Cannot use v-for on stateful component root element because it renders multiple elements:\n<div v-if="1"></div><div v-else v-for="i in [1]"></div>')
+      .toHaveBeenWarned()
+  })
+
   it('warn <template> as root element', () => {
     parse('<template></template>', baseOptions)
     expect('Cannot use <template> as component root element').toHaveBeenWarned()