Skip to content
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

Open
abalashov opened this issue Jun 24, 2019 · 141 comments

Comments

Projects
None yet
@abalashov
Copy link

commented Jun 24, 2019

Opening an issue here with a copy of my original comment to #42, per @yyx990803's request:


I am the author of "Love letter to Vue": http://www.evaristesys.com/blog/love-letter-to-vue/

As a big booster of Vue who can appreciate its distinctive characteristics and the way it has stood apart from competing frameworks, I must say that I am perturbed and disappointed by the function API proposal. I have to agree with the above posters who say that no serious problem is being solved here which would warrant such a radical shift, and that in essence, this proposal amounts to chasing shiny new things. Yes, there are indeed some code decomposition and modularity problems that this change addresses. But not all theoretical problems are worth addressing.

One of the biggest virtues of Vue, which I mention in the article, is that here, halfway through 2019, it's still quite literally the same Vue I first picked up in late 2016. This is a refreshing contrast to the general pace of change and deprecation in the JS/web world. Frank Chimero's high-level, perspicacious take on this problem bears mention here: https://frankchimero.com/writing/everything-easy-is-hard-again/

From a political and a philosophical point of view, I think it's vital to understand that introducing radical API changes of any kind to any project is breaking, and a generous offer of backward compatibility with old APIs does not solve this problem. With due acknowledgment to the fact that the 2.x API is not slated for deprecation and the labourious emphasis this has received, you're still sunsetting it in an overall ecosystem sense. The 2.x API will no longer be a first-class citizen. A completely new way of doing things insinuates a preferred new way of doing things, and sample code, tutorials, books, etc. will inevitably adopt it over time, leading to a manageability and direction crisis for those with investments in huge Vue code bases. New APIs are unavoidably ideological statements, and the devaluation of old APIs -- indeed, the very ontology of "old" vs. "new" -- have an ideological valence.

While change is inevitable, people want to follow "best practices" in an effort to stave off bit rot, and feel a psychological pressure to buy into the current wave of thinking. So, offering to support the old options API is not of much help; this RFC raises the prospect that the commitment to maintaining backward compatibility with the 2.x option API is not steadfast, and will slowly de-orbit over time, like the Mir space station. In effect, you are decreeing that there's now a new way of doing things, and this is the way they should be done.

I also agree with the criticism mentioned in the Downsides section above: the concern that this way of doing things is arcane and more likely to lead to byzantine or labyrinthine "spaghetti code". One of the biggest selling points of Vue is its simplicity and approachability. It's static enough that a major organisational commitment to Vue has shelf life and durability, rather than evaporating in one's hands, as so many other JavaScript-related commitments do owing to capricious, whimsical API changes and architectural gewgaws.

Thus, technical bickering about code organisation and modularity notwithstanding, from a business and ecosystem health point of view, I think this is the light in which this RFC should be considered, and the cost against which the (from my perspective, marginal) improvements offered should be carefully and judiciously weighed. I do not see a sufficiently captivating problem here worthy of such a radical solution, even if commitment to the 2.x approach is scrupulously observed for some time. It's not worth throwing away the things that make Vue good.

I'm natively Russian, and there is an old political anecdote about this:

Lazar Kaganovich brings Stalin a scaled-down model of a new, reconstituted Moscow, the grandiose global Capital of Socialism that Stalin envisioned (not unlike the visions of Hitler & Speer for the Thousand Year Reich architecture). Most iconic historical features of pre-revolutionary Moscow are demolished, and replaced with grand plazas and enormous, titanic buildings that capture the idealism of the class-conscious revolutionary proletariat, the enormity of the Generalissimus's futuristic vision, and the zeal to shed bourgeois architecture and aesthetics and break with the past.

Stalin asks: "Lazar, where is Saint Basil's Cathedral?"

"It was removed; it will be demolished."

Stalin heaves a sigh and rolls his eyes.
"Lazar, put it back."

@yyx990803

This comment has been minimized.

Copy link
Member

commented Jun 24, 2019

I respect your opinion so please don't take any of the following as being personal.

no serious problem is being solved here which would warrant such a radical shift, and that in essence, this proposal amounts to chasing shiny new things

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:

  • the RFC thread also has MANY users expressing how they find the RFC to be solving the exact problem they are facing right now. And they have also mentioned that the current object-based syntax is the exact bottleneck that hinders the scalability of their projects. I acknowledge that many of you may never personally experienced this (which is totally valid, and even great!), but if an aesthetics choice comes at a real maintenance burden (admittedly only in certain types of projects), then it's no longer just an aesthetics problem. Denying some users' opportunity to solve their problems with a better abstraction due to the aesthetics preference of some other users seems like the wrong trade-off to make.

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

  1. Large components (hundreds of lines long) with encapsulates multiple logical tasks
  2. The need for sharing the logic of such tasks between multiple components.

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 html, js and css folders) don't really scale. The code related to a feature will be forced to be split between three folders, just for a false impression of "separation of concerns". The key here is that "concerns" is not defined by file type. Instead, most of us opt to organize files by feature or responsibility. This is exactly why people love Vue single-file components. SFC is a way to organize code by feature. Ironically, when SFCs were first introduced, many resisted it because they feel that it violated separation of concerns, only later to acknowledge that SFCs are in fact the more reasonable way to separate concerns.

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.

introducing radical API changes of any kind to any project is breaking, and a generous offer of backward compatibility with old APIs does not solve this problem

"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.

this way of doing things is arcane and more likely to lead to byzantine or labyrinthine "spaghetti code". One of the biggest selling points of Vue is its simplicity and approachability. It's static enough that a major organisational commitment to Vue has shelf life and durability, rather than evaporating in one's hands, as so many other JavaScript-related commitments do owing to capricious, whimsical API changes and architectural gewgaws.

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 setup() is not any different from understanding idiomatic JavaScript code and any technique you can use to organize ordinary JavaScript code can be used to organize your setup() function. Any knowledge / style guide / code review process that applies to normal JavaScript code written by your team can be applied to code in Vue setup() functions.

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:

  • Options-based API thinks of a component as defined by the types of properties/methods/options it contains.
  • Function-based API thinks of a component as defined by the logical topics it is encapsulating.

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).

It's not worth throwing away the things that make Vue good

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:

  • The template syntax does not change (and the performance is getting better!)
  • The way the reactivity system works does not change
  • The concepts of computed properties, watchers & component lifecycle do not change
  • The SFC format does not change
  • The CLI does not change
  • The progressive nature of the framework does not change
  • The team's commitment to providing better development tools does not change
  • Technically, even the object format does not change: everything that worked will still work.

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.

@smolinari

This comment has been minimized.

Copy link

commented Jun 24, 2019

I have to agree with the above posters who say that no serious problem is being solved here which would warrant such a radical shift, and that in essence, this proposal amounts to chasing shiny new things.

The new API solves these problems:

  1. It fixes the issues Vue currently has with logic composition.
  2. It solves the issues of type inference, leading the way for Vue to be built on TypeScript and offering first class TypeScript support.
  3. And not directly mentioned in the RFC, but core to the whole RFC, is the way features of one or more components can now be encapsulated properly and still have strong cohesion. It's allows for better following of the SOLID design principles.

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......

Options removed in the Lean Build
These options will not be available in the lean build of 3.0.

data (replaced by setup() + value + state)
computed (replaced by computed returned from setup())
methods (replaced by plain functions returned from setup())
watch (replaced by watch)
provide/inject (replaced by provide and inject)
mixins (replaced by function composition)
extends (replaced by function composition)
All lifecycle hooks (replaced by onXXX functions)

......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

@beeplin

This comment has been minimized.

Copy link

commented Jun 24, 2019

@smolinari totally agree... digging into Quasar's complex components and finding something among all kinds of mixins is a nightmare. :))

EDIT: in case of misunderstanding: I am talking about the flaws of mixin mechanism. Nothing to do with quasar ;)

@smolinari

This comment has been minimized.

Copy link

commented Jun 24, 2019

@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

@martinsotirov

This comment has been minimized.

Copy link

commented Jun 24, 2019

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.

@thatandyrose

This comment has been minimized.

Copy link

commented Jun 24, 2019

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? vuex has a nice example of "modules" within a store to separate concerns of different sections of your app.

In this way, could we not think of the options API as having many concerns? So, you could perhaps have many data options, many computed options, but all nested within their own concern.

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 😬 )

@smolinari

This comment has been minimized.

Copy link

commented Jun 24, 2019

In this way, could we not think of the options API as having many concerns? So, you could perhaps have many data options, many computed options, but all nested within their own concern.

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 setup method, but can be encapsulated in different "feature" functions or rather - for better TS support - feature objects.

@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

@yyx990803

This comment has been minimized.

Copy link
Member

commented Jun 24, 2019

@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.

@martinsotirov

This comment has been minimized.

Copy link

commented Jun 24, 2019

@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.

@martpie

This comment has been minimized.

Copy link

commented Jun 24, 2019

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.

@yyx990803

This comment has been minimized.

Copy link
Member

commented Jun 24, 2019

@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 data()?). In addition, note that your service class can only encapsulate pure state logic and won't be able to encapsulate logic that has to do with side effects (i.e. watchers or component lifecycle hooks), so there's a limitation on what type of logic you can extract.

@martinsotirov

This comment has been minimized.

Copy link

commented Jun 24, 2019

In addition, note that your service class won't be able to encapsulate logic that has to do with side effects (i.e. watchers or component lifecycle hooks), so there's a limitation on what type of logic you can extract.

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.

@Akryum

This comment has been minimized.

Copy link
Member

commented Jun 24, 2019

@martpie Vue is already providing a lot of different ways for doing the sames things, and it's been fine!
For example:

  • Inline templates, SFC templates, render functions, JSX, x-template, ...
  • Extends, Mixins, HOC, Renderless components
  • Script tag, vue-cli, custom build setup
  • Directive shorthands or full directives
  • Root state, event bus, Vuex, other community solutions
@Akryum

This comment has been minimized.

Copy link
Member

commented Jun 24, 2019

Export shared functionality in a separate vanilla JS service class that you just import in every component that needs it.

@martinsotirov Sounds a lot like what the Function API provides (but it does in a more Vue kind of way).

have to rethink your architecture or break up the component

The Function API allows you to easily do exactly that without having to create a lot of new components.

@yyx990803

This comment has been minimized.

Copy link
Member

commented Jun 24, 2019

@martinsotirov

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.

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).

@jacekkarczmarczyk

This comment has been minimized.

Copy link

commented Jun 24, 2019

@yyx990803 I agree about life cycle but why watchers? Can't I put watchers for example in the constructor or in some method (startWatching() { watch(() => this.foo, () => this.doSomething() })?

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 observable in 2.6), I guess watchers and computeds are almost ready as well. And if this is something that people need they will start using it but nobody will oppose because it doesn't even try to deprecate anything, it's not related to component API etc. And when it will start to be widely used it will be much more natural to merge it into the component API. That could mean that the function API could be introduced later than in 3.0, maybe 3.X, maybe 4.0, but you'd have a time to see how the small features (observable, watchers) etc are used in the real world and have a chance to analyse it, maybe rethink API etc

Sounds a lot like what the Function API provides (but it does in a more Vue kind of way).

@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.

@yyx990803

This comment has been minimized.

Copy link
Member

commented Jun 24, 2019

@jacekkarczmarczyk the RFC has already been updated to reflect exactly that: 6fe6f7b

@jacekkarczmarczyk

This comment has been minimized.

Copy link

commented Jun 24, 2019

@yyx990803 Not exactly, the current RFC still uses components as a main example of how to use it:

Expose logic-related component options via function-based APIs instead.

A new component option, setup() is introduced

One of the key aspects of the component API is how to encapsulate and reuse logic across multiple components.

What I meant is to decouple it completely from components and call it reactive data api

@martinsotirov

This comment has been minimized.

Copy link

commented Jun 24, 2019

@martinsotirov Sounds a lot like what the Function API provides (but it does in a more Vue kind of way).

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.

@yyx990803

This comment has been minimized.

Copy link
Member

commented Jun 24, 2019

@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.

@Akryum

This comment has been minimized.

Copy link
Member

commented Jun 24, 2019

@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...

@backbone87

This comment has been minimized.

Copy link

commented Jun 24, 2019

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.

@martinsotirov

This comment has been minimized.

Copy link

commented Jun 24, 2019

@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...

The solution to tight coupling is not to not use a framework. That's just naive.

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.

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?

@mika76

This comment has been minimized.

Copy link

commented Jun 24, 2019

@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.

@yyx990803

This comment has been minimized.

Copy link
Member

commented Jun 24, 2019

Please stay on track and focus on the original RFC (on why it is or is not necessary).

@tochoromero

This comment has been minimized.

Copy link

commented Jun 24, 2019

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.
Is this mix and match approach going to be supported?

@Akryum

This comment has been minimized.

Copy link
Member

commented Jun 24, 2019

Is this mix and match approach going to be supported

Yes.

@jacekkarczmarczyk

This comment has been minimized.

Copy link

commented Jun 24, 2019

it was originally two separate RFCs (advanced reactivity + dynamic lifecycle injection)

@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 advanced reactivity + dynamic lifecycle injection to function-based component API more natural

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

@smolinari

This comment has been minimized.

Copy link

commented Jun 27, 2019

Maybe @yyx990803 could write a Medium article on the status of this RFC?

Scott

@vertcitron

This comment has been minimized.

Copy link

commented Jun 28, 2019

I don't want to say again what has been said here several times, so I just add my voice to affirm that this proposal is a really great improvement, which should definitively turn out all the workarounds we had to implement when developing large scale applications in Vue, where a lot of business logic is required.
As anybody can decide to use it or not, please keep it on, I'm impatient...

@vedmant

This comment has been minimized.

Copy link

commented Jun 28, 2019

I personally against this RFC, I never encountered problems that it's supposed to solve. I really enjoy using Vue over React because of clean separation in the code between computed, methods, data, watch, etc.. In any Vue component you can see this separate and easy readable, with this RFC everything going to be a mess as there is no forced structure.

@smolinari

This comment has been minimized.

Copy link

commented Jun 28, 2019

with this RFC everything going to be a mess as there is no forced structure.

That was my first impression too, but I've also had the pleasure of working with larger components for the first time right about when this RFC came out and I'm certain had those components been built "feature-based" instead of with Vue's options, the larger components would have been easier to reason about to find and fix bugs. Currently with the options object API, the parts of features end up spread out over the options, or even worse, within mixins and the flow of logic is hard to follow.

With a function-based/ feature-based system, logic flow and also logic encapsulation is easier to do and easier to reason about once a component becomes complex. Obviously, if you have not-too-complex components, the options API does just fine. 😄

The good things is, this RFC is purely additive, meaning you can ignore it. The options API is staying for the "foreseeable future".

Scott

@jmellicker

This comment has been minimized.

Copy link

commented Jun 30, 2019

The advantages of the proposal are clear, it's just too bad the new syntax looks so kludgy, weird and unintuitive to an average (or new) developer. The current class-based API (while guilty on all charges), is more intuitive, easier to learn and has been a major factor in the adoption of Vue.

Why not take this opportunity to solve the problems of the current class-based API, and at the same time make Vue 3's developer-facing syntax zen-simple and more inuitive?

For example, instead of this proposed syntax:

<template>
  <div>
    Count is {{ count }}, count * 2 is {{ double }}
    <button @click="increment">+</button>
  </div>
</template>

<script>
import { value, computed } from 'vue'

export default {
  setup() {
    const count = value(0)
    const double = computed(() => count.value * 2)
    const increment = () => { count.value++ }
    return {
      count,
      double,
      increment
    }
  }
}
</script>

what's wrong with binding all component-scoped variables and functions to the component template/DOM?

<div>
    Count is {{ count }}, count * 2 is {{ double }}
    <button @click="increment">+</button>
</div>

<script>
    const count = 0
    const double = count * 2
    const increment = () => { count++ }
</script>

Couldn't we get closer to this ideal?

(BTW, as a "CEO who codes", I've always wondered... why do I have to type things like export default or setup(), or <template>? Why can't I just write the code I want to write and the compiler takes care of adding needed boilerplate behind the scenes?)

@smolinari

This comment has been minimized.

Copy link

commented Jun 30, 2019

@jmellicker - From my understanding, the issue with your suggestion is, it would only work in the compiled/ CLI version of Vue and not in the UMD version. The goal is to have something that works in both versions of Vue, in order to stay inline with its progressive nature.

I can attest to thinking the same way as you are and also fighting "the flow" and being reluctant to accept the new API (at least as it is). However, I've made some suggestions here for better syntax, looked stupid in some cases, got some good replies from Evan explaining why I'm being stupid (he didn't say that, I did 😁 He was always a gentlemen). And then I started doing actual code with the API (which is possible with this sandbox).

I guess what I am trying to say is, there are some concessions being made to keep Vue progressive, like having to wrap bindings around primitives (the value() and .value bit). It seems like boiler plate, but in the end, it's necessary and once the concept of the function-based API is under your belt, it's actually just a few things to remember more, but adds a whole new world of possibilities for larger more complicated apps.

Yes, it offers much more flexibility and that means more responsibility in devland to do the right things with it and yes, it means less magic from Vue. And that is why it was a good decision to make the API additive to the option-object API and part of the "more advanced topics" of Vue, like render functions and using JSX.

Scott

@yyx990803

This comment has been minimized.

Copy link
Member

commented Jun 30, 2019

@jmellicker

Why not take this opportunity to solve the problems of the current class-based API, and at the same time make Vue 3's developer-facing syntax zen-simple and more intuitive?

We did try (and actually spent a LOT of time on this), but class is a dead end. If someone can come up with a class API that actually solves all these problems, I'd be happy to reconsider.

why do I have to type things like export default or setup(), or <template>? Why can't I just write the code I want to write and the compiler takes care of adding needed boilerplate behind the scenes?

The main reason is alignment with standard JavaScript. If you extract the code from <script>, it will work exactly the same as a standard ES module. The code you suggested is in fact supported by frameworks like Svelte, and can also be made possible via a Babel plugin, but it's no longer how JavaScript works. It becomes framework-specific magic that only works inside Vue files. There are a number of problems I see with this approach:

  1. Code works differently with/without compilation. As a progressive framework, many users may wish/need/have to use it without a build setup, so the compiled version cannot be the default. In comparison, Svelte positions itself as a compiler and can only be used with a build step.

  2. The moment you try to extract logic out of components and into standard JavaScript files, you face the dilemma: you either lose the magical concise syntax (and inevitably need to work with the lower-level API) or you make the magic syntax work in normal JavaScript files (which is going to create a lot more problems).

    Svelte goes with option 1 and provides a store system that works quite similar to the Vue reactivity API, but completely differently from its components. This means it's going to take a lot more work when you are trying to extract existing component logic into reusable code. In comparison, with Vue's function API you can easily extract part of your existing component logic and reuse it in another component (you can even directly export the extracted function from the component itself).

    TL;DR: with concise syntax (but drifting away from JS) you lose composition flexibility.

  3. Non-standard behavior makes it problematic to integrate with TypeScript.

As for <template> - it can technically be dropped, maybe that could be an RFC.

@gustojs

This comment has been minimized.

Copy link

commented Jun 30, 2019

I'm not sure dropping <template> would be a good idea. We would end up with some users not needing <template> and some in need of it to do <template lang="pug">.

@aztalbot

This comment has been minimized.

Copy link

commented Jun 30, 2019

I don't like the idea of dropping <template> either. Like @gustojs said, some people need it, and also others like myself find freely floating fragments strange.

Code works differently with/without compilation. As a progressive framework, many users may wish/need/have to use it without a build setup, so the compiled version cannot be the default. In comparison, Svelte positions itself as a compiler and can only be used with a build step.

@yyx990803 I fully support Vue's design constraint as a progressive framework. I think it's awesome. Also, this new API is fantastic – and powerful. I plan on using it heavily. I personally don't mind the ergonomics of .value and toBindings.

However, SFC's are compile-time constructs that wouldn't work in the browser the same way. Yes, they mirror components exactly, but they need a build-step. The upside is ergonomics and so we all love SFCs. So, maybe there is a sort or middle-ground. The <script> section of an SFC should always have standard JavaScript – that makes sense and I don't want that to change. But, what if we add a <setup> section with an experimental automatic-bindings mode. Here's what I'm thinking:

<template>
  <div>
    Hello {{ name }}
  </div>
  <div>
    Count is {{ count }}, count * 2 is {{ double }}
    <button @click="increment">+</button>
  </div>
  <div>
    Mouse is at {{ x }}, {{ y }}
  </div>
</template>

<setup experimental-automatic-bindings>
import { computed as c } from 'vue' // no need for `reactive` or `binding`, `binding` is automatically added
import useMouse from 'hooks.js'

// keep it as a function so it is idiomatic JS still and args can be typed
export default function (props, context) {
    const state = { // automatically converted to const state = binding({ name: '' })
       name: ''
    }
    let count = 0 // automatically converted to const count = binding(0)
    const double = c(() => count * 2) // `.value` automatically added to `count`
    const increment = () => { count++ } // `.value` automatically added to `count`
   
   onMounted(async () => {
     state.name = await fetchName() // `state.name` is actually `state.value.name`
   })
    
   // keep return so we can control what is exposed to template and how
   return {
      ...state // actually `toBindings(state.value)`
      count,
      double,
      increment,
      ...useMouse // if this returns reactive state, wrap in toBindings, lint rules govern this function in the other file
    }
}
</setup>

This kind of idea might not work (or implementation could be quite complex), I'm just trying to brainstorm ergonomic solutions. In any case this sort of thing is outside the scope of this RFC. This API is great and we should get our hands on it and then maybe try experimenting with things like this.

But, what I think this sort of approach would accomplish is hiding what some think are the ugly parts of this API, while also making it an explicit choice by the developer to opt for some "auto-magic." It's also somewhat in line with <style scoped> in that it adds something to the compiled output and will work differently if directly copied to a file where that functionality is not enabled. I could foresee IDE support to toggle between hiding/showing all the bindings logic (so you can see the output and maybe then copy to non automatic binding file or copy in logic with bindings and then hide the bindings).

In a file with hooks, maybe this functionality is enabled by some comment like // @vue experimental-automatic-bindings so any useXXX function in that file is transformed. Or, maybe any hook or setup function inside a <setup experimental-automatic-bindings> is transformed so that these must always be in .vue files at all times (in the same way scoped css must be in a vue file).

We don't have to add a <setup> section, we could just have <script experimental-automatic-bindings> and transform any setup and hooks in there in case having setup inside createComponent would be beneficial. Hooks would be non-default exports from those modules. The point is to not make it any sort of default behavior and not drift too far from JS – we're just auto-inserting our binding logic.

The downside is it makes assumptions about how you will write your code here, and specifically it makes assumptions about what you won't try to do. For example, you can't ever have a non-binding primitive in this case (although some static analysis might be able to avoid wrapping values that don't need to be bindings), and you can't rebind any variable, you can only ever mutate .value. It can also be tricky for the compiler to know when to NOT apply toBindings – maybe it can rely on some assumption based on code path or maybe an escape hatch is needed.

Another downside is that when using TypeScript in this mode, if TypeScript knows something is a binding (like a computed or one returned from a hook), then it may error if you try to do certain things that violate that type (double + 1 // type error, double is a Binding, you can't add 1 to it). However, the error will make it clear at that point to just add .value manually or maybe you have some lint auto-fix for it already (and these cases are probably less frequent).

@jmellicker

This comment has been minimized.

Copy link

commented Jul 1, 2019

Thanks @smolinari @yyx990803 and all for your detailed and thoughtful responses.

Apologies for the ambiguity, I'm definitely in favor of ditching the class API and allowing developers to group related code together and compose components as they wish, that will be great!

Also, thanks for the reminder that many people use Vue progressively and in a wide variety of user cases and you want to maintain that flexibility. From a technology perspective I am 100% behind the new API and look forward to diving in.

HOWEVER...

I still believe that the proposed syntax will be harder to initially grok for beginners and below average developers (and studies have shown that as many as half of Javascript developers are below average 🤔). My comment above was not made from a technology context, but a marketing one. No one wants to see Vue's meteoric rise in adoption slowed because people are initially put off by the first code examples they see.

So I wonder what you all think of the idea that there could be a special SFC definition with zen-simple, super-beginner-friendly syntax ("framework-specific magic that only works inside Vue files") that would (with a Babel plugin) compile to normal Vue components using the new API?

The goal would be that the .zen SFC syntax would be so simple, friendly and welcoming that the Vue team could then consider pursuing the original strategy of deprecating the current class API and focusing on the "lean build," simplifying your lives moving forward... while providing a beginner-friendly entry point.

I would be happy to fund such an effort, but would only proceed if @yyx990803 thinks it is:

  1. feasible to implement and maintain
  2. a good idea 😀

A developer could choose, at any stage of a project, to stop using the .zen SFC format and "graduate" to working directly with the new API (training wheels come off the bike).

@smolinari

This comment has been minimized.

Copy link

commented Jul 1, 2019

@jmellicker - I totally agree that the new API isn't going to be quickly accepted or understood by beginning, below average or even intermediate developers. Not the ones, who have worked with the option-object API for any period of time and not if they aren't seeing true working code.

This is my prediction of the future though. With time, more and more advanced devs will use the new API for their complex components. As these developers make more and more code, and as, let's call them, "the junior developers" see that code and how it's easier to reason about overall, then I bet the function-based API will catch on more and more.

So, my personal response to your idea is, let evolution happen. Let's see how much the new API is used for advanced SFC building and then go from there. 😄

Scott

@Jack85

This comment has been minimized.

Copy link

commented Jul 1, 2019

@smolinari this is the point.. you re introducing something where small to medium guy on the street (immediately doesnt see advantages) is no longer in focus, questions coming about fast pickup and productivity.
Now its more about the "smart" guys who can "see", or the "rich" guy who feels right to say new api should look like this - over. Its like in any society when you feel only "smart" guys ruling this world, thats why its everything getting so complex for guy on the street. I feel more like vue should be for anyone, with lots of clear examples with clear advantages.
You "cannot outsmart" simple "usage" of first class citizen - object api until you have something more simple or intuitive proven, crucial point.
No one stopping progress here, being competitive, we want this. Its about the form, if you acknowledge object and functional same importance, future plans, before you even decide to depreciate something..

One idea - If it doesnt belong to "commercial vue llc" or plugin like vue-func (separated, like with vue-resource) first?
Imagine like - set your acc balances to 0 and staying humble enough to earn the small guys (masses) again .. and REPEAT (having some iterations before full major integration, staying in healthy process, making sure no empire will rule and claim now its theirs api, or giving sharks meat for free once you fall off the ship since they re waiting for any mistake of those vuers on the other side).
Since it would be separated after 1y downloads, then you "can" compare vue vs vue-func, since you "know" its much better, "everybody" needs it, there ll be millions of downloads ever after..
Introducing it this way, since market is always right - Simple challenge: Lets Prove it.

@smolinari

This comment has been minimized.

Copy link

commented Jul 1, 2019

I'm not introducing anything. I'm just a guy trying to help out. 😊

And yes, this new API IS about "the more experienced (not necessarily smarter) guys (and gals)".......currently. It is they, who are having the issues of functional composition and feature code encapsulation. It is they, who are understanding this new API without any qualms (for the most part). It is they, who are resoundingly applauding it (for the most part).

I hope everyone is aware, the new API is "additional" to the object API. It has been decided to continue support for the object API for the "foreseeable future". Thus, Vue will stay the same great 'ol Vue for the less experienced developers. Nothing is changing for them. Vue's approachability isn't changing either.

The new API is going to be handled in the docs as an advanced topic, like render functions and using JSX....... for those who need it. 😄

Everything else is staying the same!

Scott

Disclaimer: The above is my recollection of history. I could be wrong.

@vedmant

This comment has been minimized.

Copy link

commented Jul 1, 2019

I hope everyone is aware, the new API is "additional" to the object API. It has been decided to continue support for the object API for the "foreseeable future". Thus, Vue will stay the same great 'ol Vue for the less experienced developers. Nothing is changing for them. Vue's approachability isn't changing either.

I'll quote this comment:

While change is inevitable, people want to follow "best practices" in an effort to stave off bit rot, and feel a psychological pressure to buy into the current wave of thinking. So, offering to support the old options API is not of much help; this RFC raises the prospect that the commitment to maintaining backward compatibility with the 2.x option API is not steadfast, and will slowly de-orbit over time, like the Mir space station. In effect, you are decreeing that there's now a new way of doing things, and this is the way they should be done.

I can say that I'm rather experienced developer, I switched from React to Vue mostly because of much cleaner code structure, and I guess as well as lots of other developers. With this new API there will be not much advantage over React personally for me. Having new optional preferred way of coding will introduce a lot of problems to my work, as I often fork and edit various Vue packages for specific project needs, if they start using new API it means I also be forced to deal with this new way of coding.

@smolinari

This comment has been minimized.

Copy link

commented Jul 1, 2019

With this new API there will be not much advantage over React

The new API is nothing like React or even React hooks. It's better and simpler and it's still Vue.

Maybe you don't realize this, but the general structure of Vue components will also stay the same. There will still be the three code blocks: <template>, <script> and <style>.

Yes, the <script> section may look a bit different. But, the few new rules there are with the new API to make "normal" JavaScript "Vue-Reactive" are not that hard to understand or convert to the object API. But, if you do that, you'll probably finally understand the new API and feel silly trying rework it into the object API.

I'm NOT a really experienced developer and I understand the advantages of the new API and how it should be used.

I also be forced to deal with this new way of coding

Only if the API catches on. If it does, that means there is a reason for using it. It means, some developers are gaining an advantage for using it. And, if there is that good reason to use it, then you should be dealing with that reason (and the code), because one day, you'll probably run into that reason for needing to use the new API too, and you'll be ready!

Scott

@FredericLatour

This comment has been minimized.

Copy link

commented Jul 2, 2019

Personally, I like this function based component API. It's not like if it was a major shift (even if I can understand to a certain extent some concerns).

That being said, I'm certain it won't be available before the end of the year (unfortunately). What would be the recommendation for developing a Vue application now with the best possible Typescript support? At first sight it looks like the Class based/Decorator approach seems the way to go as far as Typescript is concerned. At the same time, the syntax is really quite different from the future function based component API and I'm wondering if it will not make it difficult to move to this API later.
What's your take on this?

@jmellicker

This comment has been minimized.

Copy link

commented Jul 3, 2019

This quote from Rich Harris seems relevant: "Frameworks are not tools for organizing your code, they are tools for organising your mind."

https://youtu.be/AdNJ3fydeao?t=398

@smolinari

This comment has been minimized.

Copy link

commented Jul 3, 2019

@FredericLatour

That being said, I'm certain it won't be available before the end of the year (unfortunately).

The new API is already working with 2.X as a plugin. https://codesandbox.io/s/todo-example-6d7ep

What's your take on this?

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

@FredericLatour

This comment has been minimized.

Copy link

commented Jul 3, 2019

@jmellicker Is your comment supposed to have any relation with mine? If so, I'm sorry to say that I can't get it :)

@FredericLatour

This comment has been minimized.

Copy link

commented Jul 3, 2019

@smolinari
Following the discussion on the matter (RFC) I thought there would be at some point some kind of plug-in but I did not realize it was already available.

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.

@igasparetto

This comment has been minimized.

Copy link

commented Jul 5, 2019

@yyx990803,

The main reason is alignment with standard JavaScript.

I agree with this. I also agree on the Typescript note.
During the years, people tried to bend JavaScript to work OO, but JS is a functional, weakly typed language. Fact. We decided to remove TS from our projects to allow faster development. No regrets as well written tests assure quality.
I am in favour of the Function-based component API - please go for it! Regarding the syntax, people just need to get over their fears and learn the new way.

As for <template> - it can technically be dropped, maybe that could be an RFC.

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 <template> tag.
What is the problem with <template>?

@leopiccionia

This comment has been minimized.

Copy link

commented Jul 5, 2019

@igasparetto It's not about removing Vue templates, but making the use of <template> tag optional.

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 <template> tag include Ractive, Svelte and MarkoJS.


But, as said, it's subject for another RFC.

@FredericLatour

This comment has been minimized.

Copy link

commented Jul 5, 2019

@igasparetto Sounds like a weird comment to me.
You praise the new function based component api and at the same explain how you ditched Typescript for the better!! Vue 3 is fully embracing typescript so I am not sure what you mean here. People are adopting typescript for the exact same reasons why you decided to remove it.

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.
see: fp-ts

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.
In that respect, I haven't seen any intention to remove the templating language.
I suppose that some discussions go around the possibility to use render function in a way favor UI composition ...

@michaelscheurer

This comment has been minimized.

Copy link

commented Jul 9, 2019

According to @yyx990803

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

1. Large components (hundreds of lines long) with encapsulates multiple logical tasks
2. The need for sharing the logic of such tasks between multiple components.

I solve the mentioned problems in my applications with separate JS classes and thus additionally use the advantages of OOP.

  1. & 2. are exactly the problems I encountered when my Vue applications grew bigger and more complex over time.

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 can take advantage of the benefits of OOP: inheritance, maintainability, well structured code, clear responsabilities, performance improvements with v8
  • I can access functions and complex logic at any point in any component: in methods, computed properties, watchers, etc. All with one line of code.

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:

  • full reactivity for non-primitive data types
  • improved (native) typescript support (even better oop support with ts classes)
  • possibility to store a class instance in vuex and manipulate the class variables directly and keep the reactivity anyway.
  • support async computed properties

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.

@aztalbot

This comment has been minimized.

Copy link

commented Jul 9, 2019

@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)?

@thenikso

This comment has been minimized.

Copy link

commented Jul 10, 2019

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 class somewhere. You'll just add a tiny utility method there and before you know it the whole app logic is in some nested class method somewhere. I don't like OOP and I am glad to see Vue moving away from it with this proposal.

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, $something injection are difficult to follow and maintain. The function composition proposed with this RFC, along with the improved ts support would be a game changer.

Among the many use cases I can foresee, here is one where I could useKeyboardShortcuts({ 'cmd+c': copy }) in the setup and have keyboard shortcuts defined locally to my component and nicely handling override and cleanup (pseudocode):

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!

@smolinari

This comment has been minimized.

Copy link

commented Jul 10, 2019

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

@shepelevstas

This comment has been minimized.

Copy link

commented Jul 10, 2019

This new setup() method looks like just another lifecycle hook (between maybe befroeCreate() and created()) into what is happening with a component behind the scene anyway. Just allows to tap into the process of component creation. More complex than other hooks, with its own logic, but feels like a good thing to have. Powerful and rarely needed. For advanced use. If the rest of the API stays the same as before (and it is so!), then this development should be much appreciated!

@shepelevstas

This comment has been minimized.

Copy link

commented Jul 10, 2019

What if, by the time of this RFC, there were no hooks like mounted() or created(), and the RFC was about adding them? Whould anyone argue that it will make API more complex, that there will be more things to learn and more ways to do things? Sounds absurd, right?
If I understood right, the framework is being rewriten from ground up to be lighter and faster, and all the API is preserved and fully compatible with the previous major version. This is realy cool! Itn't it?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.