Video Summary and Transcription
The Talk discusses the recent feature updates in Vue 3.3, focusing on script setup and TypeScript support. It covers improvements in defining props using imported types and complex types support. The introduction of generic components and reworked signatures for defined components provides more flexibility and better type support. Other features include automatic inference of runtime props, improved define emits and defined slots, and experimental features like reactive props destructure and define model. The Talk also mentions future plans for Vue, including stabilizing suspense and enhancing computer invalidations.
1. Introduction to Vue 3.3 and TypeScript Support
Hello, UK! Today, I'm talking about the recent feature update in Vue 3.3. The focus was improving the development experience with script setup and TypeScript. Vue initially didn't support TypeScript, but as the user base grew, we rethought parts of the framework to accommodate complex applications. Good TypeScript support enhances cross-team productivity and long-term maintainability.
Hello, UK! So it's my first time in the UK, first time in London, super excited to be here! We had a bus tour yesterday, I didn't really have much opportunity to walk around myself yet, but it's really been a very eye-opening experience, a lot of history here, so super excited and super excited to connect with all the developers from here, and also from all around wherever you are coming from!
So today, I'm going to be talking about the feature update that's happening recently in Vue, and in case you don't know yet, Vue 3.3 was just released yesterday. So this was the first minor release in quite a very long time, so 3.2 was actually released more than a year ago, and one of the reasons for that was because we spent a lot of time working on Vite and other related projects. We invested a lot of time in, say, the IDE side of things, making the language tools, Volar and everything, braining up the whole development experience around the tooling ecosystem around the Vue core. But now we're moving our attention back to the Vue core, and you're going to see more frequent releases from Vue core after this one. So this is the start of a lot of new stuff that's coming. So let's take a look at 3.3.
The focus of this release was, again, improving the development experience when you're using script setup in single file components and TypeScript. Now, don't get mad at me if you don't use TypeScript. I think, obviously, when Vue just started we didn't even support TypeScript. In fact, a lot of the initial APIs weren't even designed with TypeScript in mind. So, over time, we had to slowly rethink parts of the framework to think about, oh, how does this API work with TypeScript? When we introduce new APIs, how will these new APIs work with TypeScript? And the reason for that is because in the initial stages when Vue was relatively small, our user base was mostly focused on relatively simple use cases. They were largely using a back-end framework and they just wanted some simple interactivity on the front-end. But nowadays, our use case has expanded wildly and people are building really complex applications with Vue. And in situations like that, when you have large projects, big teams, a lot of people working together with varying level of experience, having a type system and having good development experience with TypeScript is going to help you a lot with the cross-team productivity and long-term maintainability of your applications. So we believe this is a really important thing that we want to really nail, and there has historically been a few pain points in Vue when you're using Vue with TypeScript, which we believe we have largely resolved in this release.
2. Imported Types for Defining Props
Since the introduction of Script Setup, one of the most wanted features in Vue 3.2 is the ability to use imported types when defining props. The view compiler analyzes the types provided to define props and determines the props that the component expects at runtime. The compiler generates the correct runtime props list, ensuring that the app works.
So probably one of the most wanted features since 3.2, since the introduction of Script Setup is the ability to use imported types when defining props. Now this example here actually worked in 3.2 when you import a type from another file and use it in DefineProps with the type declaration syntax.
Some background here, the reason why we need to do something special with types here is because at runtime, a view component needs to know the explicit list of props that it is expected to receive. So without that information it won't be able to tell whether something it receives should be treated as a prop versus just a fall-through attribute.
This is the reason why the view compiler needs to look at the types you provided to define props, and then try to analyze what props this component expects to receive at runtime. Here, just by looking at the syntax, you'd notice that it's very easy to determine that message msg is the only thing that this component expects. It's deterministic, so the compiler says, OK, I've already figured out message is going to be an expected prop. Even if I'm not able to completely infer what this external type is, it doesn't prevent me from generating the correct runtime props list, so that your app will just work, right? Because in production, we don't really do runtime props validation based on these types here.
3. Imported Types and Complex Types Support
In Vue 3.2, the compiler lacked the ability to look inside imported types. However, in 3.2, we managed to support most common cases. We use TypeScript's API for module resolution and parse files to generate the proper type information. There are still limitations, especially for complex types, but for application-level code, it covers most cases. We're working with component library authors to improve their authoring experience and support their special cases.
But things become a bit more complicated when you, say, use an imported interface, and then you just pass it to define props like this. Now, in 3.2, the compiler was not able to really look into what is really inside this type, because up to 3.2, view's component compilation is always single-file context, which means the compiler only sees this file. So it doesn't have any information about other files, because it does not have the information about your file system, about your alias configuration, or the path configuration in your tsconfig. So if we want to really resolve this type and look into what properties are included in it, the compiler really does need to resolve this import specifier. It essentially needs to know which file we are in, resolve the relative path, and then we also need to take into account, say, if you are importing a type from an npm package, how do we resolve that npm package file? And how do we, say, if the user has configured custom path mappings in tsconfig, you can have random alias here that's pointing to any file on your file system, and we will need to resolve that the same way that TypeScript resolves the types. So that sounds like a pretty daunting undertaking. And to be honest, it really took us quite a bit of effort to make this work, but in 3.2 we actually have managed to support most of the common cases that you can run into. For example, when you are using a relative import, it's relatively simple because we know the current components file path, so we can just use this relative path to look for the other file. And then we need to handle caching and file invalidation when you edit those files, and then we also need to handle the cases where you're importing from an alias or an npm package, so we actually use TypeScript's own API to do the module resolution to get the exact same file that TypeScript is resolving to, and then we parse that file to get the proper type information in order to generate the runtime code you use. So it looks like a simple feature, but in fact it's quite involved and took us quite a bit of time to figure out how to do it. But even with this, there are still some limitations that we have to be transparent about, not just imported types, but we also added support for a limited set of complex types. For example, here we are using an imported type together with an intersection type with some additional props you might want to add to this interface. We can also do interface extends, or sometimes you may use utility types like required or optional, built-in utility types like that. So we tried to account for these types as much as possible. For example, this example here will just work, because we are able to resolve whatever is in props, we are able to resolve whatever is in here, and we are able to just merge them together. But eventually the whole analyzation process is still AST-based, we are not actually using TypeScript itself to do all the type inference and everything, because that would be really expensive. It's kind of a pragmatic approach where we try to support as much as we can by just analyzing the AST, so it will not be able to cover 100% of the types, so if you have crazy complex types with conditional types, conditional infer, or crazy mapped types with generics and everything, some of them will not really be fully supported. But for the most common cases, this is probably going to cover most of the cases you would run into in application-level code. So the advanced type cases usually happen when you're writing a relatively advanced component library, and we're still working with component library authors to figure out what special cases they may need so that we can improve the authoring experience for them and also the consumers of those component libraries. So this is going to still be an area we're going to continue to invest in.
4. Generic Components and Defined Components
The next big feature in Vue 3.3 is generic components, which allow you to express the type of selected items in a list component. You can use generics, similar to TypeScript, to define the type parameters and constraints. We also introduced a reworked signature for defined components, enabling library authors to use generics directly. This provides more flexibility and better type support when writing complex components with JSX or TSX.
The next big feature that we enabled is generic components. So if you are not familiar with TypeScript or generics, think of it as a... Let's use this component as an example. So you're building a list component. This list component is going to receive a list of items, and it's also going to receive that item which is selected. So at the type level, you want to say the selected item is going to have the same type as the item in that list. So here, the problem is, I don't know what exact type it's going to be.
So generics allows you to express that. So here, we're just going to add the generic attribute to our script tag here. So this is going to be exactly the same as when you're using normal generics in TypeScript, you have this angle bracket, and you put the generic type parameters in there. So you can put anything you can put in those square brackets in here. And you're going to say, we're just going to have a generic type, T, and I'm going to accept a list of items that's going to be an array of T and a selected item, which is going to be T. So as I said, you can do anything that would work with normal generics, which means you can have these extends constraints, saying this T needs to satisfy this type constraint. Or you can have multiple type parameters, and then you can use them together. So this is just a really contrived example. Don't try to make too much sense out of it. This is just to demonstrate the syntax possibility. So anything you can do with normal generics in TypeScript, you should be able to express it here.
And we also shipped a kind of half-baked thing about generic components with defined components. So this is more oriented towards library authors, because if you've used Vue, you know Vue can actually work with JSX, and you can also use TSX with Vue. And this is, in some situations, preferred by library authors when they write really complex and dynamic components for you to consume. This gives them a bit more flexibility, but they also run into the situation where they need generics, and previously there's just no way to do that. Or they have to resort to really really complex hacks, typetricks for them to be able to express these things. So we reworked the signature of defined components to allow you to directly pass a function with generics to defined component. And it works kind of like React function components, with the difference being that, instead of directly returning JSX or a render function, or virtual nodes, you want to return a function that returns JSX. So the difference between this and React hooks, if you've used React, obviously you would know that the difference here is that Composition API code runs only once when a component is created. It does not get repeatedly called, however this return function here, the render function, is going, does get repeatedly called. That's the reason why I need to separate them, but this gives you a pretty nice compromise between being able to use Vue's reactivity system, with TSX, JSX, or manual render functions, together with very satisfactory type support, with generics. One thing missing here is that we do need to provide the same generating runtime props list based on your types support. Here's what to do here.
5. Automatic Inference of Runtime Props
We're developing a Babel plugin to automatically infer the runtime props list and inject it into components. This will eliminate the need for manual double declarations and reverse engineering in component libraries. Our long-term goal is to allow TypeScript users to focus solely on types without worrying about the runtime props list.
We are working on a Babel plugin that's going to be used alongside our JSX plugin so that you can, when you use a type only props declaration here, it's just going to automatically infer the runtime props list and inject it into this component for you. You don't need to double declare everything. One of the implications of this feature is we hope that some existing libraries that are using TSX to author view components can migrate to this and then we're able to just ship types only for all props between both the component library and the application. So there's no more need for you to manually write a list of runtime declarations than somehow reverse inverse the types from it, which is a very common practice in component libraries right now. So that's one thing we try to want to resolve. Eventually we want to get to the point where if you are using typescript, no matter if you are a component author or a component consumer, you should be able to just care about the types and without caring about the runtime props list any more. So that is a long term goal we hope to achieve.
6. Improved Define Emits and Defined Slots
In Vue 3.3, we introduced more ergonomic define emits, allowing you to declare event types using labeled tuple syntax. This feature is inspired by Vue macros, developed by Kevin, who is now a core team member. If you're interested in exploring experimental ideas, check out Vue macros. Another improvement is the ability to define slots with types, enabling better type checking and error detection in the IDE.
So another feature in 3.3 is the more ergonomic define emits. So if you've used define emits previously, you probably have found the signature to be a little bit weird. The reason is this, in fact, is the exact type for this emit function you are going to get back here. It's just a little bit awkward to write. So here we are doing some little internal type conversions so that you can just say we have a foo event, which is going to accept this. So this syntax may look unfamiliar to some. This is, in fact, called labeled tuple syntax in TypeScript. So if you ignore this ID label here, this is just a normal number, a tuple with a single element that is a number. But TypeScript actually also allows you to label your tuple element. So you can give each element inside a tuple a name. So you can say this first element is going to be ID. And the purpose of that is when you look at the type from somewhere else, it's gonna give you better information. You're gonna say, oh, the first element is the ID, the second element is that. So it's kind of like an arguments list, which is the exact purpose of what you want to declare in here. So we just internally convert this type to give you the correct emit function type back. If you're using Vue 2, this is in fact supports Vue macros. In fact, this feature is inspired. We just took the feature from Vue macros. And the author of Vue macros, Kevin, he is now a core team member. And he has been actively working on a lot of experimental ideas in Vue macros. And if you are on the more explorative side, you're okay with trying new things that may or may not work, you should check this project out.
Then there's types lots with defined slots, right? So one thing that we kind of have always had a bit of a problem with is that slots are not expressed as part of the components props, because everything happens in the template. Previously there was no way for us to express them with types, but now we provide you with the ability to do that. So this defines slots macro. The syntax here requires you to know a bit about how slots are represented internally in Vue. So when you pass a slot from a parent to a child component, they are internally represented as functions. So a slot passed from the parent is in fact a function that's being passed and it's going to be called by the child. So here when you define slots you are going to represent them as functions, and a slot function receives its slot props, which the child component can pass into the parent slot. So when you declare slots in a component like this, the IDE is going to essentially detect that you're declaring... if you happen to make a typo, and say slot name equals a name that does not exist here, it's going to give you an error.
7. Vue Slot Props and Define Slots
The slot props in Vue 3 provide type checks for the default slot. However, the required slots check is not yet implemented. Vue macros can be used with Vue 2.7. Define slots do not have runtime implications, only type level checks.
Say oh, this slot, you haven't declared this slot, you are probably making a mistake, and it's going to give you type checks for the slot props if you pass a message to the default slot and it's not a string, it'll yell at you. And also in the parent, when you consume this component in the parent, and you use a slot you receive the message from the scope slot, it's going to give you the string type correctly. So currently one thing that's still missing is the required slots check. As you can see, we do have the ability to differentiate between a required slot versus an optional slot with this. Currently the required slot check is not implemented yet, but it's planned and it will be there eventually. And again, you can use this in Vue 2.7 with Vue macros. The IDE support in Volar actually supports both Vue 2 and Vue 3. And also, define slots doesn't have really any sort of runtime implications, it doesn't do any runtime checks. This is purely for type level checks that's going to be used by Volar and Vue TSC. You do get the same checks when you are just running the type checks from the command line as well.
8. Reactive Props Destructure and Define Model
Here, we are introducing an experimental feature called reactive props destructure. By default, Vue tracks reactivity through dot property access. However, when you structure a prop, you lose reactivity. With the reactive props destructure feature, you can destructure props with default values while maintaining reactivity. This feature automatically adds dot access at compile time, providing a cleaner syntax for declaring default values. Another experimental feature is define model, which simplifies the process of creating custom inputs with model support. Instead of manually declaring props and events, you can use define model to generate a ref that behaves like a normal ref.
Here, we are going into some experimental territory, so reactive props destructure. How many of you have heard or tried reactivity transform? No one? So, reactivity transform was an experimental feature that we tried for quite a long time but eventually decided to drop. If you're not familiar with reactivity transform, then we might need to provide some more context here.
So, the first thing is in Vue, by default, all the reactivity magic happens by when you are using a property in your template, using something inside a watcher, Vue automatically tracks them. And the way Vue tracks them is by tracking these dot property access. So, when you say props dot foo, it in fact triggers a getter or a proxy trap internally, in which Vue performs the magical tracking. Oh, ok, the time is actually... I need to go faster. But the idea is, by default, everything needs to happen through these dots. Which is why if you currently structure a prop, define props like this, you actually lose the reactivity. This foo, this structure here is just going to be a custom that's never going to change. But what if we can make it reactive and that's exactly what we're doing here.
So, if you opt into the experimental reactive props to structure feature here, you can destructure with default values in everything and this foo will stay reactive, say when you use it in a computed property, when you use it inside a watch effect or you return it from a getter, it will stay reactive. And the trick is really simple, we just compile this at compile time to something like this. So, we just like automatically add the dot access for you so that you don't need to write it yourself. And it gives you, and the main reason for we want to do this is because it gives you a such nicer, cleaner syntax when declaring default values. If you've tried to declare default values with define props before you know you do this with defaults, which is really awkward, this is something we really want to get rid of and this gives us a way to do that. But again, this feature is a little bit controversial in the sense that it does introduce this new compiler magic that you need to understand a bit more about how v-reactivity tracking works to understand why this works, but hopefully the DX improvements that it provides will eventually be worth it. This is just demonstrating that if you use this destructor prop inside a watch effect, it's going to be tracked, just like props.message. Every time the parent props change, this console.lock will lock again.
Now, define model is another experimental feature. If you've ever authored a component, say wrapping a native input, and you want to have a custom input with more advanced features, but you still want your component consumer to be able to use the model with it. Previously it's quite verbose. This is what you need to do. You first need to declare a prop called modelValue, then you need to also have a corresponding event that's going to be emitted, and then you need to do your manual wiring here on the native input, and eventually piece everything together. So with 3.3, this is what you need to do. You just say define a model. What it gives you back to you is a ref. This ref just works like a normal ref. You can mutate it.
9. Mutating Refs and Advanced Level Usage
When you mutate it, it just emits the event back to the parent for you. And because it's a ref, you can bind it to an input with the native v-model. And then there's define options. And there's this little bit of advanced level thing. If you have used composition API and worked with composables, you know Vue-use. When you use Vue-use, it can accept a normal value or a ref. But the problem is, if you want to pass a deeply nested property, it becomes complicated. We provide a new function called toValue to normalize everything to a value. And there's the JsyncPoSource, which has potential implications for TSX.
When you mutate it, it just emits the event back to the parent for you, and if the parent value changes, it'll just update the ref for you. And because it's a ref, you can actually just bind it to an input with the native v-model. So then you can work your additional logic on top of this, so it makes it a lot simpler to wrap a native input to build your custom input components on top of them.
And then there's define options. So when you use script setup if you still need to declare additional options, previously you need to use a separate script block. Now you just use the macro so you don't need to choose separate blocks.
And then there is this little bit of advanced level thing. So if you have used composition API and you've worked with composables, anyone here have used Vue-use? Great, so when you use Vue-use, you know Vue-use usually can, when you want to pass something into a Vue-use function, it can accept a normal value or a ref, so at the type level we can think of it as something called maybe ref. You can pass a value or a ref, but the problem with that is, for example, if you have a scenario like this, you have props and you say I want to pass props.Vue into it, but I can't just say use feature props.Vue, because in that way I'm just passing a value that's not going to stay reactive. So what we had to do previously, is you're going to either use two ref, so you say two ref propsVue, you're creating a ref out of propsVue and then passing it into the function. And the problem with that is two ref, the previous signature, isn't entirely flexible. The idea here is, what if you're trying to pass in a ref for a deeply nested property? So you want to say tworef props.Vue bar. Now this is already starting to look a bit weird, and another downside here is it doesn't handle the case where if the foo may or may not be there. So you say, okay, I can refactor this, I can use a computed property. Now I'm just going to say props.vue?bar. Now this will handle what, no matter if foo is there or not. The problem is computed, sometimes we may just use computed without thinking too much about it, but internally, computing has to create a watcher that keeps track of things and invalid values and all that. The easiest way to do it is if your composables actually just support accepting a getter. So we provide a new function called toValue for you to do that. What it really does is inside your composable, you're going to say foo is not just maybe a ref or a getter. And toValue just normalizes everything to a value, no matter if it's a value or a ref or a getter. It just normalizes it. So you can do this and you don't need to care what foo is. It can be anything that the user wants to pass and it'll just normalize it. And if you think about the normalization directions, in the middle you have maybe, ref, or getter. On the left, we can use toRef to normalize every one of them into refs. On the right, you can use toValue to normalize everyone of them into values. So it's just a two-way street.
Okay, so then there's the JsyncPoSource. So I want to mention this mostly because this does have a potential implication if you're using TSX.
10. TSX Support and Vue.js 1.0 Beta
If you use TSX with Vue, opt into this now to avoid any issues when the default global registration is removed in 3.4. Vue.js 1.0 beta, the static site generator, will be released soon. Vitepress is recommended as the best documentation tool.
If you don't use TSX, it doesn't really concern you, but if you use TSX with Vue you want to opt into this now so that when we remove the default global registration in 3.4 it's not going to affect you in any way. So yeah, and what's next? Vue.js 1.0 beta very soon now that we have 3.3 out. This is the static site generator. If you've read the docs, the Vue.js docs, the Vite docs, RollUp docs in fact, a lot of these new projects, their documentation is built on Vitepress and if you're having a project yourself, you want to document, try it out. I personally believe it's the best documentation tool out there.
11. Vue Future Plans and Exciting Features
We're planning a round of Vue core issue PR cleanup and prioritizing important issues for the next minor sprint. In 3.4, we aim to stabilize suspense, improve safe teleport, and enhance computer invalidations. We also have exciting plans for Vapor mode and additional platform features. The native app scoped proposal in the CSS Working Group will simplify Vue's scoped style implementation. Async context will track component initialization in async call stacks, and DOM parts will be a crucial compilation target for VaporMode. These developments will make VueCorp simpler and more efficient.
Next thing is we're going to do a round of Vue core issue PR cleanup. We do have a bunch of P4 important issues that we want to tackle before the next minor sprint. Then in 3.4, some of the things you want to prioritize are to stabilize suspense, some improvements, building safe teleport and more efficient computer invalidations. The goal for future minor releases is we want to keep them smaller in scope, so that we release more often, release faster. In Q3 and Q4, we're going to continue working on Vapor mode and there are some additional platform features to keep an eye on. If you are interested in how the future of Vue will interplay with new use of platform initiatives. One exciting thing is the native app scoped proposal that's going on in the CSS Working Group. So in short this feature will allow us to completely implement Vue's scoped style implementation to make it much simpler and more performant, which is a great thing. Less inside the framework and more just leveraged in the native platform features. And then there's async context, which allows us to track the current component context in an async call stack. So async setup, async methods and async pnet actions, we are going to be able to know which component initialized it. And then there's the DOM parts, which is going to be an important compilation target for VaporMode. A lot of these are ranking from closer to materialization and to DOM parts, which is super early stage. But these are the things that we're keeping an eye on, we're excited about. We believe they'll help VueCorp to be simpler and more efficient.
And that's it! Thank you. Thank you all so much. Please step into my office. Remember, if you do have any questions, you can head over to the Slido and ask them. And while we wait for people to get on their phones and ask a few questions, I have a question for you. You just announced Vue 3.3, and you have so many different features, some of the features that you talked about today. How do you prioritize which ones you want to work on, and what features are you most excited for?
You mean inside the release? Yes. I'm personally more excited about the external type support, the imported type support, mostly because it has been quite a technical challenge for myself, and for a very long time, we just doubted whether it's even possible, but we eventually made it happen in 3.3. So that's kind of a good thing for me to, I feel kind of relieved to be able to get that out of the door. And then overall, I think all these features together kind of gives us a pretty good confidence to say Vue single file components, especially with script setup, now works really well with TypeScript. You're going to have a really good DX when you're using TypeScript with Vue. So that's the most important thing for me. Nice. That's amazing. So we do have some questions coming in.
12. Vapor Mode and Unref Compatibility
The Vapor Mode in Vue 3 is a new way to compile templates into a more memory efficient and performant code. It's compatible with the existing component format and offers a lighter and faster development experience. However, it has a limited set of supported features. Additionally, the question about changing unref to support getters is not possible due to the current API design and the potential breaking changes it would introduce.
Some questions, what is vapor mode? Right. I've mentioned this a few times in past conference talks. So right now, the way Vue works is we compile your templates into JavaScript. And the JavaScript that we produce right now are still called Virtual DOM render functions. So there is still something called a Virtual DOM runtime. So in Vue 2, we sorted to Virtual DOM mostly because we wanted to provide server rendering capabilities. In Vue 3, we realize Virtual DOM do have some memory overhead and runtime overhead when it comes to performance, so we are already doing a lot of compiler time optimizations to make the Virtual DOM code generated as efficient as possible. But still, it's not going to be as good as if we can skip the Virtual DOM entirely and use a completely different code generation strategy.
So Vapor Mode is essentially this new way. It's going to take the same component format, the same single file component with the same template syntax. Nothing needs to change. It's going to initially support a subset of the features, but it's going to be 100% compatible. We just take the same template code and compile it into something that's going to be quite different underneath. So something that is going to be much more memory efficient, much more performant, and generates lighter weight, it requires less code at runtime as well. So overall, it's something that's going to be largely transparent at the development experience level, so you just opt into it. When you opt into it, obviously you're going to be limited to a set of features it supports, but it's going to be the same view experience. But the code that it generates is going to be magically lighter, faster, and more efficient. I love it when things just magically get lighter and more efficient behind the scenes.
So thank you very much. We have another question as well, and I think I'm going to read the last line of this question first, which is, it looks awesome by the way, but why not change unref to support getters so that existing composable implementations already support it? That's a good question, in fact. So if you think about it, ref and unref are the two counterparts. So what happens when you pass a function to ref? Unref is it will actually create a ref using that function as its value. So unref has the same issue. So if unref receives a function, right now what it does is, the current API premise of unref is, let's say, if something I received is a ref, I'm going to return as value. If it's not a ref, I'm just going to return the value as is. And a function actually falls into the not a ref category here. So if we change unref to just call the function and return its return value, then it's actually a breaking change. So unfortunately, I thought about supporting that in the first place. But we realized, oh, it's a breaking change. And we can't do that.
Vue API: toValue, TSX, and Composition API
We introduced toValue as a new API that functions similarly to unref but calls functions. When implementing recursive and dynamic components, TSX is preferred for highly dynamic use cases. The Composition API is recommended over the Options API for building large-scale, maintainable codebases, thanks to its better TypeScript integration and composability.
So we need a new API for that. So which is why we introduced toValue. So toValue, essentially, you can think of it as unref, but it just will call functions. Yeah. Thanks. Thanks. And thanks, Canan, as well, for asking that question.
We have another one coming in from Mitch. If trying to implement recursive and dynamic components, would you recommend TSX instead of using an SFC? Using CSS? TSS. Oh, TSX. Recursive components? I don't think there's a fundamental difference here. Usually, when people resort to TSX, it's because they have to deal with a lot of rendering a bunch of dynamic components based on a data structure, typically. This happens a lot in configuration-based components like a table component that's going to receive an options object to specify what fields to render in the header, what rows are going to show and all that. Everything is highly dynamic, and you're really going to have a hard time expressing this kind of logic using the template syntax, but using TSX allows you to, say, programmatically build up the component tree based on these option values, and that's the most common case where people prefer TSX. Recursive may fall into that, but if it's only recursive, you don't really have to, but it really depends on how dynamic your use case really is. Thank you. Thank you for the question once again. We have another one as well. And folks, you can upvote questions as well to make sure that we ask all of them, as many as we can. We have so many questions, and if we don't get to any, make sure you catch Evan upstairs. But moving on, we've got another question which is asking about the composition API. So would you say that Vue is shifting in favor of using the composition API by all the new features that you're releasing to stop encouraging using the options API at some point, or does the options API always have some cases where it will be encouraged over the composition API? So right now, to be 100% honest, I don't think there will be cases where I would recommend options API over composition API. So the clear advantage of options API is, I would say it's diminishing, especially when TypeScript is considered. But in reality, our productivity sometimes is not a completely objective metric. It can be associated with your subjective preference. When you are happier with the API, you are more productive. So if you have strong personal preferences with API styles, I would not say you should work against that. But if you think about it from a purely pragmatic perspective, you are saying I want to build a large scale, maintainable code base. Then I would recommend going with Composition API, mostly because of the better TypeScript integration and the composability, the ability for you to take a component you've written and extract parts of it, reorder things, and reuse things across components. In general, Composition API lends itself much better to these practices compared to Options API.
Tips for Working in a Development Team
If you're personally fine with either strict procedures or more flexibility in a development team, it ultimately depends on the team dynamics and mutual agreement. Some level of code convention and enforced rules is necessary for long-term productivity, but being too pedantic can hinder progress. Finding a balance is key.
So it's a better long-term bet. But if you're just doing your personal thing and you say Options API still feels nice, and you've not really encountered any limitations with it, then by all means you can go with it. I love how you include your happiness or developer happiness in the production or in the development cycle, to be one of the factors for why you would pick one over the other.
Which kind of just leads me nicely into the next one, which is what are best tips for working together in a development team. Would you prefer strict procedures or more flexibility? Like, should people be flexible and kind of happy in their own environment? Or strict and we're going to build the best code that we can? I think this has a lot to do with the team dynamics in the first place, right? I think, it depends on like, when you have a team, if you have a team made of people with very different opinions and they are also strongly held, then it's not going to be productive either way, right? So the important thing is that the team needs to be on the same page on what they mutually agree is something they can accept and work with. Personally, for me, I think it's really going to be a team decision. If I am, say, taking a job and join a team, I am personally going to be fine with either direction. It depends on what the team leads decide. I think some level of code convention and enforced rules is necessary if you want long-term productivity. It's just like you don't want to be super pedantic about it. I would make an example, right now, the way I format my code is I use Prettier and I catch errors with TypeScript. I don't really use ESLinks that much anymore. So if I join a project with Airbnb ESLink rules, I am going to be like, no. So that's the pedanticness I am talking about. And on your teams, on that scale, where would you say your teams sit? Probably a bit on the more flexible end.
Vue and Compilation Magic
The trend across all frameworks is towards more compiler magic. React Server Components, SvelteKit, and Solid Start also rely heavily on compilation. The reality is that we are moving towards a more compiler-powered framework scene. Angular is also exploring single file components. I'm not worried about Vue diverging from the JavaScript ecosystem because everyone is embracing compiler-powered frameworks.
Nice. Another question, would you worry that Vue is adding too much compilation magic and will slowly diverge from the rest of the JavaScript ecosystem? You've got sort of the JavaScript ecosystem and then all of these bubbles. Are you worried at any point that Vue would diverge away from that? I don't think so. In fact, if you look at the general trend, the overall trend across all frameworks is there is just more and more compiler magic. React Server Components requires a lot of compiler-type magic. You know, Server Actions, which they just recently introduced, SvelteKit, Svelte is arguably even more magic in terms of compilation. Right? Solid Start also has a lot of compile magic involved. So, I think the general trend is people are, whether you like it or not, the reality is we are moving towards a more compiler-powered framework scene. And in terms of the – and if you also look at Angular, there's a lot of interest in the Angular community right now of exploring single file components for Angular. So, I think, I would say, no, I'm not worried about that, because everyone is doing more compiler stuff nowadays. It's like what you said, do the things in the background to make it more efficient. I really appreciate when you do stuff like that for us.
Comments