No createElement warning in @vue/compat or docs on how to fix
Version
3.2.1
Reproduction link
https://github.com/dra11y/compat-buefy
Steps to reproduce
- 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
- Compile the project. Warnings appear as they should, but the project compiles without error.
- Load page in browser.
- Receive Error in vue.esm-bundler.js:10082 Uncaught TypeError: createElement is not a function
What is expected?
@vue/compat should either:
- error out when createElement is detected, because it is a breaking change; or
- 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.
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
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.
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.
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.
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?
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.
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.
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