I understand that reactivity is a big deal in VueJs. However, I tend to use OOP for my front end development, and therefore data is often provided by functions. This will be naturally non-reactive, which can be overcome by using this.$forceUpdate.
The question is - is this a fundamentally bad concept? Or is it just fine? Is there a better way? Probably watchers can be used, but I feel like that would make the code more complicated.
Example:
<template>
<multiselect
:value="myclass.getValue('param1)" />
#input="val => valueUpdated(val)"
</template>
Then in methods:
valueUpdated(newVal)
{
this.myClass.setValue(newVal);
this.$forceUpdate();
}
Note, myClass has async data, so by the time of the first rendering, it is not filled with the data. MyClass comes into the component as a prop, and it is a TypeScript class.
this solution works fine, but is there a better (correct) way?
Doing/forcing view to update manually is a fundamentally bad concept. The view should update at the moment your data changes - and it should update automatically. This is what reactivity gives you. This is like going from a car with manual gears and 49 shifts to a car with automatic shifting.
The whole idea is that you define declaratively the behavior of your view - how it depends on the data model. Building the view imperatively is more tedious and error-prone and certainly less elegant.
OOP may work well in non-visual libraries - but for UI composing components with props and events works better.
Related
I wrote a Stenciljs component "textarea" with a lot of methods and logic. Additionally I need an inputfield with pretty much the same functionality but some minor differences. I don't want to maintain two components that have nearly the same tests, nearly the same styles and nearly the same methods that would end in a lot of code duplicates.
Right now i created a generic component "idb-input" with a "type" property that can be "textarea" or "textfield" and use if else statements to decide which methods are allowed for this type. Since this is a bit messy i would like to have a "base component" and inheritate from it for example. I already tried to use a generic typescript class but i can't use the decorators and a lot more limitations.
Further this approach isn't that clean in HTML:
<idb-input type="textarea">
<idb-input type="numberfield">
<idb-input type="inputfield">
What is the right way solving this problem?
So I believe the title introduces my question. Given you're introducing a new dependency in each component you want to use mapGetters in, this adds bloat to your package.
This also means your component is now specifically bound to Vue, which is 1 more thing to contend with were you to port components over to another framework like React.
Consider these 2 computed property lists:
import { mapGetters} from 'vuex'
computed: {
...mapGetters({
active:'interface/active',
mode:'interface/mode',
views:'interface/views',
}),
}
vs
computed: {
active:() { return this.$store.getters['interface/active'],
mode:{ return this.$store.getters['interface/mode']},
views:{ return this.$store.getters['interface/views']},
}
Sure the latter is a tad bit more verbose, but nothing overly bad. You've still got all your getter data the same way in either scenario. To be this seems like totally unnecessary fluff for the small benefit of saving a few characters.
The former example weighs in at 207B (201 characters)
while the later example weights in at 207B (201 characters).
To me that means in terms of type savings, there's virtually none to be had. However I do concede that the former is more readable, portable and ultimately easier to maintain as your relevant data is all in 1 spot.
There are a couple of things that I think are worth mentioning here, regarding an opinion you have on the helper function like mapGetters
About your code being tightly coupled to vuex should you decide to import mapGetters: it should be clarified that the approach where you access this.$store.getters directly doesn't make your code any less coupled to vuex. After all, such direct access already does assume quite a lot about store's functionalities and how they're exposed to components.
Take snippets you provided as examples, it can already be seen that your store implementation a) has a concept called getters, and that b) it supports modules such that you can define an interface module like you did.
Suppose one day, you somehow decide to switch store's implementation to something like puex, which has absolutely no concept of either getters or modules, you will have to refactor all 3 declarations anyway. Or even if you decide to switch to something more advanced like Flure, it will also put you in a similar situation: though Flure has concepts of getters and modules, they aren't exposed the same way vuex does, so you have to edit all 3 lines (again).
Let alone porting to React, the code you have in computed properties won't even be portable to other store libraries created specifically for Vue. So, really, you shouldn't worry too much about using mapGetters.
Secondly, for type-saving sake, if you're not going to rename these properties anyway, your mapGetters call can actually be shortened to a one-liner:
computed: {
...mapGetters('interface', ['active', 'mode', 'views']),
}
Third, once you start having multiple lines of these, it's just easier to track all declarations (e.g., how many getters or state fields you expose from each module). So these helper functions actually helps improve maintainability if used well.
computed: {
...mapState('interface', ['list', 'packages']),
...mapGetters('interface', ['active', 'mode', 'views']),
...mapGetters('foo', ['bar', 'baz']),
}
Forth, regarding bundle size, on second thought I believe this might be the main reason why people downvoted you. So, I'll try to explain:
The thing you need to understand is that real-world production webapps these days use build tools like webpack to create javascript bundles. And because of that, javascript you wrote can be pretty much regarded as source code, which is totally different from the code shipped in a bundle. The reason why they are so different is that the bundle can utilize both minify and gzip techniques to achieve drastic improvement in file size (usually more than 70% size reduction in practice).
In other words, for webapps these days, javascript is not being served as-is, but almost always be processed by webpack or the like. So, all the character counting you have been doing on the source code isn't going to make a noticeable difference to the final bundle size at all. And because of this, some people might regard your concern as an extremely bad form of micro-optimization, hence their downvotes.
If I were you, I will try to embrace these helper functions as much as possible, and look at the bundle size after automatic build optimizations before worrying about the bundle size at all. And even if the final bundle size is too big to your liking, I can almost guarantee you won't gain much from mapGetters refactoring, as it's usually other stuff that substantially bloats the bundle size (like a view component library, for example). Really, premature optimization on vuex isn't really worth your trouble.
So, to me, these functions (mapState, mapGetters, mapActions, mapMutations) are definitely worth using.
I have built a complex date management component that itself is made up of many smaller components nested a few layers deep. The components at different levels need access to observables and actions that live in the root component. I am dealing with this by passing what I need through props. While this all works, the code would be much simpler and easier to follow if I could create a store in the root component, use provider, and then inject in the child components as needed. Exactly like we do when creating a store for app wide use.
So to summarize...
I want each instance of my complex date component to create it's own instance of a store. I want to then use provider and inject to pass this store instance to child components.
Before I proceed with this significant refactoring, I want to be sure that this approach will work and is sound. Is there an alternative approach that makes more sense. Am I overlooking something?
Note: Although I know code samples are useful, I'm hoping that what I am describing is straight forward enough that they are not needed in this case.
Yes, you can create stores for a component and use Provider and inject to pass them to child components. Actually that is what I do in my current project and it works well.
BTW, I think this is a big Pro of MobX compared to Redux. You do not need to keep all states in a single global store. You can create complicated components that work perfectly by themselves. It is object-oriented, which is the philosophy of React.
What is the difference between:
this.currentLanguage = this.$store.getters.currentLanguage;
and this for getters:
this.currentLanguage = this.$store.state.currentLanguage;
Also this:
this.$store.commit('setLanguages', this.languages);
is gives me same results with this in mutations :
this.$store.state.languages = this.languages;
I'm receiving what I want in both cases. And getting value direct is easier because I don't need to write getter/mutation.
Why should I use getters/mutations?
you do not need to use getters in this case. getters are like computed properties on a regular Vue component, so you only need them when the data is a more complex descendant of the other data.
you want to use mutations, however, because even if it looks like the this.$store.state.languages = this.languages is working, it isn't necessarily updating it correctly. if you have other components depending on the store, it may not receive the new data because it hasn't undergone the full mutation process.
When you use mutations you have a clear record of what changed in things like vue-devtools. It also allows for time travel debugging.
This is really nice when you're debugging.
The state vs getters is a lot more of a gray area. I personally like not having to think if something is in state or a getter. If I need to get something from the store I use a getter.
That question got answered pretty well by the Vuex Docs itself:
Again, the reason we are committing a mutation instead of changing store.state.count directly, is because we want to explicitly track it. This simple convention makes your intention more explicit, so that you can reason about state changes in your app better when reading the code. In addition, this gives us the opportunity to implement tools that can log every mutation, take state snapshots, or even perform time travel debugging. https://vuex.vuejs.org/guide/ - nearly bottom
So basically the idea is to explicitly track changes. This allows other tools to attach themself to those mutations and actions and track your code which is a huge benefit for large scale applications. It will also make your code more readable and at a later stage much easier to update. Ever read into an old project where they always changed on the go without one "way" to do it - it won't happen here.
I am building an application that uses Vue to display most of the data that my users will see (charts, tables, etc.)
Now, imagine I have a table built with Vue with a list of actions performed by each user. Now, the user name cell will be especially formatted with the user’s picture, social handle, etc. The way the user is presented in this table is the same as it will be in other components. It will also contain some especific functionality (e.g. hovering over the picture will provide expanded information about the user; but this functionality is only planned).
My objective is to reuse as much code as possible. This is why I was planning to have a small UserProfile.vue component inside my Table.vue component. Using this logic, I may repeat this for other information as well (think CompanyProfile.vue, ProjectDetail.vue, etc.).
Is this a good idea? Will it have cause significant performance issues to have multiple vue components inside one and other?
Vue has optimized the result of rendered html as far as possible. You don't need to worry a lot about it. Child components only update when their respective data has changed, that is usually quite cheap.
Good to keep in mind:
When authoring components, it’s good to keep in mind whether you intend to reuse it somewhere else later. It’s OK for one-off components to be tightly coupled, but reusable components should define a clean public interface and make no assumptions about the context it’s used in.
For larger applications, you may use Async Components
Vue will only trigger the factory function when the component actually needs to be rendered and will cache the result for future re-renders. For example:
Vue.component('async-example', function (resolve, reject) {
setTimeout(function () {
// Pass the component definition to the resolve callback
resolve({
template: '<div>I am async!</div>'
})
}, 1000)
})
I found some interesting topics that you might want to look:
Performance for large number of components
Performance degradation when using components
Unlock performance tracing in vue
Analyze runtime performance
So now, you know what to do. Hope, it helps.