This post is a logical continuation of my post / article - How I stopped loving Angular / How I stopped loving Angular .
It is recommended to read before reading.
For about a year now in all projects in which I participate, I use Vue instead of Angular.
In this post I will share the main impressions and differences after Angular, as well as tell some things from the actual experience of using Vue on combat projects.
Here is a brief list of the main problems that bothered me in Angular at the time of writing the previous article:
You should immediately make a reservation about Dependency Injection: after switching to Vue, it’s still worth noting that in Angular it’s more convenient to mock external dependencies in unit tests.
This depends entirely on the code base and internal best-practices, but often in Vue it is more difficult to mock something in tests than in Angular (the example will be lower).
However, I do not consider this to be a serious reason for the use of a pattern so alien to JavaScript on the front end.
And one more small reservation regarding why Vue was chosen, and not React.
Disclaimer: all points below are VERY subjective (therefore, they should not be taken as criticism, but only as a personal view):
reate-react-app
in my opinion, today, is the least functional CLI of all the most popular frameworks, so for React it is worth creating a much more powerful tool for a long timeA little about the main differences and impressions that I noted for myself when switching to Vue.
The first thing you come up with when choosing a UI framework is, of course, the documentation.
I will not re-parse the Angular documentation with its
Banana in a box
I can only say that in Vue it is much simpler and more intelligible.
I will not give examples, because all of them may seem subjective to you, while starting to read the docks, you immediately feel that they were written for people.
It is also worth noting that the docks are written in 6 languages, although English would be enough (I think that now any developer should know English at least at the reading level).
From my previous article, you could understand that I had previously thought that the Angular CLI is the best , but as it turned out:
@angular
and CLI packages with different versions, which, in turn, leads to a tremendous problem when upgrading the CLI / angular version.On the other hand, Vue CLI 3 is the coolest CLI for today.
Angular uses Zone.js to track changes, which monkey patches for this standard API, like setTimeout
.
This leads to certain problems:
Vue does not need Zone.js, tracking works by turning all data / state into Observer.
At one time, when I saw sortsy, I was even somewhat upset that there was no magic.
At the top level, everything is trivial: Vue goes through all the objects through Object.defineProperty (for this reason, IE8 and below are not supported) and thus adds a getter and a setter.
Even nothing special to add ...
However, this approach has pitfalls, but they are extremely simple to describe and easy to understand.
And the understanding is not so much Vue, as the JS itself is at its core.
In Vue, you cannot dynamically add new root reactive properties to an existing instance. However, you can add a reactive property to nested objects using the Vue.set method (object, key, value):
var vm = new Vue({ data: { a: 1 } }) // vm.a — vm.b = 2 // vm.b Vue.set(vm.someObject, 'b', 2)
It should also be noted that since version 2.6, Vue will not have these problems either, since there will be a transition to the Proxy, and it will be possible to also track the addition / removal of prop.
In Angular, from the box, the core of the framework was RxJS, everything is Observable.
Despite my love for Rx, many people and I had questions about whether it was needed inside Angular?
Especially at first, many people simply turned Observable into promises through .toPromise()
.
And in general, the idea of ​​a shared data bus is not the easiest to understand, in addition to the complexity of Angular itself.
At the same time, being such a massive framework, Angular out of the box does not provide implementation of the most popular data management pattern for today - State Management.
There is NgRx , but it became fully usable not so long ago - as a result, we even have an old project with a custom implementation of a Redux-like stor.
And now about Vue.
Not only can any RxJS fan be able to easily connect it at any time by simply adding a new package.
Already there is even Vue-Rx , allowing the use of RxJS Observabl'y along with data.
If we talk about State Management, that is a great official Vuex.
At one time, with great surprise, I discovered that besides him there are also a huge number of alternatives .
Actually, this is where the main advantage manifests itself (although it may seem like a disadvantage to someone) Vue - everything can be connected as needed.
Just add:
What would you lack, it always integrates simply and quickly.
The reason for one of the most severe psychological injuries I received from Angular is a router.
Despite the fact that he has already corresponded three times, he is still terrible (yes, I repeat).
I will not describe all his problems in detail, but since the topic is sore, briefly:
If you look at the sources, you can understand that many of these problems are related to the complexity and functionality of the router.
That is, do not even be in it strange solutions like teams, the number of features still makes it difficult and difficult.
In Vue, the router is extremely simple and working.
Speaking of simple, it should be mentioned that at one time I did not find the usual Angular parameter abstract: true
.
Out of the box it is impossible to make a route without a template, but this is solved in one line of code - by creating a component like:
// AbstractRoute.vue <template> <router-view/> </template>
Is it bad that there is no such functionality out of the box?
And yes and no, because on the one hand it is important how easy the problem is solved, and on the other - the complexity and speed of the router itself (as opposed to tricky Angular).
By the way, here's the cool thing:
path: `url/sub-url/:id', component: MyComponent, props: true
Now the component MyComponent
will have a props
id
, which will be a parameter from the URL.
This is an elegant solution, because id
will immediately be reactive and it's very convenient to use it in the component (again, it just works ).
On the one hand, all components in Angular are TypeScript classes.
Despite this, the use of many TypeScript chips is often either inconvenient or impossible.
This applies primarily to OOP - inheritance, abstract classes, scopes.
The main problems arise with Dependency Injection and AOT compiler (God forbid you run into them ).
In Vue, the instance configuration is an object — it’s easy to work with and expand, refactor.
To reuse the code there are powerful things - Mixins and Plugines (see below for details).
In the Enterprise segment, components usually have a finished design, mock-ups, and so on. Most often, they are written from scratch, immediately sharpened for a specific task / product / project.
But not always developers have the opportunity to create everything from scratch, especially with pet projects and prototyping.
Here libraries ready UI components come to the rescue, and for Vue there is already a great many of them: Element, Vuetify, Quasar, Vue-Material, Muse, iView, and so on.
Especially I would point out Element and Vuetify, the impressions are strictly positive: beautiful and stable components for any needs, good docks.
We also like the set of Buefy components based on the cool Bulma CSS framework, it is especially convenient to use it in Bulma applications, where third-party components are connected as needed.
In the case of Angular - Enterprise-level component libraries of just a couple of pieces, this is primarily Angular Material (Google) and Clarity (VMWare).
Unfortunately, the rate of development of Clarity has recently declined, which is even more frustrating in terms of Angular's prospects in this matter.
And now the main problems from the real experience of using Vue in combat projects.
The main problem Vue for serious projects, today, I would call too much freedom of choice.
On the one hand, the fact that the same thing can be done in many different ways is very cool.
But, in reality, this leads to the fact that the code base becomes rather inconsistent.
An exaggerated example: custom logic on a page can be made by declaring a certain component, and it can be as local
const Component = { created() { // logic ... }} new Vue({ components: [Component],
and global (accessible from everywhere).
Vue.component('Global', { created() { // logic ... }})
You can make a local Mixin which will implement this functionality.
const mixin = { created() { // logic ... }} new Vue({ mixins: [mixin],
and it (admixture? is it? ..) again can be global.
Vue.mixin({ created() {// logic ... }})
In the end, there are plugins that do almost the same thing as global mixins.
const MyPlugin = { install(Vue, options) { Vue.mixin({ created() { // logic ... }}) }
Of course, all these features are actually needed for specific tasks and are very useful.
But which option to choose is not always obvious to the developer, especially for a beginner.
Despite the fact that we use Vuex, I noted for myself that sometimes people find it hard enough not to use data () to be used instead of state.
This is a question of speed rather - it is clear that adding something to data faster, but almost always it turns out that later it will have to be taken out in the state and spend extra time on it.
Perhaps, the situation in projects where there is no review code and there are a large number of juniors without much experience with Vue will be especially sad.
I can assume that working with this code in a few months will be completely unpleasant.
Also, after Angular, it was not very convenient and obviously mocking some things in Jest.
A specific example is local storage. Someone decided to goadling this issue on a githaba .
Object.defineProperty(window, 'localStorage', { value: localStorageMock, })
I did not find the solution beautiful, but as it turned out, there is a more elegant one.
global.localStorage = localStorageMock;
Yes, this is not a Vue problem, but an ecosystem problem in comparison with Angular.
In my opinion, precisely such real examples should be described in the documentation.
In general, the guys from Vue know about this problem and solve it by writing a cookbook with recipes .
In fact, this is a set of ready-made solutions for popular tasks.
For example: unit tests ,
validation and work with HTTP .
However, the recipes are still quite basic and are not enough for serious tasks.
Tests are described and for a general understanding of this is sufficient, but the mocking mentioned above will have to be searched for yourself.
I’ll talk about validation later, but the work with HTTP is not precisely described in depth.
I would say that Angular taught me to work with the backend API through services, I consider this a good pattern that greatly facilitates support and reuse of the code.
But since we don’t have the notorious DI, and it’s not very convenient to create service instances, I would like to have a similar pattern in the Cookbook.
For the most part, we have solved this problem by developing local code conventions and best practices. Links at the end of the article
I have already said and written many times about how cool and useful TypeScript is, but the fact is that you need to know how to cook it.
In Angular, everything is tied up with experimental features (decorators), inner classes and hundreds (thousands?) Of unnecessary abstractions.
In Vue, the possible use of TypeScript is much more logical - it only allows you to expand the capabilities of the developer, without having to be tied to certain features of the language.
However, to date, using TypeScript with Vue is not always that easy, here are a number of problems we have encountered.
First, they never took my pull request because of their broken tests = (((
The joke, of course, is just my personal pain.
The main problem of TypeScript in Vue I would call the fact that there are two different official approaches to its use .
This is Vue.extend (taipings that come bundled with Vue out of the box and supported along with the main library)
import Vue from 'vue' const Component = Vue.extend({ ... })
and very similar to the Angular decorator vue-class-component
import Vue from 'vue' import Component from 'vue-class-component' @Component({ template: '<button @click="onClick">Click!</button>' }) export default class MyComponent extends Vue { message: string = 'Hello!' onClick (): void { window.alert(this.message) } }
Personally, I don’t like the class-component, and for several reasons we use Vue.extend:
Here are some other problems with TypeScript, of varying degrees of sadness:
this.$store
from the component are possible with virtually any payloadBut Vue.extend (), in turn, has some drawbacks:
// myObjectProps interface MyType = {...} // data interface MyComponentData = { someBooleanProp: boolean; } export default Vue.extend({ data(): MyComponentData { // data return { someBooleanProp: false }; }, props: { myObjectProps: Object as MyType // TS },
In general, forms are a problem not only for Vuex, but for almost any State Management pattern.
Still, it involves a one-way data stream, and the forms imply a two-way building.
Vuex offers two solutions .
Through binding the value
to the state value, and for updating the input event with sending the commit:
<input :value="message" @input="updateMessage">
// ... computed: { ...mapState({ message: state => state.obj.message }) }, methods: { updateMessage (e) { this.$store.commit('updateMessage', e.target.value) } }
Or nevertheless use two-sided biding and v-model
and the receiving and commit should be performed via a getter and setter:
<input v-model="message">
// ... computed: { message: { get () { return this.$store.state.obj.message }, set (value) { this.$store.commit('updateMessage', value) } } }
We think the second option is more convenient and concise, but still very verbose.
Present to describe in a similar way a form with 20 fields or more?
It turns out a huge amount of code for such a seemingly primitive task.
The problem is a little less relevant if you use mappers, namely mapGetters()
and mapMutations()
,
but, as I wrote above, they are currently working poorly with TypeScript and had to find another solution.
We wrote a primitive mapper, which adds a getter (getter from state) and a setter (commit mutation):
static mapTwoWay<T>(getter: string, mutation: string) { return { get(this: Vue): T { return this.$store.getters[getter]; }, set(this: Vue, value: T) { this.$store.commit(mutation, value); } }; }
It allows you to reduce the amount of code and describe the fields in a similar way:
stringTags: Util.mapTwoWay<IDatasetExtra[]>(STRING_TAGS, UPDATE_STRING_TAGS)
And, accordingly, use the v-model
:
v-model="stringTags"
What was somewhat surprising after Angular is that there is no form validation out of the box in Vue. Although it would seem, the feature is very popular.
However, it does not matter, there are two most popular solutions for this task.
The first is a very similar mechanism with template-driven forms in Angular - vee-validate .
In fact, you describe all the validation logic in HTML.
<input v-validate="'required|email'">
I would say that this approach is suitable only for relatively small / simple forms.
In reality, validation is often very sophisticated, and it becomes inconvenient to describe everything in HTML.
Plus it doesn't work very well with Vuex.
The second solution is Vuelidate. An incredibly elegant way of validating almost any component — the model itself is validated, not one or the other input.
The funny thing is that before discovering this wonderful package we ourselves had already started writing something similar.
<input v-model="name" @input="$v.name.$touch()">
import { required, email } from 'vuelidate/lib/validators' export default { data () { return { name: '' } }, validations: { name: { required, email } } }
I highly recommend, if necessary, the validation of forms immediately consider Vuelidate - it works fine (including Vuex), it is easy to connect and customize.
Actually, this is the main problem (?). Vue is only a library, not a heaped all-in-one framework.
Out of the box:
Yes, there are officially supported:
However, with all this, in Vue out of the box there is such a cool built-in stuff like Animation ???
At one time, stumbled upon was somewhat surprised by the rich possibilities for the animation of just about anything.
This is perhaps a much more convenient and powerful toolset than in the same Angular.
A cool example from the documentation :
A minor problem, again not directly related to Vue, but which we encountered on a real project.
Out of the box, when generating a project for e2e testing, there is a choice between Nightwatch and Cypress .
Despite the fact that I personally see Cypress as the coolest e2e testing tool for today, there is still no support for browsers other than Chrome.
Therefore, we could not choose it for the combat project - the real customers still use other browsers.
Once our tests began to crash on Linux CI for a completely inexplicable reason (everything was OK on Windows), and the errors were not informative.
Later we managed to find out that the problem is related to hashes (#) in URLs .
URL' vue-router
.
, ChromeDriver
, Nightwatch ( Cypress TestCafe), "" url :
.url('data:,') .url(client.globals.devServerURL + `/#/my-hashed-url`)
Angular boilerplate , , Vue .
, , Developer Experience. , .
, .
Vue — React Native.
.
, Vue NativeScript Vue, , , .
Weex, , .
. .
: http://www.stefankrause.net/js-frameworks-benchmark7/table.html
( " " ).
: GitLab ,
CodeShip , Alibaba, Xiaomi .
.
— TypeScript Vue , , .
, production.
, , TS Vue, JavaScript.
, , , TypeScript .
Vue , , .
, " ", Tide Vue .
, , .
, .
cookbook — , ( ).
Vue seed (TS, Jest, ESlint ..), , Vue CLI 3,
.
Vue CLI 3 .
??
Yes. Thanks for attention.
PS: , Vue — ,
PPS: , () — =(
: https://medium.com/@igogrek/how-i-started-loving-vue-d41c1c8e77e1
Source: https://habr.com/ru/post/358766/
All Articles