Build Resumable Apps with Qwik
Links & Resources
Click to expand the full transcript
Captions provided by White Coat Captioning (https://whitecoatcaptioning.com/). Communication Access Realtime Translation (CART) is provided in order to facilitate communication accessibility and may not be a totally verbatim record of the proceedings.
JASON: Hello, everyone, and welcome to another episode of Learn with Jason. Today on the show, we have Misko Hevery, thank you so much for joining us. How are you doing?
MISKO: It's a pleasure to be here and thank you for having me.
JASON: I'm so excited to talk to you today because I feel like you are doing some work that's in a really, really interesting part of the web that I think is going to move this all forward. But before we talk about that, let's talk a little bit about you. Folks who aren't familiar with you and your work, you want to give us a little bit of a background?
MISKO: Yeah, so, let's see, where do I start? I think my prior life that people might still remember, I used to do a testing blog, and I was trying to convince everyone that testing was a good idea. It was 16 plus years ago, it was a long, long time ago. I did this thing called Angular JS, followed by Angular, and I might have something to do with karma, the test runner. And so, now, it's, what is it? 2022, and so last year, I decided that, you know, I'm ready for a new challenge. So I joined Builder I/O, it's a startup. If you know CMS, this is something completely different. And I joined as a CTO. And, of course, I'm working on Qwik now, one of the things we're trying to do.
MISKO: Yeah, I think I'll back up a little bit and go back to the J query days. There was a transition from J Query to Angular JS. Introduced components, a structure to your system and basically introduced a nice way. The thing that we know today what was started by Angular JS. And I think what's happened sinceEning angular JS, what's happening in my opinion is that all of these frameworks are basically, I'm not sure the word arguing is the right word. But they're competing on DX experience. They're basically saying, we can make the DX better. And, there are a lot of reasons why you might choose one over the other, and I think there's been a lot, a lot of innovation that's happened in the DX, right. So there's good reasons to use one versus the other. But what hasn't changed is really the capability of the framework. Like, the way the frameworks work, the way hydration works, the way they're booted up, everything has to happen up front. The way you have the bootstrapping of the system. None of that stuff has really changed. And if you go back to J Query days, one of the things super nice, you could start with an existing page and sprinkle J Query in. As little as much as you wanted and there was a nice transition process. And it was a lot more of the HTML is the truth and J Query enhances it. And we went into the world of, like, no, the framework's the truth and the HTML is just like this annoying side effect to deal with in order to build our app. Right?
JASON: Mm hmm.
MISKO: And that's the transition I'd like to go back to. I want to have the best of J Query and a lot of things wrong with J Query, I'm not saying it was awesome. But this idea that you could sprinkle it in and get the benefit and HTML is more of a truth to you, but also, get all of the benefits of the nice DX that we want. So it's kind of a merge thing I'm aiming for here.
MISKO: Yeah, before we jump into that, let's kind of focus on two things, I think which are the problem of the current world. And the two problems are, personalization. It is hard to get personalized websites. And the reason, server side prerendering is very expensive. In the days of J Query, it was super cheap and we could do it and we could do A/B testing, all kinds of things. But now server side rendering causes us to rerun the whole application. And the reason is because we can't create strings because on the client, there's the hydration which does the reconciliation, and if the reconciliation, it'll override whatever you want, right? So all of this makes it the personalization is actually super hard problem that for the most part we don't have a good answer. The second problem is hydration. And hydration is the thing that the browser has to do to turn the static HTML into an interactive one. Right?
**JASON:** mm hmm.
MISKO: This is why Amazon spends ridiculous time and effort to try to get the number as high as they possibly can get it because they know slow websites means less revenue and for somebody like Amazon, you know, even a fraction of seconds is probably millions and millions of dollars of revenue that's gone.
JASON: Mm hmm.
MISKO: So, yeah, I talk too much.
JASON: Well, and I think what's interesting about this, and you say it's, you know, we could just blame the developers and if we say it's the frameworks, it's sort of the frameworks, but it's also the realities of building for the web where you use your framework and like, you can spin up or create React app. And if you never had third party scrapes, never had business requirements for telemetry or reporting, then suddenly, you're like, OK, well, I can kind of code golf this down, get in the high 80s. And someone's like we need to add amplitude and suddenly, you're loading tons and tons of third party scripts. And you know, it's death by a thousand cuts. If your framework is requiring you to ship 50 kilo bytes of framework, and then your app itself is another 200 kilobytes of script. And your Google tag manager is shipping, you know, in reality, megabytes of script it has to execute. And then, whatever the ads are loading or those all have events that are blocking execution and so, you get into this really bad, like, snowball effect of, you know, what we can't fix the third party script. So I'm not going to stress too much about the extra framework overhead. It's not like it would fix it. We throw our hands up and go, well, it's the best it could be, I guess.
MISKO: It's systemic, it's not the developer's fault. It is kind of for lack of a better term, the industry and the industry's controlled by the frameworks and therefore, it's the frameworks' fault. Right?
JASON: Yeah, I guess, ultimately, it's one of those problems that it's a multi headed problem.
JASON: And when you have a problem that doesn't have a clear source, it falls to the people with the most control, which would be the framework authors.
MISKO: Yes, and you made a good point that you could build a React app and get 100 out of 100. What I find interesting, you could build an app and get 100 out of 100, install Google tag manager and get 100 out of 100. You could install intercom by itself and get 100 out of 100. But if you look at the amount of time you've gotten each one of those, basically ate a whole lot of time.
MISKO: When you put it all together, there is just no way that you can make this go fast, right? And so, the thing with Qwik we're trying to do is, essentially, step back and say, like, look, our goal is to get 100 out of 100.
MISKO: What do we have to do differently to get there? There's a two part problem you have to solve in two ways. One is, you've got to fix the framework. And this is what Qwik is. And the second thing, you've got to fix the third party scripts, and that's party town, party town takes the third party script, and I know it's a lot of talk about party town here, I'm not going to go into deep on here, but that solves the third party problem, your Facebook pixel or whatever you have and the main gets uploaded. And what's left now is just the framework. And specifically, the problem you have to solve on the framework is when the framework boots up, it has this huge cost of what we call hydration, right? And it's a combination of reconciliation, and specifically, when I talk about reconciliation, I mean, figuring out where do I install the listeners? I've got to install somewhere. I need to click listeners, mouse listeners, hover listeners and so on. Where do I put them? And not just where do I put them? But if you click on them, what exactly is supposed to run, right? And so, these listeners have two parts to it. They're closures, they're not functions, they're closures. And we don't know how to closures, but functions don't have data associated with it, right? If I lazy load a piece of code that says add to shopping cart like, it's great, but what shopping cart? What item am I adding? And who is the user I'm adding it under? And all of this thing matters. Without this information, you can't just go add to shopping cart. That's meaningless thing. That's not a thing.
JASON: Yeah. And so, to for folks who don't have a computer science background, if you're not familiar with the term, like, closure as opposed to functions. What we're saying is, like, if you take a function and that function takes arguments, the arguments are unknown to the function, right? So you're saying, I want to call this function on whatever you send me. And that makes it stateless. It's a pure function. Same input, same output, everything is expected, right? When we're talking about closures, you're wrapping up some form of state, right? So you're now saying, this function that was stateless now needs to depend on some external thing and that needs to be true every time. So you, like, grab all of that and put it into one bundle. And this is why it's so hard to do hydration and to do a little bit of sprinkling. If you sprinkle and add to cart button and sprinkle a cart over here, you now have two independent apps, how do they share that cart? How do they talk to each other? Right? Now you write a lot of overhead and weirdness and people typically say, that's too hard, let's hydrate the whole page. It'll be one app and it can talk to itself.
MISKO: I want to make a distinction there, there are two types of states you have to say. The obvious one everybody understands is oh, I'm adding to a cart, what item, which cart? Who is the username? That's application state. Right?
MISKO: But these functions also close over the framework and this is not obvious at first. Because let's say you go and add an item to a cart, the framework's like, I have to re render something somewhere. How is the framework supposed to know? What is the structure of the components? At which point do I re render? Where is the function for re rendering? It's all of these secondary things people don't think about is secretly hidden in this closure, right.
MISKO: It's not just about, oh, it's next.js and serialize the app. It's the second part, the framework needs to know, where are the components? Where the boundaries? Where's the parent? Who's the child? What are the children? How do I project the children? On and on and on, a long list of things the framework has to know. And framework author, you're like, yeah, I don't really think about this particular problem.
JASON: That's the whole goal, right? That's why we use frameworks is so we don't have to think about those things.
MISKO: Exactly. So the problem is, you need to restore the state. And you need to restore both the state of the app but also the state of the framework.
JASON: Mm hmm.
MISKO: The restoration of the framework state is why everybody says essentially, I don't know how to do it, I'm going to rebootstrap the whole application. That's what shoots you in the foot. It is the thing that, like, yeah, you lost. Sorry. Because the framework for example needs to know what is the components? That means, great, now all of the components that are currently in the HTML, I actually have to download and execute them, right? It doesn't matter if you're trying to be clever and you put a component behind a suspense or lazy load or whatever. The framework's like, sorry, I need to know what's behind there. I need to down load that thing. No amount of lazy loading is going to help you. What's on the page? What's rendered on the page is the thing that has to be downloaded and executed. And there's nothing you can do about it.
JASON: Yeah. And so, you can change the timing of when you download, but you still need everything.
MISKO: That's where Qwik comes in, right. Qwik's basically the premise is what if the framework could reason about the system without having the Qwik presence. In other words, what if there was sufficient information in the HTML itself where the framework would make inferences about, like, oh, if this changes and change that thing over there or, this is a component, and the component has props for this other component or it's a component that has projecting the children, so do this. What if the framework could make these inferences without having access to the HTML, without having access to the source code, right? That's the premise.
MISKO: You're still serializing the state of the application anyway, it's in JSON. So no matter which way you slice it, you realize that if you look at it wholistically, yes, HTML's bigger, but it's nowhere close to make up for the cost of the thing that you're saving.
JASON: Gotcha, gotcha, OK. And that makes sense, right? If you're able to encode the information once, then, you know, because if you look at a a Gatsby or a bundle, you have the HTML, that has to download. And then, you get the core framework, which is going to be React, the next core, whatever else. And then, you're going to get component code that got split out at build time and you're going to get these JSON files that include, like, whatever you downloaded. Whatever's in your get static props call or your get server props. That turns into JSON that comes in and the next code hydrates the HTML, looks at the component code, that component code looks for the JSON and pulls that all in. And so, the component code would be roughly the same size as your HTML because of the way that, you know, that works. You're setting these components up the same way you do markup. So you limit that. Qwik's got to bring in in the state the same way that next or Gatsby brings in the state. So you probably have an equivalent state size.
MISKO: Mm hmm.
MISKO: Yes, we can talk about that. We ship, to get our bootstrap, to get our kind of up and running, we have this thing called Qwik loader, and Qwik loader is approximately a kilobyte.
JASON: It's pretty fast.
MISKO: It's the fact that you have to execute it.
JASON: Yeah, the execution, and that's where one of the biggest shortcomings in metrics tends to be is the time to interactive. Right? Because you download the page and you have to parse and execute and then, everything kind of, the thread frees up after all of that execution, now, you can click stuff. Now, you can do things on the page. And that, like, you might get your HTML on screen in a second. But you're not actually interactive for 3 seconds. And I would say, like, those two seconds of your page being there but not working, that I find that Infuriating when I go to an app and it shows me the app and I click the button and it's like, just kidding, I don't have everything I need yet.
MISKO: That's right. You get that. You get it. Let me go back to one more point I think I want to make.
MISKO: When we talked about framework has its own internal state, pretend you're a framework, right. And pretend somebody hands you the HTML that was produced by Next.js or React. And somebody says, where are the component boundaries? Where are the listeners? You can't answer that question. Like, looking at the HTML, there's no indication whatsoever of where the component boundaries are and where the listeners are. And this is the problem. That's literally the problem. That the framework that loads, looks at this HTML and says, I don't know how to reason about this. I don't know where things are. So if you could somehow put all of the information back, so that the framework can look at the page and be like, oh, yeah, I see the components are here. This is the boundary of the components, this is a different component over here. This is where the listeners are. This is what I have to do over here et cetera, et cetera. That's basically what we're talking about.
JASON: Gotcha. So I have a question in the chat. With the JS being downloaded on demand, is there a way to manage latency for mobile browsers?
MISKO: If you start with 300 kilobyte app and Qwik also ends up about 300 kilobyte app and it's probably a reasonable comparison given both use JSX. The difference is that Qwik can remove a whole bunch of stuff that you don't need whereas you can't do that in a normal framework. Even if you have a component that never ever changes, you still download JSX, because we need to understand the boundaries. That is not negotiable. You get to skip a whole bunch of stuff here. And then, you get to download things in the correct order. And then, finally, you don't get to execute any of the stuff, right? You only execute when you actually need to do work. So, the normal frameworks need to basically download all 300 kilobytes, execute all of the 300 kilobytes and they're ready to go. Whereas the Qwik framework, you know, you might start with 300, but then you realize, only about really 150 of it is actually what we need. So you already download less.
MISKO: No parsing whatsoever. Because you don't have to redo anything to figure out where things are. And it's only when you click do we actually go and start executing the listener for particular things, and only the parts of the page that actually deal with the updates will get downloaded. Execute the angular and then probably downloading the shopping cart render function so you can re render the shopping cart. But everything else on the page, never gets executed.
JASON: Gotcha. OK. So
MISKO: This is kept statistics on and this information is what we use to make sure that when we make bundling, we give you the bundles in the correct order. In the order of like, how likely we think you are to actually do this particular operation.
JASON: Got it. Got it. Got it. We have one more question, and I want to start coding. Does it maintain a dependency graph within the app?
MISKO: Yes, so that's kind of the
JASON: I love it. Absolutely love it. Just realized I was getting a little bit of echo in my mic, so I'm throwing headphones on. And with that, I feel like we could probably talk about this in the apps for solid multiple hours, but I think at this point, probably the easiest thing to do is start showing people how it works.
JASON: Let me switch us over to the programming view here.
MISKO: While you do that, I want to make one more point here. And that is that as I said, you know, existing frameworks are really competing on the DX, right? They're really competing, what do I like better? This way or that way? And when we start coding by Qwik, you'll see that Qwik looks awfully like an existing set of frameworks and that's no accident. I don't want to reinvent something else. The value add of Qwik is not that the DX is different. The value add of Qwik is the runtime. What actually happens under the hood and the consequences of those choices. And those benefits are on a completely other level. You just cannot even compare it to the existing set of frameworks. And this is what actually makes it super hard to talk about Qwik because people are naturally like, oh, so it's like React or it's like this and it's like, well, no, I know it looks like it. But no, it's completely different.
JASON: Yeah. All right. In the spirit of show, don't tell. Let's dive into this and make it happen. So before we get started on this, I'm going to do a quick shout to our captioner. So we've got Diane here from White Coat Captioning today, taking down everything we're saying, thank you very much. And that is made possible through the support of our sponsors, Netlify, NX and Backlight kicking in to make this show more accessible to more people, I very much appreciate. Let me drop a link there if you want to follow along with the captions there on the home page of the site. We are also talking to Misko today. So you can go and follow Misko on Twitter if you want more and more of this information. Talking about Qwik, Qwik is this is the repo for Qwik. And it looks like there's also a site here that I'm going to drop in. Earlier, you mentioned Partytown. I dropped the link. Partytown is not associated with Qwik, but both Partytown and Qwik are maintained by the Builder I/O team and are solving, you said, two sides of the same coin. So if part one is you need a framework that's super efficient, and you know you have to deal with third party scripts, these two tools together or used separately can make a big impact.
MISKO: You got it.
JASON: All right. So I've now run out of knowledge. I'm going to need your help here. If I want to get started building with Qwik, what's the first thing I should do?
MISKO: Well, click on get started and let's follow the instructions over there. See how well they hold up. You need Node, your favorite IDE, and you will need to type NPM start, Qwik@latest, I believe.
JASON: OK. So we're going to NPM. So let me run that. Project name.
MISKO: Asks for a name, we can start with that.
JASON: Let's go, resumable apps Qwik.
MISKO: Let's go with a starter, unless you want a to do. That's more stuff up and running. But the starter is easier for basic hello, world, and getting from there.
JASON: Let's start with a Hello, world, and play here.
MISKO: Let's drop express because it's going to be local development.
MISKO: Take all of the basics. You have to enter, I don't think you have to change anything and there you go.
JASON: Run the NPM install.
MISKO: Get your favorite ID going.
JASON: All right. And I'm going to open this up in VSCode, there it is, and let's, let's rock 'n' roll. I'm going to start this thing. In this terminal here. And that gives us local host 3000, browser here, let's get one running. And here is our basic page. All right. Try interacting with this component. So we've got some basic interactivity running.
JASON: Like that?
MISKO: Yeah, like that. And now, if you open it up
JASON: OK, so let me refresh.
MISKO: We could get it running in the proper SSR. You can do NPM run build and NPM run serve. And I think for the demo purposes, it's nice to automatically have things updating.
JASON: Yeah, for sure. Cool, cool. All right. So this is, like, I love the, we also can see here if I make these bigger, we brought in 500 bytes for one thing, 4.5 kilobytes for another. 550 bytes for another and each of those gives us the ability to hydrate and looks like the logo is this one. And that rehydrated after the others, I think.
MISKO: It's interesting it did. I need to look into it, it looks like a bug. We don't like to use the word hydrate because that implies all of this other stuff, which we don't.
JASON: Sure, sure.
MISKO: The other thing I want to point out is, again, you're running it in dev mode, and so individual files get served up individually. In production, you could have a different bundling strategies where you would obviously, you don't want your app downloaded as a thousand little files, right? You want to put them together. And that's something that's part of bundling process towards the end where you decide what goes where and which order so it's optimized. So don't think of it as in, like, this will be like this forever and I'll have to deal with apps that have gazillion download files, et cetera. It's not necessarily how it's going to work in production.
JASON: I gotcha.
MISKO: The point to make here is that existing frameworks have a really hard time to, if you want to have multiple bundles, what you have to do is create multiple entry points into your application. Right? Existing frameworks do not help you with creating multiple entry points. Whereas, Qwik, out of the box will create tons of entry points for you. And that's kind of the also part of the secret sauce, right? Because unless you can take your big app and break it up into pieces, this magic won't help you as much as you think. Right? So part of this magic is how do I break up the code in a way that the developer doesn't have to think about it? It just naturally should happen.
JASON: Gotcha. Yeah. And so, part of the way that works and this ties into a question from American 2050. Is the listener declared in the HTML input itself? Because like you said, we didn't do anything until I started.
MISKO: Yeah, refresh the page, and I can't wait. I can't point on your screen. Use that little arrow thing that allows you to look at the input. Yeah, that thing and you click on the input and shows you the down tree.
JASON: I'm going to make this a little bit smaller.
MISKO: Here's your input. And notice it says, it has this attribute on input. This is how the system knows, if there's an input event that's triggered over here, then input bubbles up and there's a global listener that intercepts it. And it comes back to here and says, ah, I need to go download a source H main, main render, JS file and from there, I have to get the symbol made on render on input. This is dev mode, in production mode, these would be shortened and have hash open and other things but this gives it information as to where do I go? And there's a weird bracket zero. That's for restoring the closure. And we can talk about that in a second, if you're up for it. I don't want to drive this thing, you should be driving, you should just ask me questions.
JASON: I mean, I think, I'll let the chat drive a little bit on that how in the guts of this you want to get. But this is interesting, and a question is there a way to pre load these chunks before you start typing?
MISKO: Yeah, that's what we talked about earlier. You can start pre fetching the code. You won't execute the code, so you can save on that, but you can pre fetch it and deserialize the state of the system. There are many things we could do eagerly to kind of bring the system up. Right? The point is
JASON: Maybe that's a good first step. Let's poke around in the code here, I'm going to do a quick thing because my git ignore is in place here. I'm going to get init, it won't all be dim. And now, if I go into I'm going to take a wild guess, this is the right one. Congratulations, Qwik is working, and then, if I let's see, don't want that. We've got our next steps. So we've got an input. And OK. This all looks pretty familiar. So stuff I don't care about, this is tail wind, not today. Place holder, value, our standard thing. So this on input is what looks like Qwik, and syntax here of dollar sign.
MISKO: We should talk about that. It's not a convention. It is a special thing. It is information for both the developer and the optimizer, right? So the thing that's different about Qwik, if you look at existing frameworks, they all are essentially runtime based. Meaning that, it's just runtime, nothing special about it. Qwik has a compile time component. We call it compile time component the optimizer and the job of the optimizer is to break up your application into smaller files so that we can have more chunks. Because as of right now, whenever you download the component render function, you are also downloading the event. Right? The two are there's no way to get one without the other, as of right now. And so, we have this thing called optimizer and does a bit of magic. The question is, how does the optimizer know the magic should happen? And the answer is the dollar sign suffix is what it tells it to do. And every time you see a dollar sign, what you should be thinking to yourself is that is where the dynamic import happens. So every time you see a dollar sign on the page, dynamic import is happening behind the scenes. So if you scroll up a little bit, you also see a dollar sign online 6.
MISKO: You could have a reference to main, but unless you want to render main, main's content isn't going to get downloaded.
JASON: Got it. So if I had like a routing solution in here and based on, you know, some piece of URL state, I'm going to show one of two components, then I could reference the two components that would need to be shown. But until whatever condition is met that would actually render that component, it won't do the download because of this dollar sign?
MISKO: Correct. And the point to be made is that when you want to do lazy loading existing frameworks, you have to basically jump through complicated set of hoops, right.
JASON: Mm hmm.
MISKO: It's not seamless. Whereas in Qwik, the API just naturally has a dollar sign in there and you don't even think about it and you just write it in the natural way. And yet, you end up with tons and tons of dollar signs everywhere, and every single dollar sign is an entry point. So there's a one to one correspondence between dollar sign and entry point. As of right now, just looking at this particular file, you have two entry points. Actually, you have 3, right? Of the file itself. And then, you have the component, which is the main. And then, if you scroll down, you'll have the listener, which is input over here. I don't want to drive here because you're the one driving, but if you want, this is something interesting about the input.
MISKO: So, OK, let's think about the input for a second. So the const input takes a closure and take the closure and refactor it, so just cut it out. Just say something like FOO in all capital letters. And paste what you had in there. This is what you need to do in order to do lazy learning, right? You need to hoist this particular code into a top level location so that or separate file so that you can do lazy mode.
JASON: Mm hmm.
MISKO: Do you see the problem here?
JASON: Yeah, we're missing, we had state in the component, that's gone. Had our event, which event we can handle. But this that's not going to work.
MISKO: Line 57 is the problem, right? And line 57 is the crux of the problem, it is the it is what we talked about the closures, the closures capture state. And when you move the piece of code into top level function, which is what we need to do in order to have a lazy loading thing, we have broken the state. State can no longer do its magic, right? The natural question to ask is how does that work? How exactly do we recover this? You probably want to undo all of this stuff. OK. And if you go to your browser, that's refresh, again, look at the network tab.
JASON: I stopped it so I could run.
MISKO: NPM run dev.sssr.
JASON: There we go.
MISKO: OK. Now if you start typing inside input box. OK. You see bunch of code that downloaded. So let's look at the first one. And look at the response. And if you look at this, this is basically our closure. If you look at the source code, right? The source code got ahold of the event and then said state dynamic was input the name. This was literally our stuff except for that line 3 that got inserted over there.
JASON: Mm hmm.
MISKO: Right. That line 3 is the magical part that restores our closure, that restores our state. Right. And so the question is, well, how did the code know what to do? If you go to the input inside, you'll see the bracket zero at the end. You see the bracket zero on the URL? That basically tells it how, where exactly the first argument for the restoration of sculpture comes from, and this builds and recovers everything out of the system.
JASON: OK. And we find that, this reference, I mean so a couple folks asked to just dive right into the heart of this. So let's
MISKO: How does it work? For complicated set of reasons that I don't want to get into, but basically, they come down to the problem that you want to memory leaks. The bracket zero means I want to get the zero object out of the input and notice the input has something called queue object. And queue object has one exclamation point. It means subscription. It's an index into the array of location one. So you typed, so let's refresh the page, again. And scroll to the very bottom of the dom. And you see there's a script Qwik JSON, and if you open that up. Yeah, there we go. There are two arrays in there, the objects and subscription array. And in our particular case, we were talking about object 1, so zero, one, so name is equal to four. This is our weird serialization format because we need to be normally JSON can only have direct graphs and we want to have cyclic graphs. So we are perfectly fine with complicated things. So four is not the value four, it actually points the fourth item inside of this array, which is if you count down is the world, right?
JASON: So we use the input 1, object 1, so 0, 1. So this references 4, so 0, 1, 2, 3, 4, world.
MISKO: That's right. There's a bunch of other URLs in here, we call them QRLs, affectionately. [ Laughter ] Yeah, so there's a bunch of QRLs in there. And they tell us what, if a particular object becomes invalidated, where do we get the code for it? So this basically, it's a pointer to the render function for the main. And looks like there's also a render function for the logo itself. Then there's an exclamation point over there, and the subscription now talks about, again, there was object one, you skip the zero.
JASON: We go back up, cue object 1 with an exclamation point, that's a subscription, 0, 1.
MISKO: That's the object. And that basically says, if you, if you change the property name, then go and invalidate hash zero. So it is not an object in here, but it's an object in the dom: So notice that if you scroll up higher, there's going to be a div with queue host. Right there. It says q ID 0. That's the location of the component that is now invalidated as a result of you modifying. Let's change it from a source code side.
JASON: Let's see. To the network. So if you type anything there. You look at the first one, right. All we did is we copied the value from the input to a state.name. In something like React, you would have set state. Problem is set state is that it's a function and we don't really know, have a good way of serializing it. So we instead use proxies. So state is actually a proxy, and when we build this proxy up, we know that the value of name is world, and we also know that if you change the name property, then we should invalidate a particular component, which in this case, there's only one component, that's the component we're invalidating, by validating, we have to rerun the render function. And so, the magic here, right, is that how did we know all of this information? Well, we knew all of this because on a server, we ran the code. Right?
MISKO: Server ran the code, we, again, created a store object, which was a proxy, and then, when the render function got ran, the render function did a lead on the name, so we kind of recorded that saying, oh, somebody's interested in the name property. OK. And the result of all of this stuff is what we call subscriptions. And the subscriptions is serialized as part of the system when it's sent to the client. So when the client wakes up, it can reason about all of this information without having to download anything. Because imagine you're in React, right? And you do this, and you change some value call, set state and you change the shopping cart. How is React supposed to know which components have become invalidated?
JASON: Right. Yeah, that's why we have to build the whole tree and you have the, you know, the algorithms that they've been working on to get more intelligent about only invalidating parts of the tree that got invalidated. But it does require building that tree in the first place.
MISKO: By the time you built the tree, the game is over, you lost, you have downloaded all of the code and executed all of the code.
MISKO: How do I not download and execute code? This is the mental model that the Qwik tries to solve. All points, you need to think about, how do I avoid work? Work equals download and execution of code, and I can't afford that.
JASON: Mm hmm.
MISKO: Let me take this thing into an absolute extreme so as a developer, you don't have to think about it. Like, if you go to look at the source code of the component, and the component is kind of big because there's a lot of text in there, but if you delete all of the text, all you're left with is a state object declaration, a listener that mutates the state object, and the render function that reads the state object. Right? And that's it. That's all you really need to know. And we need to be able to serialize all of this information in the way so that the client can reason about it and say, like, oh, yes, I see this hasn't changed, therefore, I know I don't have to re render this.
JASON: Fascinating. This is and this is kind of where I'm what I'm excited about when we look at the progression of the web is that it feels like this manner of thinking about things would have been so complex in a cross browser way. Even 5 years ago, you know. Like seeing the browser's API is kind of stabilized and enable us to do some of these things in a way that doesn't require kilobytes of polyfills and tons of edge case handling is really exciting. It feels like we've kind of nudged a door open where we can take a major leap forward in the way that we build for the web. And so, if I want to start, like, let's build a little bit here. I'm going to do
MISKO: If I were you, I would blow away everything that's in here.
JASON: Let's do it. We're going to get rid of it.
MISKO: Delete it, yeah. And put whatever you want in there.
JASON: OK. I'm going to get rid of all of this. Get it out of here. This host is like
MISKO: Ignore it for now. We can go into details what host it is, I think it's not necessary.
JASON: Let's start here, right? I'm going to have a little bit of data in my app. And I want to return a component. And the component that I want to build is going to be a, let's make a quick photo switcher, right? I'm going to build a instead of this logo component, which I'll just get rid of delete that. I'm going to create a folder, a component called photo.
MISKO: You can put a component in the separate files, but for the purpose of the demo, you can put them in the same file.
JASON: That's a good idea. Let's keep it all together.
MISKO: It will still get broken up into individual separate downloadable chunks.
JASON: Fascinating. So I'm going to do photo, and that can be a component. And that component is going to take a photo URL. And that's going to, then, return an image source of photo URL. And alt text, that alt, alt and here. These both need to be strings because it's TSX. Right.
MISKO: Mm hmm.
JASON: What's wrong? What am I doing wrong?
MISKO: I think you need to save props, isn't a props colon in the front?
JASON: I think I need to declare.
MISKO: You need to close line 11 requires a parentheses and semicolon. 11?
MISKO: Close parentheses and semicolon.
MISKO: It's interface, OK, perfect.
MISKO: Yeah, you have yourself a component.
JASON: So then, I'm going to, we're going to return we'll call it a div and in the div we'll say, you know, photos from my trip. This will be a little blog for whatever. And then, I want to, we're going to put together just a little display. So it'll be I want to be like a switcher, though, so let's do, like current photo and we'll set the current photo to zero. And then, I want a list of links. This will be fine. I'm going to set it up the way I normally would. Which is to say, we'll set up rows, but we're going to do it. We're going to do one of these. And then, we'll do one of those. And we'll do one more for good measure. And then, down here, what I want is I want to show the photo that has a photo URL. Look at this, oh
MISKO: This is so tricky. [ Laughter ]
JASON: So good when it does this. We're going to call this one alt. And this one is going to be photo URL, right? And so, in here, I'm going to take out photos. And we're going to do the I'm going to clean this up a little bit. So we'll do, let's go to unsplash and get a few photos of like get a beach. And I'm going to take this one. And I'm going to copy the image link directly. Drop it in. And the alt for all of these is going to be a beach at sunset. Because that's all I did on this trip. So we'll take two more, and then, let's get two more beaches. So we're not lying. We'll do that one, here's another one. And we'll get one more. There we go. All right, so I have 3 images. Beach at sun set. And then, what I need to do here, we've got state.photos and this one state.photos and the alt. So based on this, I'll have a link here that I need to listen to to update the current photo, and by default, what we're going to see is our first photo. So this won't function, but it shouldn't break. So let me start it. It's already running, perfect. Let me go to my site. Photos from my trip, there it is. Beauty. So, nothing
JASON: Let's make this thing interactive, right? So I'm going to, I'm going to see if I can infer from based on what we've seen already, I'm going to go on click dollar sign and then, because I am going to hard code this and not try to infer it, I'm going to event and then I'll event, event default. Actually turn off co pilot because it's yeah, not today. And then, down here, I want to state current photo equals zero.
MISKO: I'm not actually sure what's going to happen with the event default. And the reason for that is because by the time you get the event, it's already asynchronous. Remember, everything here is async. And so, we have a different mechanism for preventing default. I don't think it matters in this particular case because it's a URL. But it's a little mental shift in the sense that everything is async. There's no such thing as sync anymore.
MISKO: Ignore the default for a sec.
JASON: I'm going to take this, and I'm going to run this a couple more times so we'll get it in here.
MISKO: Of course, you can do a map, right, to kind of automatically generate these.
JASON: That's a better idea. Why don't we do that? So I'm going to, and if I want a map, I can literally just 0, 1, 2 and the map like standard.
MISKO: Wouldn't you just want to map over the input array? The list as you add them? They should automatically?
JASON: What a great idea to just do the thing I already created. OK. So we'll link over the photo of what I really want is the index. And I'll mark that as unused. And then, we will return these. Get this and drop this. And then, that's going to be the pound index, right. And then, in the on click, I can set the index. And these. Now, we've got much cleaner looking code. I don't have the formatter set up for this. I probably need to enable something, so I'm going to leave that manual formatting for now. And, ta dah. So theoretically, this works because I did something wrong.
MISKO: Index three times.
JASON: Index. That's how map works, isn't it?
MISKO: It is. Why isn't it yeah. I'm confused. There we go. Line 46, you left zero in there. Line 46.
JASON: Oh. [ Laughter ] Oh, that makes sense. Hey! We got ourselves a working app. And I I believe
JASON: I think I broke it. What did I do?
MISKO: What did you do? I don't know.
JASON: Expected. Oh. Is it my formatting?
MISKO: Yeah, it's that A.
JASON: What did I just do?
MISKO: I would do apple z.
JASON: I know what I did, I tried to copy/paste this. Copy image link. That is the image link. I'm coming back here, I'm pasting in the dang image link, quit helping me. Yes? Good. OK. Let's try that one more time. And there's our images. So we've got a pretty basic, but it's definitely working.
MISKO: I think we should look on to the click listener, how it got transformed. You'll be surprised, I think.
JASON: OK. I'm going to click on reload.
MISKO: Click on the buttons, the network tab, the network tab, notice, it says 0, 1 over there.
JASON: Uh huh.
MISKO: Go to the network tab.
JASON: Network. Do the click.
MISKO: Click and open up, click on the download it. Oh, by the way, notice, you clicked, and you clicked on the one that's already selected so it never rerendered and never downloaded the rendering code for it.
JASON: Oh, that's right. It didn't.
MISKO: If you click on another one, it downloads the render code.
JASON: Look at that. OK, so that's really cool. That's smarter than I would have expected.
MISKO: Yeah, now look at what it generated, right. You're capturing two things inside of the closure. You're generating, capturing index in a state.
JASON: Mm hmm.
MISKO: Look at the source code. Index and a state and we need to recover that and we need to basically understand that. So if you look at your dom, right, and look at the click listeners. So this one says 0, 1, and 01 and you see they all point to, again, this is kind of convoluted because of the memory leak thing, but you can see they point to C, D, and E. So if you refresh without clicking on anything and you look at the end of the script type, that's JSON, this is, you know, you could count them down and be able to find, basically, the location of 0, 1, and 2, and should all be encoded in there. All of the information is here. One of the things you could do now is yeah. Basically, the whole state is in here. Here's your 0, 1, 2. You can see them over there. And all of that encoded in this state.
MISKO: And you didn't have to do anything. You just wrote an app the way you would write an app. You didn't think about it, lazy loading or server side rendering, or running this or that. Just the right thing.
JASON: It just does the thing. And that, I mean, that's and that's really like, that's why we reach for React, that's why we reach for Next or Astro or whatever we're reaching for because we need it to do the thing. My job is not to build a renderer, my job is to get this feature live so somebody can click a button and buy the thing. And so, having the ability to let the framework make these choices on my behalf so I deliver a great experience and I don't have to build the cleverness to do selective hydration or the cleverness to do, to not hydrate the thing at all because it's not interactive and just knowing that if I, you know, if I build the thing that doesn't need to be interactive, it just won't be. You know, that's one of the things I find exciting about astro, you explicitly opt into which pieces need to be interactive. And what, you know, what it seems like Qwik is offering a similar approach here that also doesn't involve the bootstrapping of React. Which is you say, I'm making this interactive by adding a click listener. When I click this thing, I want this to become interactive and Qwik knows to do that for me. And it's doing the laziness. It's doing the hydration, the resumability. Because of the state encoded in the HTML. That's really cool. Like, this is, this is you know, it's also very cool. I know that you can do this with any framework where you can kind of peek under the hood. But it's very cool it's understandable enough that we can peek into what tripped into the browser to see how things work. And it's not untangling. Granted, I'm sure the React code, you can bounce around and see it, too. But it feels light. This this feels like something I can approach without having to dedicate a day to dive in and see how everything plugs.
MISKO: This is completely intentional. If you want to do personalization, I know I touched on it in the chat. You need to be able to the strength in such a way that the client can go from it.
MISKO: Everything is laid out as strings and HTML. So if you need to modify or change, it's perfectly fine. Let's do one more thing, and then, if you're brave, we can do a trick that hopefully will blow everybody's mind off.
JASON: So just time checking, we've got 7 minutes, if we need to, we might have to jump straight to the trick.
MISKO: I can't show you the trick because the API got deleted and we haven't put it back yet. It was a good trick, though.
JASON: All right. That means, there may be a follow up.
MISKO: There'll be a follow up. The trick is this. You're running an application, you run this API, which is essentially what we do on the server, which is like, serialize the state of the app. And then everything gets shifted to the HTML. Then, you open up a new tab, you cut and paste the HTML from the old tab to the new tab. And the application keeps running where you left off.
JASON: Oh, that's really cool.
JASON: What's also fascinating about that, that opens the door for OK.
MISKO: That's exactly what happens.
JASON: Imagine this, right, if that is how Qwik works, I can start a session on my phone and just air drop my session to my browser and not have to, like, do anything else. It's just there. Right? Literally cross platform experiences just work. Oh, that's exciting.
MISKO: When it first came out, you would open up a VM, boot Windows inside the VM, and you could save the VM and move the file into a different physical VM and run it. And you don't have to boot Windows anymore. All the windows open with your middle of your text editor, et cetera.
JASON: Mm hmm.
MISKO: That's how Qwik works. Fundamentally. This is why I talk about resumable, right?
MISKO: Qwik doesn't have a concept of bootstrap. What does it even mean to bootstrap an app that's the same thing as boot as windows. Yes, but I want to skip that part, right? I want to go to the good part of interacting with it. And as long as you can recover all the memory
JASON: Mm hmm.
MISKO: You can do that. Continue where you left off.
JASON: That is extremely cool. And really gets my that gets my gears turning on, like, different interesting experiences you could build where, you know, there are things that, like, I'll give you a great example. I was just on an app that I was using my desktop, and part of what they wanted to do was this, like, camera verification thing that wanted, they were like, this is easier on mobile. I was like, I'm 30 minutes into this form, I'm not switching to mobile. I tried to do it on my desktop, it failed. How great if they could have said, you know, open your phone and go to this link and we'll just transfer your whole session over it and it works. So many really, really cool things you could do that just improve that user experience without having to build really complex machinery to make that work.
JASON: Very, very exciting stuff.
MISKO: This is Qwik, this is how Qwik gets from the server to the client.
MISKO: You can easily get to the tab to tab. That's the same thing.
JASON: Very, yeah, exciting. Yeah, exciting days ahead for the web, y'all. Stay tuned, buckle up, it's going to get weird, but it's going to be so much fun. You said you wanted to show one more thing before we doing the magic trick.
MISKO: Let's go to the network tab for a second.
JASON: Mm hmm.
MISKO: Export it, recovered state through the used scope and continue where we left off.
JASON: Mm hmm.
MISKO: Click on one. And so, what's the next thing downloaded? Let's see, that one was
JASON: Our main component. Because that had a re render.
MISKO: Why did it have to re render? It shouldn't have to re render. Why? Why? Oh, because you changed
JASON: The state. Because we updated the current photo state. I could have put
MISKO: OK. I see. The reason why this OK, I get what's happening here. It's line 50, at the bottom. It's line 51. You are doing the binding here. Instead of passing photo URL in alt, just pass in the state. Ask if you do that, then you'll see that the main component will no longer rerender, right? So passing the state, you have to update the photo. And then, that just takes the state.
JASON: What's the type for Qwik?
MISKO: Just type any for now. And then in there, the state, yeah.
JASON: And then, I'm going to replace this bit with alt. And we'll change that to source. OK.
MISKO: OK. Refresh the page.
JASON: Verification I did this, still works, OK. Here's my handler.
MISKO: Yeah. Click one. Notice only the photo downloaded.
JASON: Oh, see. This is, that's like the little stuff that matters. Look how much of a difference that made. So this, I can see being a challenge for folks like me who are used to React where it doesn't matter. So this will be a mental model shift. Remembering to move that complexity where it goes.
MISKO: Even if you do the wrong thing, right, remember, like, you wouldn't do the wrong thing and on 50 levels, you would do it on one level, right?
JASON: Sure, sure.
MISKO: At most, plus one download. And then, you would still if you had a relap. If you had hundreds of components and you did it on the wrong level, great, you have an extra thing downloading, but still, you've avoided tons of stuff.
JASON: Right. So, we're coming up on time and there was a question I saw a little while back, let me see who said it. It was a question about is there any router Shifter with Qwik?
MISKO: Not yet. But we're working on it.
JASON: This is maybe the biggest challenge, right? Is that Qwik is new. And so, all of this power is here. But you do have that challenge of, you know, incumbents like React have a huge ecosystem. So this is where we need the early adopters, right, folks interested in pushing the web forward to wade through this murky part where there aren't the full ecosystem of things. So that we, you know, you get the benefits of Qwik.
MISKO: I have two answers for you, you're going to love both. First, the easy one. We're going to have a function called Qwikify which takes the React component and turns it into a Qwik component. It won't get any of these benefits. But you will essentially, every time you do that, you will create what will essentially be the same thing as astro island. So that one component can be lazy loaded all of the benefits you would get.
JASON: Oh, yeah.
MISKO: You'll be able to take existing React, like widget libraries or existing applications and just use it out of the box. OK. That's one thing.
JASON: My incremental adoption path could be, I start by taking my whole React app, full thing, wrap it in Qwikify and now, I'm like double wrapping. I've got shipping Qwik to ship React, but then, I can incrementally pull pages out and slowly refactor the dependencies out without changing my components because they're JSX.
MISKO: And you go. OK. That's one. We need to talk about routing.
MISKO: Basically, there are two different ways of doing routing, there's the NPA and SPA, right? The multi page application and serial page application. And what it comes down to is where is the router? At the server? Or at the client?
JASON: Right, right.
MISKO: The routing happens on the server, the server gave you HTML. Now, if you navigate to a second page now and unfortunately, we don't have this working yet, but it's going to be a full refresh. New HTML coming in. Now, because it's just HTML, it is so quick, it doesn't matter, right? But it's a full refresh. But, what if instead of giving you a full refresh the server just sends you the inner HTML of the middle content so you keep the menus and the left nav and all of the stuff as is, but the server only sends you the inner HTML for the middle content. You entered that in there. And you continue running where you left off because Qwik has this magical property that everything can be serialized and you can continue execution where you started with. You end up in this strange world where, like, NPA/SPA difference disappears. What does it mean?
JASON: Unfortunately, we are out of time. I've got to wrap this up. So let me do a quick run of links here. Misko is on Twitter, you can go and follow there. If I didn't get to your question in the chat, please go ask. This is a link to the Qwik repo on GitHubs. So get down, digging into that. If you want to get to the Qwik docs, they're at Qwik.builder.io. We have a little bit about Partytown, we built that, I'll make sure that's published. And one more shout out to White Coat Captioning and Diane who has been with us all day. Thank you so much for being here and that's made possible through Netlify, NX and Backlight all sponsoring the show. While you're checking thing out, go to schedule, we've got Google Calendar and follow on Twtich if you want to be notified when we go live. I'll be doing things on edge functions next week, it'll be a little more casual. We've got Johanns coming in to talk about a whole bunch more. Head over to the schedule, mark your calendar and I'd love to see you, again. Misko, where should people go? Anywhere that I didn't just link if they want to do more with Qwik.
MISKO: Our home page is the best place, from there, you can, there's a link to Discord, come check us out on Discord and, you know, it is early stages, so expect a lot of sharp corners. But very nice Discord community, happy to help.
JASON: Excellent. All right. With that, y'all, we're going to call this one a success. Thank you all so much for hanging out with us today. We are going to have so much more good content. Make sure you stay tuned for when this episode goes live if you want to review. Misko, thanks so much for spending time with us today. This was so much fun.
MISKO: I had fun too, thanks.
JASON: We're going to see you all soon. Thanks y'all.
Closed captioning and more are made possible by our sponsors: