Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vue3 lib not working in Vue2 projects #152

Open
jclaessens97 opened this issue Apr 8, 2022 · 9 comments
Open

Vue3 lib not working in Vue2 projects #152

jclaessens97 opened this issue Apr 8, 2022 · 9 comments

Comments

@jclaessens97
Copy link

jclaessens97 commented Apr 8, 2022

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:

Problem

I'm currently building a private reusable package for a real-world application. This application runs on nuxt2 (currently nuxt-bridge), and we're planning to migrate to nuxt3 when all the packages we need are there. The idea was to build that reusable package in vue3, and use vue-demi to make it possible to be used in our nuxt-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 in vite.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.

@jclaessens97
Copy link
Author

@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 😅

@jclaessens97
Copy link
Author

@LinusBorg answered me on Discord saying the following:

image

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.

@KaygNas
Copy link

KaygNas commented Jun 20, 2022

@LinusBorg answered me on Discord saying the following:

image

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.

@GUGIG
Copy link

GUGIG commented Jul 4, 2022

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.

@KaygNas
Copy link

KaygNas commented Jul 5, 2022

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.

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.

@GUGIG
Copy link

GUGIG commented Jul 6, 2022

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

  1. use 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 to see the breaking change
2. externalize 'vue-demi' when bundling via vite(rollup).

// 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.

@GUGIG
Copy link

GUGIG commented Jul 6, 2022

seems like there's already a better solution to the render function's compatibility issue..
check this link

@GUGIG
Copy link

GUGIG commented Jul 12, 2022

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.

@GUGIG
Copy link

GUGIG commented Oct 14, 2022

forgot to actually externalizing "vue-demi" in the public package🤦‍♂️
now it should work as intended

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants