Open jclaessens97 opened 2 years ago
@antfu do you maybe have any pointers on where the issue may be? Don't want to ping you unnecessarily but I'm kinda lost at this point 😅
@LinusBorg answered me on Discord saying the following:
As said, might be useful to have in the README.md
to avoid future issues about this topic 😄
I'm coming back on it on Tuesday.
@LinusBorg answered me on Discord saying the following:
As said, might be useful to have in the
README.md
to avoid future issues about this topic 😄 I'm coming back on it on Tuesday.
It might not be correct that "vue-demi is not for vue2/3 component", you can use render function to make the component work for both vue2/3.
import {h,defineComponent} from 'vue-demi'
export default defineComponent({
//...
render(){
return h('div', 'hello')
}
})
In your situation, your component is a sfc, and sfc need compiler to complie to js, where vue-demi doesn't involve in, also there are gap between compiler of vue2/3, that's the reason why the lib you built don't work in vue2.
My work around is build two dist separately for vue2/3, here is the template repo, hope it will help.
you can use render function to make the component work for both vue2/3.
I spent some time with vue-demi to make a tiny vue component library and managed to make it compatiblie with both vue 2 and 3 by manually fixing the build result, which is not ideal.
It seems like when building the library to production via vite(rollup), the bundler is replacing "vue-demi" that I wrote in my component file(.ts) with "vue", and removing isVue2 conditional statements, which, to me, is not understandable.
Initially I wrote my component library w/ render function(h) like you mentioned @KaygNas , and compiled it with vite(rollup). But in the build result files, vue 3 APIs were being imported from "vue" instead of "vue-demi", which caused the incompatibility when being used in vue 2 app.
I manually fixed the build files and managed to make it compatible w/ vue2 & 3 1) replaced "vue" with "vue-demi" 2) used "isVue2" to check if the end user(app) is vue 2 or vue 3, and wrote two render functions
// /lib/my-component.es.js
import { defineComponent, ref, isVue2, /*...*/ } from "vue-demi"
//...
const vm = getCurrentInstance();
const h$1 = h.bind(vm);
if(isVue2) {
return h$1(/*compatible w/ vue2 syntax*/)
} else {
return h$1(/*compatible w/ vue3 syntax*/)
}
When I write "vue-demi" inside my component directly (not in the build file), vite replaces it with "vue" during build..
And when I try to write the conditional statement directly inside my component(not in the build file), vite makes them go away and only leaves vue3-relative codes inside the resulting build file(this is probably because I initially setup my library project with vue 3).
The simple example(use-mouse) vue-demi gives us seems like only utilizes tsc when building? which seems to be the reason why it works because the tsc doesn't try to replace "vue-demi".
If there is a way to stop replacing "vue-demi" with "vue" and removing conditional statements such as the one i wrote above, i think this problem would be solved.
you can use render function to make the component work for both vue2/3.
I spent some time with vue-demi to make a tiny vue component library and managed to make it compatiblie with both vue 2 and 3 by manually fixing the build result, which is not ideal.
It seems like when building the library to production via vite(rollup), the bundler is replacing "vue-demi" that I wrote in my component file(.ts) with "vue", and removing isVue2 conditional statements, which, to me, is not understandable.
Initially I wrote my component library w/ render function(h) like you mentioned @KaygNas , and compiled it with vite(rollup). But in the build result files, vue 3 APIs were being imported from "vue" instead of "vue-demi", which caused the incompatibility when being used in vue 2 app.
I manually fixed the build files and managed to make it compatible w/ vue2 & 3
- replaced "vue" with "vue-demi"
- used "isVue2" to check if the end user(app) is vue 2 or vue 3, and wrote two render functions
// /lib/my-component.es.js import { defineComponent, ref, isVue2, /*...*/ } from "vue-demi" //... const vm = getCurrentInstance(); const h$1 = h.bind(vm); if(isVue2) { return h$1(/*compatible w/ vue2 syntax*/) } else { return h$1(/*compatible w/ vue3 syntax*/) }
When I write "vue-demi" inside my component directly (not in the build file), vite replaces it with "vue" during build..
And when I try to write the conditional statement directly inside my component(not in the build file), vite makes them go away and only leaves vue3-relative codes inside the resulting build file(this is probably because I initially setup my library project with vue 3).
The simple example(use-mouse) vue-demi gives us seems like only utilizes tsc when building? which seems to be the reason why it works because the tsc doesn't try to replace "vue-demi".
If there is a way to stop replacing "vue-demi" with "vue" and removing conditional statements such as the one i wrote above, i think this problem would be solved.
Do you tell rollup that vue-demi is an external? Try it then rollup might keep the vue-demi import statement untouched. Check the docs here.
Do you tell rollup that vue-demi is an external? Try it then rollup might keep the vue-demi import statement untouched. Check the docs here.
This works woderfully! My lack of understanding of how rollup handles external dependencies seems to be the cause of the problem i encountered, apparently 🤦♂️
After adding vue-demi as an external in rollup option, everything works as I initially expected! I confirmed that my library works in both vue 2(2.6.14) & vue 3 apps.
I'll try to publish my library package to public npm registry & github maybe in this weekends to share further details of what I've done & leave a link to it's repo in this comment.
To summarize what I've done w/ @KaygNas 's advise
render function
instead of SFC or jsx
to write compatible render logics for both vue 2 & vue 3.
-> there're some breaking changes in render function's syntax between vue 2 & 3. so either you should a) use isVue2
or isVue3
to write two separate render functions for each compatible versions, b) mix two syntax like below:
// vue 2
h('div',
{ class: 'box', attrs: { id: 123 } },
'text inside div tag'
)
// vue 3 h('div', { class: 'box', id: 123 }, // flattened 'text inside div tag' )
// how i did it.. doesn't seem ideal though but it surprisingly works h('div', { class: 'box', id: 123, attrs: { id: 123 } }, // added attrs property from vue 2 syntax 'text inside div tag' )
look [here](https://v3-migration.vuejs.org/breaking-changes/render-function-api.html#vnode-props-format) to see the breaking change
2. externalize 'vue-demi' when bundling via vite(rollup).
```javascript
// vite.config.js
//...
rollupOptions: {
external: ['vue-demi'],
// ...
}
// ...
But this begs another question that is there any reason why there's no mention in the doc about externalizing vue-demi in vite(or rollup) config file in the first place, since it seems like it's rollup's default behavior not to include 'vue-demi' namespace in build result if vue-demi is not specified as the external dependency. May be it's just a common knowledge that I should've known 🤷♂️ Or may be it is too certain-buildtool-specific to have a place in the doc? But it would be nice to see a simple section to inform in regards to this.
seems like there's already a better solution to the render function's compatibility issue.. check this link
I published a public repo & its npm package. repo link It's a demo package to showcase how I managed to make a vue component library compatible with both vue 2 & 3.
forgot to actually externalizing "vue-demi" in the public package🤦♂️ now it should work as intended
First of all, I saw a couple of issues that involved more or less the same issue, but I tought it would be useful to group everything into one issue, so we could hopefully try to solve it.
Related issues:
145
136
117
129
Problem
I'm currently building a private reusable package for a real-world application. This application runs on
nuxt2
(currentlynuxt-bridge
), and we're planning to migrate tonuxt3
when all the packages we need are there. The idea was to build that reusable package invue3
, and usevue-demi
to make it possible to be used in ournuxt-bridge
app.At first I just tried building the library and use it in a separate vue 3 app. With Vite it was really fast & easy to make it work 🎉
However when I tried importing it in our
nuxt-bridge
app I came across several issues:1) When externalising
vue
dependency in vite, I got a list of warnings that some functions are not exported by vue. This is what the most issues are about.2) When I don't externalise
vue
invite.config.js
, I just get an empty component. In my repro I don't get any warning. I just see an anonymous component in my dev tools. In my production nuxt-app, I see the following error:no template or render function is defined
. The component seemed to be imported correctly, it just doesn't render any HTML.3) I saw in other issues that the solution might be to externalise
vue-demi
as well, but that doesn't seem to make any difference for this.4) also tried using
npx vue-demi-fix
&&npx vue-demi-switch 2
commands, but these also doesn't seem to be changing a whole lot.I also saw that in #117 , @koooge researched into the issue a bit and saw that it might be due to a breaking change in @vue/core.
That's when I decided to build a repro with a vue2 app (@vue/cli), vue3 app (vite) and a simple component in a separate lib.
Repro
https://github.com/jclaessens97/vue-demi-vue2-broken-repro
I hope we can look into this issue and try to come up with a solution to be able to make vue3 components compatible for vue2, until everything is vue3 ready.