Creating a new Vue-based meta-framework from scratch with live coding.
Building for the Edge - Crafting a Next-Gen Framework
Vue.js London 2023
We can move on. So I am a member of the Next Core team along with a number of other folk here. You've already heard from Lucy. Sebastian is going to speak in a moment. Alex as well. I'm also involved in a number of other projects like Magic Regex, Fontaine which makes your layout shift disappear if you have custom web fonts. Elk which is a client for Mastodon and I have a website as well. So feel free to check out any of those that may interest you. This is my milieu. If you ever message me on Twitter or Discord or whatever, I'm probably sitting at this desk. And one of my three cats is probably stopping me from working. I don't know if you've ever experienced that you have changed your priorities for the day based on what your pet is doing. Because I 100% have. Like there are times when I just cannot code because I actually have a cat here. So the only thing I can do is read through GitHub issues or something like that. Sorry, I'm losing track. So I help build Nuxt which is a framework for building web apps. And it's built on top of vue, of course. Can I have a show of hands who here has used Nuxt? Okay, well that will skip the next ten slides. So recently we had a significant opportunity to rethink what Nuxt was about. What we would do with it. The core philosophy, the idea that you would be able to get started with best practice built in and customize as you went is still there. But a lot of things were changing. So the move from vue 2 to vue 3, there was a breaking change in PostCSS, huge breaking changes in webpack. At the same time, just as we were about to be all ready to go with webpack, Evan, for goodness sake, releases vite. What was that about? I'm a big fan. But lots of opportunities to rethink. And alongside those we had philosophical changes, like moving from a sort of singleton approach where you sort of have everything defined centrally and available to you, to a composable approach where you can actually pull in features that you want and need and actually only those get included in the bundle. An idea that you can actually share logic between components. Lots of stuff. That really made us think about what we wanted Nuxt to do. And I'm not going to talk about a lot of that, actually. I'm going to focus on the next piece, which we did, which was actually rethink the server. And how it was that we could write a server that would be designed for the serverless world. For a world of edge computing. For a world where you don't necessarily have a Docker container just always on or a server that's just constantly ready to return responses. But maybe you might need to fire something up. We moved, by the way, in Nuxt 2 from 300 milliseconds cold start to 5 in Nuxt 3. And we moved from 52 meg of bundle size to 3 in server node modules in Nuxt 2 to Nuxt 3. So there are huge changes we made. But in particular what I want to talk about today is I want to show off Nitro, which is the name we've given to our new server. We've released it so anyone can build on it. And I want to show you what it would look like to build Nuxt from scratch in a few minutes. I want to really very much try and do this. And I apologize as possible. I might go into my Q&A. I'm going to build Nuxt from scratch on top of Nitro. Powered by lots and lots of tools, which I'll show you as we go. So here we go. Let's dive in and build a framework powered by Nitro. So the first thing to do, this is an empty repository basically. Nothing much there. I'm going to install Nitro and H3, which is the server framework, a little bit like Express that we built on top of that. And start it. OK. So off the top of our head we have a web server that's running. Let's go to localhost 3000. Let's create a root. Nitro and Nuxt, we have very much the idea that we want to be as minimal as possible and as intuitive as possible. So we can do something like this. And actually, if we hit that endpoint, I'll hit it actually just in this terminal here, which seems to be tiny. If I hit localhost, what was that? Foo. I get back JSON response. I just return an object. We also as much as possible try and auto-import things. And we do that in a pattern that we started with Nuxt. We provide our own TS config to let your editor know, your IDE know what's available, both in terms of everything that we make available, whether that's type aliases, buffer paths, or making your editor know about what is auto-importable and what isn't. We can also do really cool things like this. Say I, let's see, duplicate this as another handler. I can actually get type safety on fetches across my app, so it knows the roots that exist. It knows the return types for that data. And so I could do something like this and actually get full type safety. And it's quite fun. And what you also notice, might not, is that this $fetch thing, when used in a server context, actually doesn't hit the network layer. It just makes a function call. So it's as much as possible we're trying to make it as performant as we can. So it's a bit nuts as it happens when you run that fetch call, it will make a request to the server, so it works isomorphically. Okay, so we've got a server up and running. That's just sort of hot reloading in the background as we make changes. I've deployed websites that are purely server-focused and using just Nitro and it works nicely. But let's do something view-related. Let's add a handler that's going to handle all non-server routes. So we have maybe some server routes like bar and foo, but maybe we also want to sort of render a view app, some html. And so we might do something like this. We're going to render some html. And if we hit that, some other page, you'll see html comes back. But that would be really time-consuming if I just typed everything out. Let's do this with view. So let's install view and a vite plug-in for it. And let's create a view app. So this will just say, let's see, I'll actually import something to make it a little bit more beautiful. I'll just say, hi, vue.js London. And so really what we want to be able to do is import that and render it to string and return it. So we'll do that. So we'll get create app from view. We need to be able to render it to a string. So we'll do render to string. And we also need to import the app itself. And basically, what we want to be able to do is create the app. We want to be able to render it to string. Ah, yes, Copilot. When it's working well, it's really good. And then we want to return that. So we basically want to return it here. And then we've got a bit of html going on. It's not going to work because Nitro won't understand vue. So we need to add some configuration. And we can do that like this. Oh, typescript can help us here. We want rollup config, and we're going to pass a plugin. And I'm cheating, this is not really what you should probably do. But I'm going to use a vite plugin. Just pass it in like that. Let's try that. Yay, it's working. We've got some html coming back. How about that? You're all like, hey, come on, Daniel, we do this all the time. So we probably need a template to put that in. So we'll say, hey, London. And we'll grab that text here, just a basic thing. Put that in as our, there we go. So we have a template now. And really, we want to render more than just the vue app is going to render back, because all it's going to render is the components. We want a whole web page. So we're going to use a feature of Nitro called the storage layer. The Nitro storage layer is a KV store that is vendor agnostic. So it works with Redis, with memory, it works with Vercel KV, it works with databases on the edge like PlanetScale, cloudflare, KV Storage, lots of other things. But it also works with the file system. So we're going to pull in this index.html file from here. Use storage. And we're going to get root index.html. And that we can just return template replace. And what did I put in there? This. Okay, so that should render me a whole page. Great. So it doesn't look any different, right? Still the same thing, but it's actually running a full html site, which is great. Now what we would really like to do is have this not just be server rendered. We want it to work on the client side too, so you can have interactivity. So things can actually work well for you there too. So let's do that. We're going to need to add vite. There we go. So we're going to need to change the Nitro config a little bit here. And we're going to use something called a dev handler. And that is we're going to basically need to initialize vite to run on the server as well. So we're going to give it a path, which will be underscore, underscore vite. So we know it's meant for vite and not anything else. And we're going to give it a handler. We're going to use something called a lazy event handler, which means that the first time it's hit, it will initialize, but it won't initialize before that. There are a lot of really useful helpers like this added by Nitro throughout your app. So if you're using Nuxt, you can do the same. And you're telling me I've been talking for 15 minutes. Unbelievable. Unbelievable. OK. So we're going to create a dev server here. We'll create it. We'll make it a custom app type. We need to import this. We'll make it a custom app type. And it will need to have middleware mode. And I think we'll need to pass a base as well. Great. And we'll use another utility here from H3 to get our middlewares. OK. It seems about right. Let's see if that's going to work. We should be able to hit vite client. OK, and it's returning us something. So thank you. So I think we should be able to add a script here. And give it a vite client. And I think that should load us up. A vite server. Connected. Excellent. So we have vite working. But what we don't have is any kind of hot reload or anything. It's not really connected. All it's doing is the server's there. So let's move this out into a different place. Let's move this into, we'll call it app, and call this the server endpoint. We need a client one too, which will handle whatever's going to happen on the client side. We're going to use create ssr app, which will hydrate a view rendered on the server side. And we'll do the same thing. We'll create the app. But then we'll just mount it like this. What's going on there? Right. OK. Seems good. And we'll have to add the same to our script. So now we have this. OK. No. Not working. Let's try that again. Oh. Why isn't it working? Because I moved into some other folder. Let's configure that. So we'll need to tell Nitro that this exists. So we have this new handler called, we want it to capture everything. And the handler is going to be in app client, our server on the Nitro side. It's saving as I type, so it dies. Oh. Error. That's fine, actually. We just need to tell vite that it should process view as well. For goodness sake. OK. There we go. Let's see. Is it really working? Or does it just look like it is? We do, it seems. Amazing. So there you go. We've built Nuxt in a few minutes. And tragically, I don't have time to show you any of the, well, I'll show you a few things. Show you a few things. But I'm aware we probably do need to wrap up. And basically we have vite set up. The whole thing is powered by Nitro under the hood. If you're wanting to explore Nitro, it is possible to build a framework on top of it. So analog JS, which is a framework for angular, is built on top of Nitro as well. Obviously Nuxt is too. And we would really welcome other frameworks to do the same kind of things. It provides all the primitives that you might need. So we have authentication, for example. You can use use session to create a persistent session stored across requests via cookies. I mentioned obviously storage, the KV support. It has support for deploying to any serverless or non-serverless target you might think of. From Docker to Vercel to Netlify with zero config to cloudflare to Lagon, a new runtime. And you can define root rules individually per endpoint. So you can specify certain ones to be ISR, certain ones to be server-side rendered, some not to be. Whatever you can think of, it's possible to do. Please do follow up. Do contact me if you'd like. I'll push up this code and maybe a little bit more if I have time to Daniel Rowe of vue.js Live so you can take a look at what it might look like to build a serverless framework on Nitro. And if you do want to try out Nux, which I would very much hope you do, and try out Nitro in a full production framework environment, check out nux.com. And the docs for Nitro are on nitro.andreas.org. And do follow the Nux Twitter account. We have a release, a minor release of 3.5 with some amazing features including typed root rules, which Eduardo demoed earlier today, coming out today or tomorrow. That should be pretty fun to see. That's all for now. And please ask me questions in a moment. It's been a real pleasure and a delight to be here in London. Thank you so much. Thank you, Daniel. This was awesome. I don't know what's going on here today, but people are building frameworks live, building virtual DOMs live. I don't know. Is that a new trend or something? Feel free. Well, I think it's fun to explore the building blocks. Because I think often you just don't see what goes on under the hood with something like Nux. So Nux, you get started. It clones itself into your working directory. And basically you just have a single app.vue file. So you can't really see. It should just work. All of this, there's sort of types and everything. So I do think it's sometimes fun to peel back the curtains and see what's going on. I 100% agree. Let's now dive into some questions. The first one is, should we use Nux if we don't need server-side rendering? Yes, absolutely. I mean, by the way, I think the most important thing to say is use the right tool for the job. So I'm not a one tool kind of person. And hopefully, if we are crafts people, we're basically all of us carpenters. We're making things. So use the tool that will make you most productive and make you best able to make the thing that you're making. But there's no reason Nux isn't a really good choice for building a non-server-side rendered app. So things like auto-importable components, the type safety of Nux, a lot of the utilities it provides. So from built-in components to error handling to how to configure and set up suspense and routing and all of that is something you just don't need to think about. Although not your rendering on the server. What would you say about learning curve for Nux? In case, I don't know, I'm a vue developer, I know vue pretty well, but I want to start using Nux. If you take a vue app that you've built and you want to convert it to a Nux app, most of what you'll do is delete code. So it should just work. You should just be able to drag it across and your app.vue from your non-Nux app becomes the app.vue in your Nux app and it should work. So the learning curve, everything you know is relevant, but you might not need to do as much as you had to do before. It should just do it for you. And the best thing, of course, if you have questions, is come to the Discord, which is discord.nuxjs.org. I'm always there, you can ask me anything. And there's a huge community of people who are really helpful. So the learning curve shouldn't be too bad. But if it is, there's a lot of people there to help. Okay, perfect. So we have the support. Let's dive deep into the next question. Is Nuxt3 capable of providing a fully featured backend experience regarding authentication, working with external dependencies and multi-threading, like for example Django? Or is it meant as a supporting backend for mainly frontend applications? I think it's no accident that quite a lot of the Nuxt core team came from a Laravel background. So we've experienced something of a full stack framework with that. And I think we absolutely want Nuxt to have everything you need, not that you have to use it as a full stack framework. So our focus on KVStore, on database, on database layer, we're providing the base utilities like session support that authentication modules need. There's a great authentication module at the moment by Sidebase that has adapters for hundreds of different... So I would say yes. And if there's anything that's lacking, let us know because we'd love to build it. Okay, ask in Discord. Cool. The next question is, is it unwise to use webpack with Nuxt3? I wouldn't say it's unwise. I use the tool that you like and makes you most productive. I would say that the vast majority of devs using Nuxt are using vite. So it's something like 95% of all downloads of Nuxt are with the vite builder and only about 5% are with the webpack builder. But I think that likely reflects the fact that the frontend ecosystem right now loves vite. So there's so much development happening there. There are lots of plugins being built for vite. You get a lot of support there. So I think most people starting a new project are thinking, I want to build on vite. But if you're more productive on webpack, if you've got tooling that only works on webpack, we do provide a builder so that you can use it. So it's up to you. I 100% agree about vite. When I tried it out for the first time, I couldn't believe it. It's so fast, that developer experience. But probably, yeah, there's a lot of legacy webpack projects. And I know you have a custom webpack configuration. You cannot change anything. It will break. You can't let go of it. It's like the one ring. You know, it is this webpack config is my own. We do forgive Evan for making vite, right? Even though it made Nuxt 3 take longer, but it was worth it in the end. Okay, so we have the next one. Do you prefer vuex or Pinnia with Nuxt? I would use Pinnia. So I think vuex, so Pinnia is vuex's natural successor in the vue 3 ecosystem. So I would use Pinnia. It's got great dev tools. The guy who wrote it's not bad either, but yeah, it's good. I love that we don't have a lot of choices here. Just one state management library. Not like react and a new library every month. You might not need Pinnia, I should say. So Nuxt does provide utilities like useState, which enable moving state from server to client without re-initializing it and allow sharing state between components. If you have a really simple state system, you might not need to add any external dependencies. But when you do, make it Pinnia. Okay, cool. The next one is, if you inherited a vue 2 project, is it worth the effort to integrate Nuxt? So this kind of question is always, it should be driven by business needs. So what is your capacity in terms of your team? What is the value the app is going to drive for you as a company? How long do you plan to maintain it? So for example, if you just have to keep an app ticking over and it's working fine, you don't really see any problems with it, you might not benefit from spending the investment of your time to make changes to it. That said, if it's me and I have the time to put into it, I would absolutely do it. But that doesn't mean that it's always the right decision. You really have to, but I mean, it's a business call for any tech adoption, for any migration, like rewriting anything, migrating anything. These are not decisions without cost. Yep, yep, definitely. Okay, the next one is, I've seen from the Google I.O. conference that Nuxt is collaborating with Chrome to boost performance. Can you tell us what's going on? That makes it sound like it's a secret cabal. Well, yes, we gather together in a dark room. So yeah, the Chrome team have been great. We've shipped some things that have benefited Nuxt, I think that have also benefited more broadly as well. So doing some interesting things with source maps, for example. This is on the DX side, Chrome devtools. You'll see better source mapping support with Nuxt and vite in the latest Chrome. In terms of more accurate information, you'll see your code, you'll see better context. So there's a common issue with async stack traces that you lose previous context. We can actually solve that, and we've done some great stuff with them for that. But they have a team, the Aurora team within the Chrome devtools team, and they're seeking to ensure that best practices around things like script loading and font loading are applied sort of to key frameworks. So they're working with us, they're working with angular, they're working with nuxt.js, and they are honestly a pleasure to work with. So it's always nice to have people who challenge you, come up with interesting and new and different ideas. So Fontaine came out of a conversation there. By the way, if you check out the readme, I'll give credit because it wasn't me who came up with the idea. It was Katie Hempenius, her work basically to calculate metrics for fonts to enable less of that junk when your custom web font loads. And so there's lots of opportunities like that to bring in good ideas, because I guess they've got some good people working there. Nice, glad you're doing all that, especially for source maps, because the better the source maps are, the easier it is to debug everything. I'm sure you don't have any bugs. Never. Me neither. Cool, the next one is a personal question. What are the VS Code extensions you use? I can share my VS Code config with anyone who wants, so let me know. But I probably have quite a few. But obviously you have to get Vola. Yeah, I'm sure you have a lot of them, so I don't know, maybe somewhere in that same GitHub. In README. Yeah, some I would recommend. There's a new Better TS Eris extension that's pretty nice. Console Ninja is pretty fun. It lets you actually track console logs from the browser in your IDE. That's really nice. There's a new extension called Nuxtr, for Nuxt, N-U-X-T-R, made by some people in the community, but using tools like Magicast and others under the hood that actually lets you add modules, modify your Nuxt config, all from the sidebar of VS Code. Honestly, there are so many. Awesome. And the last question is, what does the future hold for Nuxt and Nitro? I would like to say the future is bright. So what does the future hold? So I think I don't want to steal too much thunder from Sebastian, who is going to paint a bit of a vision of some cool things that you can do. But we've got a good team, and there are a lot of good ideas that are coming. So in the next week or so, you'll probably see three new RFCs, also related to the Aurora team and what we've been doing with them for Nuxt Script, Nuxt Font, and one which I think you'll find really interesting, Nuxt Assets, which is pretty cool. Yeah, lots coming. Awesome. Thanks a lot, Daniel. So the future is bright. Give us some applause for Daniel and the bright future.