-
Notifications
You must be signed in to change notification settings - Fork 546
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Function-based component API (extended discussion) #55
Comments
I respect your opinion so please don't take any of the following as being personal.
I couldn't disagree with this more. Logic composition is probably one of the most serious problems in terms of scaling projects. Quoting myself from another thread:
Vue started small, but today it's being used in a very wide range of projects, with varying level of complexity and business domains. Users dealing with different types of projects will run into different needs, some can be easily dealt with using the object-based API, while some cannot. The primary example is
For (1), each logical task is forced to be split between option types. For example, a single data-fetching task may need a prop, a data property, a computed property, a mounted hook and a watcher to work together. This means when you pick up this component and try to understand its data-fetching logic, you are constantly jumping up and down in the options list trying to locate the pieces that are related to it. At the same time, when you skim over a property, although you know what type it is, it's quite a bit harder to tell which logical task it is supposed to be dealing with. This gets worse as more logical topics are added to the component. In comparison, with the new API, all related logic for data fetching can be grouped together, and more importantly, cleanly extracted into a separate function, or even a separate file. An analogy for this problem is file organization in projects. Many of us have come to agree that organizing files by file type (e.g. splitting everything into Point (2) has largely been explained in the Motivations section of the RFC, showing that it achieves what mixins/HOCs/scoped slots can achieve without any of their drawbacks. With React Hooks, we discovered some of its characteristics could help those users solve these problems described above. This is the fundamental reason for us to come up with this proposal. It is indeed a "new thing", but we are adopting the new thing because it presents a solution to objectively existing problems, not just because they are "new". In the long run, the availability of this new API will pay huge dividends in the hours saved for the developers dealing with the mentioned problems. Type safety is also an important consideration - again, this is something many users wanted badly but may not appear valuable to those who do not use TS. That's understandable - but I think it's a bit selfish to claim that it's not solving any problems because the problems being solved does not affect you.
"Breaking" is defined by users forced to change their code. Since users will not have to change their existing code, it is not breaking. I don't think there's anything else to argue about in this aspect. If even backwards compatibility is not enough, then you are essentially saying a project should never introduce any radical new ideas, ever. I think that's project policy level argument, which if I get to vote, I will firmly vote against. We will try our best to keep the best interest of our users in mind, but the project must and will evolve.
On the contrary, the very motivation of this proposal was to improve the maintainability of long-term Vue projects. If we look at any JavaScript project, all code starts from an entry file, which is essentially an implicit "main" function being called when your app starts. If having a single function entry will lead to spaghetti code, then all JavaScript projects should be spaghetti code - which is obviously not the case. Why? Because as developers we've learned to organize our code by splitting it up, either into modules or into smaller functions. A core characteristics of the function-based API design is that understanding code in I agree that with the new API you have a theoretically lower bottom threshold for code quality, but as mentioned that can be mitigated by whatever you are already doing to prevent spaghetti code in non-Vue parts of your codebase. On the other hand, code written with the new API also has a substantially higher upper limit in terms of code quality. Any code written with the new API can be refactored into much higher quality code than their options-based equivalent, whereas with the options-based API you will have to resort to mixins and deal with its drawbacks. I also want to point out that this RFC is not about trading simplicity for maintainability. We should be aware that we are comparing the impressions between an API you've probably been using for years vs. an API you've just seen for the first time. Fundamentally, this is a shift of how you think of a component:
What many users are lamenting when they talk about "losing simplicity" is in fact losing the ability to inspect a component by option types. But with the new API, it should be quite straightforward to implement a component analyzer that provides a view that allows you to inspect the component by property types. That is to say we would be able to look at our component from both perspectives, whereas with options-based API you are limited to one (since the intention about logical topics is lost when split between options).
I'm getting tired of repeating "nothing is being thrown away." But let's try to define what "things that make Vue good" really is. Many users against this RFC seem to define that as the object-syntax, and as if taking the object-syntax away takes away everything that makes Vue Vue. But let's take a look at what is left intact:
Vue's object syntax has existed since day one. Many of the above were added later along the way, and each contributed to Vue's growth. If you believe the object-syntax is all that matters to you, I would kindly ask you to take a step back and rethink what really makes Vue what it is. After all, this RFC is not that radical a change as it may seem. |
The new API solves these problems:
I wrote an article many moons ago about the "separation of responsibilities" Vue affords the community and I knew at that time, it was why Vue was so cherished by those who "understood" it. It's why I love Vue too. Yes, Vue was also my favorite and still is, because every time I leave Vue and work even a little on some React project, even a simple to-do list, it feels......icky. So, I get it. I do understand the feeling of having been let down, by leaving Vue's "norm" of object option API. But, I also understand the benefits of the new API. I wouldn't consider myself an advanced programmer, and I'm learning more and more. I'm on the Quasar Framework team and I'm only mentioning that, because here and there, I get to work on bugs and dig into sometimes complex components. For instance, just the other day, I dug into QEditor to fix a link button/ link editor issue. It was hard for me to get into that component's code, because so much of the feature's code I needed to look at was split up into the different options. Some of the code was in a mixin, the rest spread out across a 400 line component, using a render function. It was hard for me to get the gist of the whole feature of the link button and the link editor. In the end, I fixed the bug, but my solution wasn't elegant and of course, Razvan, the founder and lead dev of Quasar, came up with a much more elegant (and cross-browser complete) solution. Thing is, I know for sure, had QEditor been built with a more "feature based" code design, where the link button and editor were more encapsulated, I'd have been able to understand the inner workings of the component much faster and might even have come up with a more elegant solution. I also wrote an article asking users to actually dig into the source code of Quasar. But, I also understood the "nah, it's wonderful magic, I don't want to learn the tricks" kind of mentality. And it's Vue's option object magic that more advanced developers want to break. And, unfortunately, it's also that kind of magic that makes Vue so wonderful. I realize that story and my impressions/ opinions aren't going to persuade many users, who haven't gotten into that kind of depth of component code yet. And that is more than likely 80% of Vue's user base. However, the things most of you are counting on, the component frameworks you are using, could very much take advantage of better code encapsulation. So, maybe we can all see this as a good addition (and most importantly, always an addition) to the current API for more advanced users???? One thing that also just hit me while writing this reply was this. We are all happy with Vue's template DSL, which is staying despite the new API btw 😄. When I reread this part of the RFC......
......all v3.0 is doing is the same for HTML. It's a (rather simple) DSL for getting Vue's reactivity into standard JavaScript. I'm not sure how many of you can agree with that, but if you look at the 7 function + the lifecycle hook onXXX additions, it's really not that much to learn or use. It's most definitely easier than React's hook system (and also doesn't have it disadvantages). And in the end, Vue's SFCs, even with this new API, is absolutely nothing like React. Why people say that, I have no idea. What is similar to React is offering more flexiblity and yes that means more responsibility on us, the users of Vue. But, it's still Vue and it's still better than the rest and now it's even more awesome. Scott |
@smolinari totally agree... digging into Quasar's complex components and finding something among all kinds of EDIT: in case of misunderstanding: I am talking about the flaws of mixin mechanism. Nothing to do with quasar ;) |
@beeplin - And just to be clear, you would also agree when I say, Quasar isn't poorly written either. It's Vue's built-in constraints, which make it that nightmare. Scott |
I think the elephant in the room is that the problems this RFC solves (logic composition and better type support) are legitimate but not faced by the community at large. I have used Vue exclusively for frontend work since 2016 (from small one-off things to large enterprise projects), and I have yet to face any of the problems this RFC solves. I admit that mixin usage can be a problem for large codebases (see Vuetify) so I just avoid using mixins. There are just better ways to structure large modular codebases. |
Hi guys and gals, not sure if this is the right place to comment on the RFC (if not let me know!) but here goes: What's great about the current options based API is clarity and opinion. It's clear what is framework hook/code and what is your code. What's bad about the new proposal is, it lacks opinion and everything is kinda just mixed in there. It's not as clear nor as friendly. Having said that, I 100% agree this is solving a real issue, which is separating different concerns of your component. And I also agree mixins don't kinda work. However, could we find another middle ground? In this way, could we not think of the options API as having many concerns? So, you could perhaps have many I realise this is just an off the cuff idea, but could be path towards solving the concerns issue while still maintaining the API friendliness and simplicity of vue! Thoughts? or... shall I just "get my coat" (reference to an old British sitcom 😬 ) |
That IS the whole idea of the function-based API. It takes the options you mentioned and makes them functions, which need to be called within a @yyx990803 - If you could just say, Vue will offer both APIs forever. Then all this will stop. The fear comes from the greatness of the options API possibly being lost at some point (doesn't matter when). It is what makes Vue so attractive to beginner, intermediate and maybe even early advanced programmers (if that is such a thing, but you get the point). You did make a great API and again, the fear is, you want to make it into something different, something where those who love Vue aren't seeing or even contemplating finding that same love with the new API. Those others aren't seeing the advantages of the new API and in fact, are seeing it as something "ugly". I too still feel that sentiment. It doesn't really look elegant to me either. But, I can overlook those "warts", as I do see the practical advantages. If you could find a better more "elegant" way to have a function-based API, next to the options API, you'll probably be hitting the home run it should be. 😄 Your "programming art" just needs to be more "pretty" and less "abstract". 😉 Scott |
@martinsotirov I'd be curious to hear about "better ways to structure large modular codebases" when using the options-based API, especially how they allow you to avoid all these mentioned problems in a large enterprise codebase. |
Export shared functionality in a separate vanilla JS service class that you just import in every component that needs it. Much easier to track things later than trying to figure out where X or Y was inherited from. Structuring code in a way that avoids mixin usage also forces you to rethink your architecture and discover potential problems, similar to how writing unit-testable code forces you to think about your code. And in many other cases it is better even to reimplement a nuanced version of some functionality instead of blindly following the DRY principle, if it helps keep clarity of the codebase better. |
I don't have as much experience with Vue.js as I have with React.js so take what I am going to say with a pinch of salt. Though I absolutely like this proposal, I think one point has been really wrongly formulated. One advantage of Vue.js is also one of the advantage of the Go language: you have "one way" of doing things, instead of 20 different ones. Whereas React falls short is this aspect: classes/function components, hooks/higher-order-components/decorators for example, are just solving the same problems in 2 or 3 different ways. Yes, they are all supported and nothing is deprecated, but when opening a file, you will never know with what concepts the developer wrote this component (redux or providers? HoC or decorators? etc..), and the cognitive effort to switch from one to the other is not trivial. Of course this could be enforced by team decisions, but still, the point remains. Here is I think one of the key of the ranting going on: giving more tools/flexibility to the community also gives more tools to fracture how everyone will write Vue components. I don't think there's a solution to this problem, but I just wanted to share that. |
@martinsotirov I think what you are doing is essentially side-stepping the options-based API and introducing a custom pattern, whereas the function-based API provides first-class affordance to such logic extraction. Reactivity inside service classes are also somewhat implicit (I assume you are returning class instances in |
If you get to the point of having to reuse things tightly bound to the component (side effects etc.), then you are doing something wrong and have to rethink your architecture or break up the component. That's the benefit of avoiding mixins, at least for me – it forces me to write simpler and better structured components. |
@martpie Vue is already providing a lot of different ways for doing the sames things, and it's been fine!
|
@martinsotirov Sounds a lot like what the Function API provides (but it does in a more Vue kind of way).
The Function API allows you to easily do exactly that without having to create a lot of new components. |
The fact that you can cleanly extract and reuse side effects is the exact power of the function-based API. It's not just about reuse - but also code organization. There are certain things that just won't make sense as a separate component, nor would they fit in a service class, but can still be perfectly extracted into a function (e.g. data fetching on mount + refetch when props change). |
@yyx990803 I agree about life cycle but why watchers? Can't I put watchers for example in the constructor or in some method ( Also as this RFC became quite a PR (public relations, not pull request) failure maybe you can try using a backdoors to introduce it. What I mean is to provide an API for reactive data which is not related to the component API, so something that is already partially done (see
@Akryum that's the point - this interferes with the existing API. If it wasn't related to the component API there wouldn't be a threat that something will be backwards incompatible or deprecated. |
@jacekkarczmarczyk the RFC has already been updated to reflect exactly that: 6fe6f7b |
@yyx990803 Not exactly, the current RFC still uses components as a main example of how to use it:
What I meant is to decouple it completely from components and call it reactive data api |
You're thinking from the point of view of a framework developer, not product owner or CTO. From the business perspective, you want to have less things tightly coupled to the framework, not more. You want to be able to reuse at least parts of it when you port your product to framework Y next year. |
@jacekkarczmarczyk it was originally two separate RFCs (advanced reactivity + dynamic lifecycle injection). Unfortunately the ability of hooking into component lifecycle is a crucial part of the composition capabilities. |
@martinsotirov Maybe you shouldn't use a framework then? 😕 I'm confused because I would rather use the framework to its full potential to go faster, be more efficient and have better, more maintenable code than going halfway which can lead to unnecessary more complicated code, unnecessary custom solutions, spaghetti code, bikeshedding... |
i never really got this "framework independence" stuff really. the benefits of (good) frameworks only comes to fruition when you embrace its paradigms and best practices fully. sure, not every framework is good for every use-case (edit: or every dev team), but that doesnt mean a framework should stop evolving. especially in the Web app world where you are so heavily constrained by the platform (browser) it makes no sense to argue for framework agnostic coding. i personally never saw a project swap frameworks without rewriting like 80% of its codebase anyway. |
The solution to tight coupling is not to not use a framework. That's just naive.
Starting to get off topic here, but anyway - this is not about framework independence but about SOLID code. Even if you're not switching frameworks but just upgrading versions, it would be much easier if your core business logic is not tightly coupled to specific framework features. I agree with you about the 80% code rewrite, what we're arguing here about is the 20%. Am I old or are best practices and design patterns just generally not known among web developers nowadays? |
@backbone87 This is really one of those things which different people see differently. And I don't think the two sides would ever come together. One one hand is the camp that says "use the framework to its fullest potential" but the other one is "protect your business logic in separate modules so as not to rewrite later". Both views are totally valid and they both have pretty strong pros and cons too. See the further up from from developing you go, the more you see how much all this rewriting costs companies. While they have to pay for someone to rewrite code that worked perfectly well in a previous framework those developers are NOT working on new features, or fixing important bugs for customers. They are basically making something work which was already working. Real big waste of money. Now I'm not saying that there are not reasons to change things and to evolve, but the people that have to fork out money, or even worse, BEG their bosses/investors for money to do this are the ones that feel the pain. @martinsotirov I think we're just getting old man 😆 Now please, this is just an explanation to @backbone87 's comment - I really do not want to open up another huge debate/talk about what's better. If there's one thing I think everyone should be aware of by now in these threads is how diverse a population is using Vue and how passionate people are about it. This is not a bad thing, but it makes evolution and change hard - always has and always will. |
Please stay on track and focus on the original RFC (on why it is or is not necessary). |
I'm personally looking forward to trying this function API, I do see it's advantages and the problems it is solving. But the Object API would still be my preferred way of writing a good amount of simpler components. I can also see components with the normal object API properties: life cycle hooks, data, computed properties and methods, coexisting with a setup function to bring in functionality that would normally be on a mixin. |
Yes. |
@yyx990803 and that's exactly what i'm talking about. Maybe the new RFC trying to add too much unknown to the well established API is what made people scared. Maybe if you implemented those 2 RFCs first people would have time to get familiar with new techniques and then, in the next version of Vue, maybe major, maybe minor (and after getting a lot feedback from real world use cases) you could add the function-based component API which would use already known patterns. Despite appearances programmers (at least some) are humans and human manipulation techniques (such as introducing changes step by step instead of 1 big at a time, even if that leads to the same outcome) also could be successful :) The only drawback is that it could delay the whole process. However since none of those 2 RFCs as I understand is not a breaking change then they could be introduced anytime, maybe even in 2.X, the point is that it would give people time and opportunity to understand new API and make the switch from Side note - it's not about how to introduce any change/new feature, but problem with this RFC escalated enormously and i'm trying to find a way how to make the change eventually implemented with people's approval rather than with votes against |
This quote from Rich Harris seems relevant: "Frameworks are not tools for organizing your code, they are tools for organising your mind." |
The new API is already working with 2.X as a plugin. https://codesandbox.io/s/todo-example-6d7ep
Learn the new API as it is the best solution for TS support. I'm not sure if you realize this, but the class-based API proposal was discontinued in favor of this, the function-based API. Scott |
@jmellicker Is your comment supposed to have any relation with mine? If so, I'm sorry to say that I can't get it :) |
@smolinari Yes, I could read, that the class based API proposal was discontinued, however it was written (by some core vue contributor) that the current class/decorator based API would still work and therefore made the (apparently wrong) assumption that this was still the way to go for having the best typescript experience. Thanks for clarifying. |
I agree with this. I also agree on the Typescript note.
You could, but please don't drop it. I have experienced first hand, Senior React developers saying templates is one of the advantages of Vue over React. IMHO, you risk killing Vue by removing the |
@igasparetto It's not about removing Vue templates, but making the use of So, instead of writing: <template>
<div>...</div>
<template>
<script></script>
<style></style> One simply write: <div>...</div>
<script></script>
<style></style> Prior art of frameworks that use template-based SFCs but doesn't require But, as said, it's subject for another RFC. |
@igasparetto Sounds like a weird comment to me. Saying that Javascript is a functional language is way exaggerated. Functions as a first class citizen is not enough to define a language as functional. Typescript is not only about OOP (modern Javascript provides classes construct as well). Functional languages tend to be statically typed. As far as I can see, the recent explorations into functional Javascript rely on Typescript for obvious reasons. I suppose that the React dev you are mentioning were talking about the html based templating language as opposed to mixing javascript within Html and not the 'template' tag by itself. |
According to @yyx990803
I solve the mentioned problems in my applications with separate JS classes and thus additionally use the advantages of OOP.
How have I solved 1. and 2. so far? It was a long way. I have tried mixins and discarded them. I failed with the attempt to pass logic in functions via props to different components. I also tested the class api, but there were too many unresolved problems (Of course it would be best if the class api could have been successfully finished). But I don't know the solutions either.) After some time I found the solution for 1. and 2. It was relatively simple. I outsourced all logic, which is used in several components, to own JS classes. Wherever I use a certain pice of logic, I instantiate the class and call the desired methods. I always store a class instance (if instanciated) in the returned data(() => {}) object (by default, the stored class instance in data() is set to null). In the template part I can easily access a class variable {{classInstance.classVariable}} The advantages of this approach are obvious:
I'm not sure if with the described approach a completely additional function-based API is really necessary for Vuejs. In my opinion, it would be sufficient if certain ceavets were solved in object-based api, for example:
In my opinion, 1. and 2. can already be solved easily and nicely today. I would rather invest the energy in perfecting and spreading this approach than in implementing, documenting and maintaining another parallel API. |
@michaelscheurer Do you mind linking to an example of what that looks like? I’ve gone in a similar direction on certain projects, but I never felt it was something I’d want everyone else to follow (the function based API feels more natural to me). It would still be missing lifecycle hook logic, right (other than just calling a method from a lifecycle hook)? |
This function-based API proposal is AMAZING and it must become a reality! Time and time again things started to go south as soon as I typed I believe that Vue should focus on component composition, avoiding other kind of abstraction/composition like class inheritance or mixins. This proposal goes a long way in that direction: if you want to do something, create a component; everything else is just functions! With the framework suggesting these best practices I could see how a user would be keen in doing the same for the rest of the app, promoting better code. What's keeping my current projects together is Vue component isolation and typescript. While OOP, mixins, Among the many use cases I can foresee, here is one where I could const shortcutsStack = [];
const handleShortcuts = (event: KeyboardEvent) => {
const shortcutAction = getShortcutFromShortcutsStack(decodeKeyFromEvent(event));
if (shortcutAction) {
event.preventDefault();
event.stopPropagation();
shortcutAction();
}
};
/**
* Add global keyboard shortcut to the component in `setup`
* useKeyboardShortcuts({ 'cmd+c': copy });
*/
export function useKeyboardShortcuts(shortcuts: { [keyComb: string]: (() => void) }) {
onMounted(() => {
if (shortcutsStack.length === 0) {
window.addEventListener('keydown', handleShortcuts);
}
shortcutsStack.shift(shortcuts);
});
onUnmounted(() => {
removeShortcuts(shortcuts, shortcutsStack);
if (shortcutsStack.length === 0) {
window.removeEventListener('keydown', handleShortcuts);
}
});
} I hope a 2.x plugin to play around with this is around the corner because my hands are tingling! |
It's already made: https://github.com/vuejs/vue-function-api My example trying to show a little bit of encapsulation (not very good): https://codesandbox.io/s/function-based-api-example-vue-rfc-78nc1 Scott |
This new |
What if, by the time of this RFC, there were no hooks like |
I really like this feature. It opens a totally new world of possibilities to writes components, I like it. What I appreciate the most is to focus on functionalities more than "objects". The functional way feel the easiest way to achieve this to me : you don't need to learn anything new like TS classes or specific JS stuff : everything is functions. Any beginner can use it without wondering about "magic stuff". I think the readibility problem is mostly a matter of habbits. What should be highlighted is that this "syntax" is not a core change : there are still methods, computed props etc. and they still work the same. The addition is only an additional way to declare them, that feels really clever to me |
something I've noticed porting a somewhat large project at work to the function api is that it is easy to wrongly use wrapped values like so: const myValue = value<string | null>(null);
const myValueIsEmtpy = computed(() => {
if (!myValue) { // OOPS! you should check myValue.value but it is easy to oversight
return true;
}
return false;
}); Even if you use Am I making sense? |
Is there still a plan to make available a version of Vue with just the functional parts, meaning, without the class-related code? I'd rather just use the functional API and see no need for loading the class-based code. I see no need for burdening users who chose the functional option with unnecessary bloat. |
Closing in favor of #78 |
Ok. So now it's Scott |
Why did this go unnoticed? I think this is more elegant and is more acceptable to a lot of developers. I love the new function api, just the verbosity I'm not happy with and that sort of solve it the Vue way. |
It's because it would require two trains of thought to use Vue, which I personally believe is something worse than the convenience offered (and I can't speak for Evan, but I do believe from the many comments I've read, it's his thinking too). I mean really, we are talking about only 3 methods more than what the options API has. Scott |
For real, because nobody implemented it, so far. It can probably be done in userland (say, There are some pain points, too, like TypeScript integration, that frameworks that followed a similar path, like Svelte, didn't figure out how to solve yet. |
Oh yeah. Good point. 👍 Scott |
Exactly. I anticipate this will be a community experiment (and probably not the only one) once Vue 3.0 is properly released. This should not be a core team concern. If anything, the fact that such tooling is plausible is a big plus for this API because it gives the community flexibility to choose between different sets of trade-offs (if they really want to). |
😁 I am obviously not talking about the methods only. From his above example, try to expose the
It's not perfect, yet, but I think this can actually work better both on the code and later IDE support. 🙃 |
Still, they can't answer a simple question. If Vue forces us to code like React, why should we use Vue? Why not React? React is where the jobs are. Failure of Angular is a reason for the success of React. Vue was loved due to the similarity with Angularjs. Vue is just junk and a waste of time if there is no simplicity and ease of use. |
@anoop-ananthan If you're dissatisfied with Vue 3 and think you're better using React, use React. You might enjoy it. Or you might notice that Vue 3 is still simpler and easier than React and more similar to Vue 2 than React. But your confrontational tone and disrespectful words aren't likely to change anybody's opinions. Especially commenting on a closed issue... |
Opening an issue here with a copy of my original comment to #42, per @yyx990803's request:
The text was updated successfully, but these errors were encountered: