Subscribe on changes!

[compiler-sfc] inconsistent behavior with vue2 when parsing empty blocks

avatar
Oct 22nd 2020

Version

3.0.2

Reproduction link

https://codesandbox.io/s/autumn-wildflower-cqpew?file=/src/index.ts

Steps to reproduce

import { parse } from "@vue/compiler-sfc";
import { parse as parse2 } from "@vue/component-compiler-utils";

const source = `\
<template></template>
<script></script>
<style></style>
`;

const vue3 = parse(source);

console.log("vue3", vue3.descriptor);

// template: null
// script: null
// style: []

const vue2 = parse2({
  source,
  compiler: require("vue-template-compiler")
});

console.log("vue2", vue2);

// template: { ... }
// script: { ... }
// style: [{ ... }]

What is expected?

Consistent with vue2, get the descriptor of empty block.

What is actually happening?

The descriptor of empty block is null.

Related issues

  • #464

Explanation

It seems to be intended as the tests show:

https://github.com/vuejs/vue-next/blob/f4621ff5ee4abe924d985177956af3ddc9bb378f/packages/compiler-sfc/__tests__/parse.spec.ts#L114-L119

However, it may cause some breakings in SFC:

  • An empty <template> block means it should render empty string, which is different from no <template> block.
  • If @vue/compiler-sfc return null for empty <template>, it's difficult for vue-loader / rollup-plugin-vue to determine if the SFC has <template> block or not.

In fact, the only difference is a runtime warning during development:

https://github.com/vuejs/vue-next/blob/288c764e5279ccef63e0ef304d4250f5ad935a46/packages/runtime-core/src/component.ts#L711

IMO:

  • An empty <template> block is intended by user, which should not be warned.
  • No <template> block might be a fault, which should be warned.

Questions / Discussion

As we do not have a full specification for vue SFC, some edge cases are not so specific, and the behavior of vue-loader / rollup-plugin-vue / other implementations can be inconsistent.

Here are some edge cases that might need to be determined:

Case A: SFC has both <template> and script.render:

<template>
  <div></div>
</template>

<script>
export default {
  render() {}
}
</script>

Which is expected?

  • replace script.render with <template>
  • use script.render

Current behavior of vue-loader and rollup-plugin-vue is the first one.

Case B: SFC has empty <template> and script.render:

<template></template>

<script>
export default {
  render() {}
}
</script>

Which is expected?

  • replace script.render with empty <template>
  • use script.render

This is affected by this issue.

Case C: SFC has empty <template>, but does not have script.render:

<template></template>

<script>
export default {
  // no render()
}
</script>

Which is expected?

  • replace script.render with empty <template>
  • replace script.render with a NOOP as default render
  • keep it undefined (will print a runtime warning)

This is affected by this issue.

Case D: SFC does not have <template> nor script.render:

<script>
export default {
  // no render()
}
</script>

Which is expected?

  • replace script.render with a NOOP as default render
  • keep it undefined (will print a runtime warning)

@yyx990803 @sodatea @znck

avatar
Nov 3rd 2020

Another edge case as brought up by @znck: no <template>, no root level render, but the setup function returns a render function. In this case the omission of <template> is intentional.