Vue: Always allow Babel to transform expressions in templates

Created on 7 Aug 2017  路  27Comments  路  Source: vuejs/vue

Version

2.4.2

Reproduction link

https://github.com/Justineo/vue-esnext-in-template

Steps to reproduce

vue init webpack esnext-in-template

src/components/Hello.vue:

<template>
<div :class="{ a: true, ...b }"></div>
</template>

<script>
export default {
  data () {
    return { b: true }
  }
}
</script>

(babel-preset-stage-2 is already enabled after initializing with vue-cli.)

What is expected?

The class value should be a b.

What is actually happening?

Error compiling template:

- invalid expression: :class="{ a: true, ...b }"

vue-template-compiler seems to be validating the expression before it goes through Babel with trying to evaluate new Function(exp). This makes ES features that can be used in templates limited to those Node supports and causes confusion: I can translate spread operators for arrays both in templates and elsewhere but I can translate spread operators for objects anywhere except in templates. I think this might not be intentional so I mark this as a bug.

improvement

Most helpful comment

Right now optional chaining has been officially part of ECMAScript 2020 (Chrome 80), therefore I think maybe we should reconsider this feature request again.

All 27 comments

As a workaround for now you can use latest Node with --harmony flag to build it.

@nickmessing

Thanks for the work around. But there are still some problem for this solution:

  1. It's not possible to always include the workaround so it made the affected projects less portable.
  2. Future ES features will always land as Babel plugins before in Node, even with --harmony flag.

@Justineo, I personally would not like to change this mechanism because if we move that away from babel-template-compiler it would result in runtime errors which make it hard to track down mistakes in templates. Another way is to try and parse it with babel instead of new Function() but that is slow and I need to research actual performance impact.

@Justineo, unfortunately, using babylon to parse expression breaks our current solution since it parses keywords successfully (ex: (do + 1)) and adds extra dependency. Looks like it's not an option and we are limited to use only code that is supported by node in expressions. --harmony solves the spread problem at the moment.

Thanks Nick. I think it's better to point this out in the docs to prevent confusion. I'll make a PR later if you don't mind.

i have learned a lot

Closing this issue, doesn't seem that we can consider this further.

@nickmessing Any reason we couldn't just use babel-node instead of node for the build step in the Webpack template?

@chrisvfritz I wouldn't recommend using babel-node, it has some performance overhead

@posva How much slower are we talking about though? I think predictable behavior is worth slightly slower builds - or even moderately slower builds. Isn't that already a trade-off we're choosing when we write tests? Or when we include a build system that compiles to more predictably supported JavaScript? 馃槈

My concern is that adding a note to the documentation won't be sufficient. When people encounter this problem, it will be nearly impossible to find that note and they could easily waste a day trying to figure out why this feature isn't working correctly - or probably more likely, just give up, assuming the Vue template compiler is buggy or lied about supporting Babel features.

If it _really_ isn't feasible to find a consistent workaround, I think we'll need some other way to communicate to users, like catch errors in the Vue template compiler to provide a warning about this behavior.

@chrisvfritz I haven't tested using babel-node with the webpack template (nor do I know if it's possible). I cannot remember what it was, but I keep a slightly bad memory of using it for node projects.

@posva, @chrisvfritz, it's not an option here anyway since it doesn't transpire code from new Function, I think I know a way to make it work with babylon but only if we make it a dependency of template compiler, can this be considered?

@nickmessing @posva While we investigate, would either of you mind if we reopened this for now, just to make sure we don't forget about it? Since it's not really a problem documentation can solve, I'm thinking we should only close when we can do one of these:

  1. Prevent the issue completely (in our templates, if not in vue-template-compiler directly)
  2. Provide the user a warning/hint when an error is raised that may be caused by an unsupported feature in node
  3. As a last resort, decide neither is feasible and that encountering this difficult-to-diagnose behavior will just be one of those horror stories Vue users tell late at night, around the campfire 馃槄

@nickmessing I think most users aren't too concerned about the size of dev dependencies, so that would be alright for me if it would make things work as users expect. 馃檪

@chrisvfritz, unfortunately I don't know how to have it without bundling Babylon into runtime build :-S

@chrisvfritz No worries 馃槅

@nickmessing Ah, I see. Crazy idea then: could we avoid pulling babylon into the template compiler with something like below at the beginning of the checkExpression function?

if (envIsNode) {
  exp = eval('require("babylon")').parse(exp, userBabelConfig)
}

That's closer to pseudocode than a real solution, but do you think the strategy I'm getting at might be feasible?

@chrisvfritz, actually yes, I'll try that way.

With spread operators natively supported in Node 8.3+, I'll close this as a wontfix for now.

In order to use Object rest spread in templates, you need to:

  • Use Node v8.3+

  • Configure buble option for vue-loader:

    buble: { objectAssign: 'Object.assign' }
    
  • If targeting any IE, make sure to include polyfill for Object.assign.

In 2.6 we will likely coordinate the configuration so that in a vue-cli project you will be able to use object rest spread without any configuration.

@LinusBorg @yyx990803 During Vue CLI 3 builds, perhaps we should also emit a warning for older versions of Node.

What about optional chaining and other babel plugins in the templates?

when the vue-loader support optional chaining ? This is a very important feature

Vue is currently transpiling template with a fork of Bubl茅 and there's no plan to support extra syntax. You can now use a method or computed property in the <script> section where optional chaining can be supported with a Babel plugin because Vue Loader handles the script part with Babel.

@Justineo it is cumbersome. it is not like vue style (simple)

Hi guys,

I want to use chaining operator in template part, is there any solution to do that ?

My problem is that i'm in a "v-if" hell! I need to check every nested structure with v-if to avoid bugs and errors in case of null value ... so this operator would be nice to avoid too many v-if.

I could use the operator in a computed properties, but my point is to clean my code of useless syntax, and calling a method with argument is bigger than writing a v-if...

Right now optional chaining has been officially part of ECMAScript 2020 (Chrome 80), therefore I think maybe we should reconsider this feature request again.

I want to use chaining operator in template part too, is there any solution to do that ?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

fergaldoyle picture fergaldoyle  路  3Comments

aviggngyv picture aviggngyv  路  3Comments

paulpflug picture paulpflug  路  3Comments

bdedardel picture bdedardel  路  3Comments

paceband picture paceband  路  3Comments