Subscribe on changes!

No createElement warning in @vue/compat or docs on how to fix

avatar
Aug 10th 2021

Version

3.2.1

Reproduction link

https://github.com/dra11y/compat-buefy

Steps to reproduce

  1. Switch a Vue 2 project that uses render(createElement ...) in one of its component dependencies (e.g. buefy) over to @vue/compat/dist/vue.esm-bundler.js
  2. Compile the project. Warnings appear as they should, but the project compiles without error.
  3. Load page in browser.
  4. Receive Error in vue.esm-bundler.js:10082 Uncaught TypeError: createElement is not a function

What is expected?

@vue/compat should either:

  1. error out when createElement is detected, because it is a breaking change; or
  2. provide a shim so that it works, but output a compiler warning. The docs should also explain this change and how to handle cases such as changing render(createElement, context) {} to, what? render(context) ... and calling h() to return output instead of createElement().

What is actually happening?

@vue/compat totally ignores all the calls to createElement in components and does not warn about them. Then, the app just breaks in the browser.

avatar
Aug 11th 2021

createElement/manual render functions are supported.

The problem lies with the buefy dependency. it defines a render function as such:

render: function render(createElement, fn) {
  return this.genNavbar(createElement);
}

Note the second argument - which does nothing. Because in Vue 2, render functions don't have a second argument.

We use this (arguments.length >=2) as a shortcut way of detecting wether a render function is a Vue 2 render function, or has already been compiled by the the Vue 3compiler.

We also can't really warn about this.

Solution:buefy woudl have to remove the superfluous second argument and publish a new version with that fix.

But generally: We recommend waiting for Vue 3-compatibly versions of such bigger component libraries as they often use Stuff under the hood that the compat build can't fix. That might not be the case for Buefy, just be warned.

Relavent docs:

https://v3.vuejs.org/guide/migration/migration-build.html#known-limitations

avatar
Aug 11th 2021

Why doesn't @vue/compat throw an error or warning since the first argument is createElement? And why does it fail anyway? @vue/compat in mode = 2 is supposed to be compatible with Vue 2.

avatar
Aug 11th 2021

In Vue 2, the first (and onnly) argument is createElement. in Vue 3, it's not. so @vue/compat has to handle the Vue 2 render function differently.

But here, it doesn't because it thinks the render function is a Vue 3 render function - because it uses >= 2 arguments.

avatar
Aug 11th 2021

So in other words: bueify wrote their render function in way that makes it look like a Vue 3 render function to @vue/compat. And it's because of this unnecessary/dead second argument.

avatar
Aug 11th 2021

Does Vue 3 ever have createElement as an argument to render function? This is inconsistent with the docs, which suggest that render has NO arguments. When would render EVER have >= 2 arguments?

avatar
Aug 11th 2021

When it's the output of a template that's been compiled with the Vue 3 compiler. Go to sfc.vuejs.org and check the "JS" tab on the right.

And no, it never has createElement as an argument, that's the point. In Vue 3, createElement = h, which is imported from 'vue', not a function argument of render - also visible in the render output on the sfc playground.

avatar
Aug 11th 2021

Why then was createElement removed from Vue 3 in the first place? If it wasn't broken, why did it have to be fixed? There are many, many libraries that relied on this API, and for @vue/compat to not be able to handle this scenario is hurtful to users trying to get on an upgrade path.

avatar
Aug 11th 2021

At the risk of repeating myself. We do handle it. If the Vue 2 render function has the correct format, which is:

render(createElement) {

}

If however you like to add arguments to functions that do nothing, like this, then we do not support it:

render(createElement, argumentThatDoesNothingBecauseItsAwaysUndefined) {

I don't see any reason to do that ever (It was likely just an oversight in Buefy), so - we do support properly written Vue 2 Render functions