Many organizations with component libraries are working on upgrades for their Vue 2 apps so that they can support Vue 3. Sometimes it's not easy! Ideally, you could write your code once and cross-compile it for different Vue runtimes. In this talk, we'll do exactly that. At the end, you'll have a recipe for shipping libraries that support both Vue 2 and Vue 3.
Building Backwards Compatible Vue Libraries
AI Generated Video Summary
This Talk discusses the challenges of upgrading to Vue 3 and maintaining Vue 2 and Vue 3 branches in a component library. It explores strategies for shipping libraries and using monorepos, as well as developing backwards compatible code. The speaker emphasizes the importance of testing and deploying components, and highlights the challenges of supporting multiple Vue versions. The Talk concludes with a demonstration of juggling and a discussion on the end-of-life status of Vue 3.
1. Introduction to Vue and My Experience
Hello, good morning. How many people use Vue in production? How many people are tired? Next talk is cool. I tailor my talks to the people in the room. I was building a library. How many people are in a Vue 2 to 3 migration? My case is the worst case. Are we building apps or libraries? This talk, we're going to change gears. A little bit about me.
♪♪♪ Hello, hello, hello, hello. All right, good morning. I mean afternoon. How... All right, we're going to do the hands thing. So get ready. Get your hands ready. How many people use Vue here in production? There we go. And this is to make sure you're awake. How many people are tired? Yeah, it's been long. Me, too. But I'm excited because the next talk, after mine, is really friggin' cool. So let's do mine. I tailor my talks to the people in the room. So when I originally wrote this talk, I wrote it because I was building a library. I was building a component design system. Hands. All right, I get hands. How many people are in a Vue 2 to 3 migration? Yeah. Yeah. Is it fun? No. Yeah? Yeah, no, it sucks. So my case is I think actually the worst case of a Vue 2 to 3 migration. And I'll show you my repo structure in a hot second. Are people building apps? Are we building apps or libraries? Shout it. Apps, all right. So this talk, we're going to change gears from what I planned. These were the questions I planned to ask. So a little bit about me. I had a really good introduction.
2. Working at Ionic on Stencil Compiler
I work full time in open source at Ionic on the Stencil compiler. The Stencil compiler is an agnostic way to build web components and ship them to multiple teams and frameworks. Previously, I worked at Cypress, where I built the component test runner. I have been involved in the View JS ecosystem for several years and now contribute to solid JS. Recently, I transitioned back to user space to address the challenges of View 2 to 3 migrations.
I work full time in open source at Ionic on the Stencil compiler. The Stencil compiler is supposed to be an agnostic way that you can build web components, TLDR, build web components. And then ship to multiple different teams, different frameworks. Sounds cool. So team A, B, C, and D can all write Angular, React, and View. And the design system team doesn't have to care, they can write in this little meta language. That's what Stencil does.
I've been there like four weeks. So that's all I got. Previously I worked at Cypress. I worked in open source. Have people used Cypress? Shout it. Woo! Yeah, woo. And I built the component test runner. Also did the faker JS core thing. And I've been around the View JS ecosystem for a hot second, three, four years. And I'm now a solid JS contributor. I love Reactivity. So that's me.
And then I did a brief stint in user space again. So I went from Cypress for two and a half years in open source full time to Path AI in Boston. Actually in Cambridge. I call it fake Cambridge, because I'm from the US. And you guys might all be from here. So in user space, as I call it, building apps is different. And I felt like I wanted to reconnect. I wanted to reconnect after being in open source for so long. I was like, what are the real problems people face? And the answer is, View 2 to 3 migrations. I joined in July 2022. And I've spent most of that time, up until the last four weeks, in user space, trying to migrate from View 2 to 3.
3. Challenges in Upgrading to Vue 3
I had many teams, many consumers of the component library. We needed to upgrade to View 3. This is the repo structure. We have four web apps. We have one shared Vue-lib. The FDA in the US requires you to delineate the different requirements. Each box is a repo.
So I have a lot of opinions. So my problem was I had many teams, many consumers of the component library I was working on. And we needed to upgrade to View 3. And we needed to. And this is a distinction I will make very clear why in a second.
We were a med tech company. We had all of the regulations that the US likes to place on med tech, which is less than what happens in Europe and other places in the world. But this is the repo structure. We have four web apps. Seems nice. We have one shared Vue-lib. And for each little box is a compilation step using Vite, or Webpack, or Vue CLI, or Jest for testing, or Playwright for testing. It keeps every option, because it was built by people who are not front-end developers. And that's fine.
I was like, if anybody can detangle this, I think I can do it. Again. Again. And the reason this looks like this, the reason this looks like this is because the FDA in the US requires you to, by repository, delineate the different requirements. Actually, the way this thing actually works is you take all your code for each Vue web app and put it on a USB stick. Yeah. You put it on a USB stick, and you have somebody review your code and tests. And they do it. They don't, like, cursorily like, looks good to me. They give code comments on your tests. They're like, why are you, I joked. I was like, if somebody ever gives me code comments on Cypress component tests on the thing I built, I don't know how I'm going to feel about that. But, so this is my problem. This isn't the ugliest slide. This is the ugliest slide. And you might be like, what the heck are these colors? Well, each box is a repo.
4. Challenges in Maintaining VUE 2 and VUE 3 Branches
Teams release at different cadences. Unrealistic to expect each repo to maintain long running VUE 2.7 and VUE 3 branches. VUE 3 is end-of-life too. Migration docs for VUE 2 to 3 have changed with more detail. Wanted a component library that could be actively developed with confidence. Shipped a button with Tailwind without migration effort. Created a template called Petite for component library authors.
Each color is a team. And as we know, teams release at different cadences. And so when you're thinking, you're at the bottom. I am at the VUE component library, red, I am this solo team. We had seven repositories, five teams, 25 engineers, and four products that all had separate release cycles. And that sucks. That really sucks. Because it's unrealistic to expect each repo in the middle, are blue and yellow, and then at the end, little green boxes, to maintain long running VUE 2.7 and VUE 3 branches. We didn't even consider VUE 2.6. We were just like, all right, VUE 2.7, this is where we're going to start. Because we know that that's the closest to VUE 3. There are reasons for that, find me after.
So VUE 3 is end-of-life too. What should companies do? It depends. Do you have to care about security patches as much as we did being in medtech? Maybe. If you have not looked at the migration docs for VUE 2 to 3 recently, they have changed. And they have much more detail than they previously did. And I'm talking in the last three months, two months.
So stepping back, I wanted, literally stepping back, I wanted a component library that could be actively developed with confidence. I wanted to ship a button with Tailwind. Yeah, yeah, yeah. I wanted to ship a button with Tailwind that could be developed, and then afterwards, didn't require a big migration effort from all of those nice repositories we talked about or the middle libraries. A humble goal, I thought. It was not. But I tried. I was like, OK, we're going to ship something called, I made a little template. It's open source. It's called Petite. And it's a project structure for component library authors, because ideally, actually, another audience participation situation here. Have you, during the course of your Vue 3 migration, noticed that some repositories have just stopped supporting Vue 2, and just cut over to Vue 3? Yeah? You're not.
5. Shipping Libraries and Monorepo
You feel it. Yeah. So TLDR. What I wish that we would do in the future is when we're shipping libraries, we want to make a monorepo. We'll get there. And we want to sim-link the source directory. This is my genius plan. It was not genius. And we want to reuse the build and test tooling to make sure that when we cross-compile a library, like, let's say, Vue Popper or whatever, that the tests and build tooling would test against both user versions. I'll explain why.
6. Challenges in Reusing Build and Test Tooling
So long as you write it in a way that works for both, view 2 and view 3. If you're using something like Tailwind or Uno CSS, when you compile your library down, you want to do it to both. Most libraries stop at unit tests. The test infrastructure for libraries could be improved. And you will use RuntimeUtils or BuildSteps to support multiple things. When we're developing our component library, we want to make sure it still works. Do people use ViewTestUtils? Great. Do you use the emitted function on ViewTestUtils? Don't. So let's take the counters component test. Backwards compatible means that the API contract should be the same.
So long as you write it in a way that works for both, view 2 and view 3. So if you're writing, let's say, the emit function here with update model value and input, ideally it should work. Backing, moving forward.
So we're using the Build and Test tooling. This is the most important part, is to get everybody to reuse the Build tooling. If you're using something like Tailwind or Uno CSS, when you compile your library down, you want to do it to both. You want to reuse your Tailwind config in both targets. You rely heavily on your test environment to ensure it works against these multiple user versions. And most libraries actually don't do this. Most libraries stop at unit tests. They don't ensure that the API works against target user version 2.7, target user version 2.6, 3, 3.3. The test infrastructure for libraries could be improved, to put it lightly. And you will use RuntimeUtils or BuildSteps to support multiple things, like model value, value, et cetera, these breaking changes we have. So let's make sure it still works when we're developing our library. When we're developing our component, I'm not going to say design system, because that's separate. When we're developing our component library, we want to make sure it still works. And we want to reuse the tests demo time that exercise the usage, not the internals. Do people use ViewTestUtils? Great. Do you use the emitted function on ViewTestUtils? Don't. I have opinions.
So let's take the counters component test. This happens to be written in Cypress. I worked there. I don't really care what you use. The point is that you're exercising the component by clicking on it. Line 11, where I say assertions, I'm going to show you what that looks like. Same point, slamming it home. Backwards compatible means that the API contract should be the same. So if you have a counter component, it should support v-model, regardless of the target user version. Min and max should still be numbers.
7. Developing Backwards Compatible Code
I have a repo structure with libview2 and libview3 folders. I'm going to write code that's backwards compatible. We're going to develop in view2 and view3. We have Cypress open in Canary and Chrome, where we can count up to 10 and down to 10.
Yeah. Yeah. All right, what do we got? Time? Where's my guy? He's supposed to tell me I'm fine. So I have a repo structure. I have packages, which is a PMPM workspace root. And I have two folders, libview2, libview3. Libview2 has a source directory, but it's not actually a libview2. This is technically, even though this little path thing is like libview2, it lies. It lies, because if I say, hello, view JS, and I go to view3 one, this is how simlics work, right?
We're in Counter View. It's like, hello, view JS. So you might guess what I'm about to do. I'm going to write code that's backwards compatible. Again, this was the dream for me. This is like the dream. So I called this repo template petit. It's on my GitHub. I'll show you the link at the end. But we're going to develop in view2, and we're going to develop in view3. This is Cypress, if you've never seen it. I built it two and a half years of my life. So we have it open in Canary, and we have it open in Chrome. Ignore the little Cypresses. They're Electron apps. It's OK. We're going to click on counter.tsx, and we're in view version 3.2 here. We can count up to 10. Oh, this is small. We're not doing that. 3.2. And we can count down to 10.
8. Using Counter Component in Multiple Versions
I have a demo component. This is how you should use said counter component. View 3 can go up to negative 10. View 2 should also be able to go up to 10 and negative 10. The test is failing because this component is not view 2 compatible. Let's figure it out. We have model value. Back in the day, we have value. This is going to be a lot easier to define in view 3, view 3.3 and beyond.
I have a demo component because somebody gave me the feedback that it's better to write in SFCs for demos instead of JSX. I don't really mind either way. The important part is this.
This is how you should use said counter component. You should pass it a minimax and a vmodel. Have people done things like this before? Yeah, me too.
All right, so in our counter demo, I print the view version just for kicks to make this demo look prettier. But the thing I really care about is the counter. I want to make sure the counter works across view 3 and view 2. View 3 can go up to negative 10. View 2 should also be able to go up to 10 and negative 10.
So you can tell we're in view 3 because of this tiny text here, tiny text, and this nice big text. But view 2, boop, is in 2.7. But you'll notice that the test is failing, and that's a bummer because it means that this component that we've written is not view 2 compatible. It's view 3 compatible, and that's cool. But ideally, our consumers, doo-doo-doo-doo, oh, come on, zoom. It's not zooming. I swear I did this like a million times.
OK. It's big enough now. Boop. OK, view 2, view 3. Counter component here does not work, and that's sad because we want our user stuff to work in multiple versions. So let's figure it out. You can see we have model value. This is how you define media model in view 3. And back in the day where the day is most days for most of you, we have value. And the way we're going to fix this is saying we have value. It's basically the same. And with a lot of the stuff that Evan was talking about, by the way, this is going to be a lot easier to define in view 3, view 3.3 and beyond.
9. Updating Model Value and Custom View Models
We can have prettier defined props, syntaxes. And we're updating model value, but what we really want to do is update. The way you write custom view models? Input. And we're emitting model value down here on increment and decrement. And so now, if I reload, I should be able to click.
We can have prettier defined props, syntaxes. And we're updating model value, but what we really want to do is update. What do we want to do? How do we make a view 2 compatible? Update value.
The way you write custom view models? Input. And we're emitting model value down here on increment and decrement. Speak up. What are we doing? We're doing input. And I do input here. And again, this is the ugliest way to do this. This is terrible. But everybody here understands abstraction. And there are patterns to make this look less ugly. Great.
And so now, if I reload, I should be able to click. Can't click. This is what I get. Oh, it's because I didn't do change. Isn't it change? It is change. Nope. Mm. Oh, Jessica. I wrote this demo this morning. Yep. Shit. If anybody wants to pair program with me right now, feel free to yell out what I should do. Change? Change? Oh, I remember. I remember. You know what it is? I'm only taking counter here. props.value, or zero. Reload.
10. Troubleshooting Model Value Defaulting
Nope. And it was right. OK, I was right the first time. Change. Sad Panda. I'm usually very good at this, actually. And I'm pretty decent at live coding.
Nope. And it was right. OK, I was right the first time. Change. Sad Panda. I'm usually very good at this, actually. And I'm pretty decent at live coding. It's one of the few skills I prize in myself. And I practice this, too.
Save it. Run it. Nope. Nope. I think this is the first time I've ever had it go wrong. Wait, what? Model value is defaulting to zero. Model value is defaulting to zero. Model value is defaulting to zero. I think it's something to do with the counter change events. Input and default. Hmm? Input and default. Input and default? OK. Input and default. Put the default back. This is line nine. Line nine. Put the default back. You got rid of it in two lines. You know what? I did it earlier. I should have done this. I got nervous onstage.
Testing and Deploying Components
I took my correct version. Let's add the data test ID real quick. View two and View 3 versions work. Harry breaking change number one is vModel. Harry change is to delete the wrapper divs. View 3 gave us partials. View 2 did not. Vite is sad because component templates should contain exactly one element. Test infrastructure helps catch these issues. It's Q&A time. Deploying packages, support Semver, breaking changes between components and versions, no coupling to view's next major version.
And I think I took my correct version. Yeah, I took my correct version. We can figure out what it is later. But I had this in my back pocket the whole time and I forgot about it. That sucks.
All right. Let's add the data test ID real quick. Test ID is counter. Count. And here we go. View two, please. Woo! Thank you. I'm actually sweating. I am physically sweating. That was so stressful. All right. And then the View 3 version also works. And one of the only things that Harry, Harry breaking change number one in View 2 to 3 is vModel, right? Harry change, not so Harry, it's pretty obvious, is to delete the wrapper divs. Yeah? Because View 3 gave us partials, right? Template partials. View 2 did not. So View 3? View 3.2.39? Very happy. View 2? Very sad. Vite is very, very sad right now because component templates should contain exactly one element. And this is the kind of thing that you can catch with a test infrastructure like this.
So backing up the bus, it's Q&A time. I am over time. I'm going to zoom very quickly through this. Deploying your packages, I want my library to support Semver. I want to have breaking changes between components, between versions. And I don't want to couple to view's next major version.
Challenges in Supporting Multiple Vue Versions
Some people stop supporting Vue 2 and only focus on Vue 3. I wanted to deploy multiple versions of my monorepo and target different versions of Vue. I implore library authors not to break their users in the future. I implore application developers to understand the difficulties. Library authors should invest in tooling for multiple framework versions. Thank you to those who helped me with the architecture. Reduce the number of layers in application development. The toolchain is the challenge, not the Vue runtime. Vue and Vue 3 migration is stable.
You may have noticed in the ecosystem, some people just change and stop support for view two. They were just like, no, my library only supports view three and up. And they did that with a major version bump. That's an option, one package at library slash view. Or you can do end packages at library slash view two or view three or solid. But I wanted to deploy multiple versions of my monorepo and target different versions of view.
So I would implore library authors not to break your users in the future. I would implore application developers to understand how difficult it can be. It really sucks. And I went through that as a user, right? I changed jobs to really truly experience the problems that we're all facing. And I would implore library authors to invest in tooling that lets you build, test, and deploy libs against multiple framework versions. If you want to see the repo structure, feel free to look at it.
That's the most valuable thing in there. Thank you all. Thank you to the following people for helping me talk through the architecture in, oh my goodness, it must have been July when I first started the pain. Daniel Rowe, who we all love, Thorsten Lindberg, been a core team member for ages. And Johnson Chu. Shoo, I'm going to mess it up. Bill Vallar does a lot of work for the Vue community and answered a ton of questions for me. Things like, how do you write a backwards compatible defined V model? Right? So you don't have to write all that ugly code I was writing that I messed up for, like, 10 minutes.
Tidbits of advice for application developers. Reduce the number of layers. Why? If I wasn't in a med tech company, I wouldn't have had so many boxes. I would've been like, monorepo, ship it, because that would make CI much easier. The toolchain is what gets you. It's not the Vue runtime. Vue runtime, I had up in a day. It's the toolchains and differences in the toolchain between VT, Vue CLI, Jest, et cetera. That's what really gets you. Vue and Vue 3 migration? Stable.
Audience Appreciation and Juggling
Everything else. That's where the pain is. Thank you. Thank you so much. People want you to continue doing LiveCoding because you're amazing. And not just LiveCoding. You can juggle too. Show us your favorite juggling pattern.
Everything else. That's where the pain is. So good advice. That's it. Thank you. Thank you so much. Really, really enjoyed that talk as well.
Also, I must say, the top question when we came in was people asking you please continue doing LiveCoding, because we've seen you do it before. You're amazing. And continue being brave. So give a round of applause for LiveCoding. And not just LiveCoding. Like I said, you said LiveCoding is one of the only talents you had. That's a lie. You are holding one can of water. I went and invested in a couple of extra ones.
Because the next question is, what is your favorite juggling pattern? And I know you can juggle. You know you can juggle. I think you should show it. Do you think she should show us some juggling? Yes. All right, go on. All right. So which of these? You should have like the music, guys, just to lay a beat that you can juggle to. Somewhere, something scrambling. See if you can lay a beat. You can do it. The Venn Diagram of nerds and juggling is a circle. I do this at MIT. I've been doing this for four months.
Juggling and Vue 3 End of Life
I've been juggling for four months. I usually bring my juggling equipment. Let's start with two cans. Juggling competitions allow three tries. Vue 3 being near end of life was surprising after watching the Hero Devs guy's talk.
I've been doing this for four months. And it's very hard to do with these cans. I usually bring my juggling equipment. So I'm going to do two.
We're going to start. Make sure I'm not burnt out from just giving a talk where I failed. Oh, it's LiveCode. Two. Seems legit. I usually do clubs. I don't like balls too much. You can do more interesting patterns. They're easier. You want it? You got it. You got it. Make some noise. OK, OK, OK. I've got to tuck your tail, get the posture. Nope. So in juggling competitions, you get three tries. It's true. One, and then one more. Thank you very much. That is awesome. Very, very impressive. I love how talented all our speakers are, as well.
One thing that I did want to give you a chance to maybe go in your slides. On one of your slides, it said that Vue 3 was near end of life. And I genuinely was like, wait, what? You just watched a talk from the Hero Devs guy, who was like, end of life, Vue 3, risk level at 97 out of 100. Yeah.
Vue 3 End of Life and Library Support
Vue 3 is end of life soon, December 2023. It's 2023 now. December 2023. Many people don't have to upgrade to Vue 3. Consider not. Look at your use case. Fair enough, now we know. That is definitely something I'm going to Google when I come off the stage.
No, definitely, I get that, I get that. And just to confirm, are you saying that view three is end of life, not view two? View two is end of life. Oh, yay, yay. Yeah, yeah, yeah. Did I fuck it up? Holy shit. So the slide says view three is end of life. I asked the question. You were like, yes, it is end of life. I'm going to hide behind this sign.
Dependency Management and Vue Demi
As a library author, write tests before shipping to ensure compatibility. For application developers, monitor and use canary deploys. Consider using Vue Demi for non-SFC projects. Avoid raw render functions and use raw composition API and refs. Template refs are useful for styling. Check out Vue use on GitHub for examples. Find Jessica upstairs, at the after party, or on Twitter.
No worries. It's been a long day. It's been a long day for sure. I was trying to keep the hype. I was fact checking. This is my job as moderator, to fact check. Well, anyway, let's get back on track.
So we've got another question from Alvaro, which is, how do you keep track of NPM dependencies for each version and keep everything compatible when these dependencies change their API? For example, VT5 or VT5 2 and 3 have breaking changes in your temp that needs to be different? So the thing is, I need clarifying information with that. So as a library author or as an application developer? That's the first clarifying question.
So as a library author, you should be writing tests before you ship. Because you have a fixed version that you need to be compatible with. As an application developer, assuring the quality of your app is very different, and you have things like monitoring and canary deploys to make sure you're good. But as a library author, you need to be writing tests where you scaffold a Vuetify 2 and a Vuetify 3 app and see if your stuff still works. See if your component library still works. If you're building on top of Vuetify, for instance. That's awesome. Really, really good advice.
And as well, someone else has asked, have you thought about using Vue Demi for different library versions? Yeah, absolutely. Vue Demi is great if you're building anything that does not have SFCs and needs a compile step in between. We tried using Vue Demi in the migration I led. And Vue Demi just does not support SFCs. And every single box that I showed on that screen exported some kind of SFC. If you're writing library code, please, just try not to make it headless. Like make it headless. Don't use raw render functions, because those break a ton. And then, yeah, just raw composition API, if you can get away with it, and refs. So somebody mentioned template refs earlier. Template refs would be the way to go if you're trying to do any sort of styling. Use Vue use. Like literally go to the Vue use source code and see how they do it. That is the best way. Like their infinite scrolling component, the headless infinite scrolling component of Vue use, those kinds of patterns. That's what you want to look at. And go on GitHub. Look at the source code. Awesome, and if people want to find more about you, ask more questions, or maybe see more juggling, where can they find you? They can find me upstairs for the next, I don't know, 40 minutes. Maybe at the after party and on Twitter. Awesome. Let's give it up for Jessica.