Vue: Handle whitespaces in templates more wisely

Created on 16 Dec 2018  Â·  4Comments  Â·  Source: vuejs/vue

What problem does this feature solve?

Currently we have an boolean option for vue-template-compiler: preserveWhitespace. It either removes all whitespace-only text nodes, or leave them untouched. There were already earlier questions about either behavior: #6200, #7701, #9021, #9127. I think the current behavior is kind of oversimplified to cover actual usage.

When we write a template we tend to leverage line breaks and indents to make it more readable, like:

<div class="item">
  <div class="aside">Aside</div>
  <div class="main">Main</div>
</div>

And if we choose to layout this component with inline formatting context, sometimes we may not want to precisely control the margin between the inner parts with CSS, instead of using the size of a whitespace (which is related to those font-* styles). For similar cases we don't want these whitespace-only text nodes. This leads to preserveWhitespace: false (and it even became the default behavior for Vue CLI 3: https://github.com/vuejs/vue-cli/commit/1864cef09e186cdf094fca876f8638a8ba1b4adb).

But when we craft some document/article-like content, this behavior becomes annoying. With preserveWhitespace: false, the following template:

<p>
  Welcome to <b>Vue.js</b> <i>world</i>.
  Have fun!
</p>

Will generate:

<p>
  Welcome to <b>Vue.js</b><i>world</i>.
  Have fun!
</p>

Which looks like:

Welcome to Vue.jsworld. Have fun!

And this is clearly not desired.

What does the proposed API look like?

In short, I suggest we offer a new compiler option, to apply the strategy React uses to handles whitespaces for JSX (source):

JSX removes whitespace at the beginning and ending of a line. It also removes blank lines. New lines adjacent to tags are removed; new lines that occur in the middle of string literals are condensed into a single space.

For examples:

<p>
  Welcome to <b>Vue.js</b> <i>world</i>.
  Have fun!
</p>

The whitespaces between <p> and Welcome are removed but the one between </b> and <i> and the one between . and Have are preserved thus giving us:

<p>Welcome to <b>Vue.js</b> <i>world</i>. Have fun!</p>

This seem to be much more reasonable IMO. And in this mode users will have more flexibility to better serve their different purposes.

In general, the proposal is:

  1. Keep preserveWhitespace but mark it as deprecated.
  2. Offer a new option to specify whether/how to remove whitespaces: removeWhitespace: 'with-line-break' | 'any' | 'none'.
  3. Ignore preserveWhitespace if removeWhitespace is specified.

(Still need more suggestions on specific API.)

feature request has PR

Most helpful comment

A summary of what is landed in e1abedb9:

  • Introduced a new option whitespace

    • with two valid values: 'preserve' or 'condense'.

    • defaults to 'preserve' (exactly the same default behavior as it is now)

    • ignores preserveWhitespace when a specific value is provided

    • when set to 'condense':



      • A whitespace-only text node between element tags is


      • removed if it contains new lines


      • Otherwise, it is condensed into a single space.


      • Consecutive whitespaces inside a non-whitespace-only text node is condensed into a single space.



In condense mode, the example template

<p>
  Welcome to <b>Vue.js</b> <i>world</i>.
  Have fun!
</p>

will be compiled as:

<p> Welcome to <b>Vue.js</b> <i>world</i>. Have fun! </p>

Essentially, this provides:

  • Removal of useless whitespace vnodes for better performance;
  • Smaller overall output;
  • Ability to intentionally keep inline whitespace between elements for layout purposes.

The reason for not entirely removing leading/ending whitespaces inside an element:

  • The logic is simpler and more consistent (all consecutive whitespace -> single space)
  • IMO makes the following case more reasonable:
<span>foo</span>
<span>
  bar
</span>

Will render as foo bar instead of foobar. This is also consistent with the current behavior.

We will likely use this new condense behavior as the default in 3.0.

All 4 comments

A summary of what is landed in e1abedb9:

  • Introduced a new option whitespace

    • with two valid values: 'preserve' or 'condense'.

    • defaults to 'preserve' (exactly the same default behavior as it is now)

    • ignores preserveWhitespace when a specific value is provided

    • when set to 'condense':



      • A whitespace-only text node between element tags is


      • removed if it contains new lines


      • Otherwise, it is condensed into a single space.


      • Consecutive whitespaces inside a non-whitespace-only text node is condensed into a single space.



In condense mode, the example template

<p>
  Welcome to <b>Vue.js</b> <i>world</i>.
  Have fun!
</p>

will be compiled as:

<p> Welcome to <b>Vue.js</b> <i>world</i>. Have fun! </p>

Essentially, this provides:

  • Removal of useless whitespace vnodes for better performance;
  • Smaller overall output;
  • Ability to intentionally keep inline whitespace between elements for layout purposes.

The reason for not entirely removing leading/ending whitespaces inside an element:

  • The logic is simpler and more consistent (all consecutive whitespace -> single space)
  • IMO makes the following case more reasonable:
<span>foo</span>
<span>
  bar
</span>

Will render as foo bar instead of foobar. This is also consistent with the current behavior.

We will likely use this new condense behavior as the default in 3.0.

I think this new option should also be added to vue-loader.

@a1p4ca vue-loader has compilerOptions which just passes through to the template compiler.

Hey, sorry for bumping a closed topic, but is this meant to also condense whitespace from html entities like &nbsp;?
For example, in this part, the second &nbsp; is not preserved while the first one is:

<span>
    <inline-button text="1" />

    &nbsp;

    <template v-if="someCondition">
        <inline-button text="2" />

        &nbsp;
    </template>

    <inline-button text="3" />
</span>

Resulting in the buttons rendering like: 1 23
I tried making it preserve the   by wrapping it in a span and even doing something silly like:
<span>&nbsp; &nbsp;</span>
which ended up being output as <span></span>.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

finico picture finico  Â·  3Comments

lmnsg picture lmnsg  Â·  3Comments

loki0609 picture loki0609  Â·  3Comments

seemsindie picture seemsindie  Â·  3Comments

bfis picture bfis  Â·  3Comments