├── CONTRIBUTING.md ├── LICENSE └── README.md /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | These recommendations are results from my daily work with Vue.js. They shouldn't be taken as universal rules, rather as an invitation for other developers to improve or challenge them. Nevertheless, you should reason your contribution taking the Vue.js ecosystem into account. 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Julian Claus 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NOT MAINTAINED Vue.js best practises 2 | 3 | These recommendations should give you assistance to use Vue.js in a progressive and future-orientated way. They are focused around the core of the official Vue.js dependencies, that are the [vue-router](https://github.com/vuejs/vue-router), [vuex](https://github.com/vuejs/vuex) and [vue](https://github.com/vuejs/vue) itself. 4 | 5 | ## Guidelines 6 | 7 | The recommendations differ in their importance. Check the following table to get an overview. 8 | 9 | Keyword | Description 10 | ------- | ------------------------------------------------------------------------------------ 11 | Shall | Shall is used in a sense of must. Ignoring it can result into problems 12 | Should | Should has a higher weight than may. It is the recommend approach 13 | May | May is used in a sense of optional. It doesn't make a difference for the application 14 | 15 | ## Best practises 16 | 17 | ### Shall 18 | 19 | #### You shall use getters/setters for your data properties 20 | 21 | Mutating a property should be as explicit as possible. Violating it could lead to an unwanted property mutation if you need the property as read-only. 22 | 23 | ```javascript 24 | computed: { 25 | property () { 26 | return this.$store.state.property 27 | } 28 | } 29 | ``` 30 | 31 | You have two possibilities to improve that. 32 | 33 | Use computed getters/setters: 34 | 35 | ```javascript 36 | computed: { 37 | property: { 38 | get () { 39 | return this.$store.state.property 40 | }, 41 | 42 | set (property) { 43 | this.$store.commit('UPDATE_PROPERTY', property) 44 | } 45 | } 46 | } 47 | ``` 48 | 49 | Use Vuex [getters](https://vuex.vuejs.org/en/getters.html): 50 | 51 | ```javascript 52 | getters: { 53 | property: state => { 54 | return state.property 55 | } 56 | } 57 | ``` 58 | 59 | [Back to top](#vuejs-best-practises) 60 | 61 | #### You shall use `Vue.set` and `Vue.delete` to mutate/delete properties/array keys 62 | 63 | Since Vue.js [can't detect property additions or deletions](https://vuejs.org/v2/guide/reactivity.html#Change-Detection-Caveats), you sould always use `Vue.set` to add and `Vue.delete` to delete properties or array keys. This is especially necessary when setting or deleting an array index or an object property. Please note: This won't be necessary in future versions. 64 | 65 | [Back to top](#vuejs-best-practises) 66 | 67 | #### You shall use pure functions 68 | 69 | In Vue.js, many click directives should be handlers. Handlers do a couple of things. They should call other functions instead of combining the whole logic, which can lead to unpure functions with unwanted side effects. It's pretty easy to overlook that. Given a common example: 70 | 71 | ```js 72 | setIsVisibleModal (isVisibleModal) { 73 | this.isVisibleModal = isVisibleModal 74 | if (isVisibleModal === false) this.setIsVisibleModalButton(true) 75 | } 76 | ``` 77 | 78 | This function does two things, even so the function name clearly coerces you to do only one thing. It's easier and more testable to use a handler instead, like showed here: 79 | 80 | ```js 81 | modalHandler (isVisibleModal) { 82 | this.setIsVisibleModal(isVisibleModal) 83 | if (isVisibleModal === false) this.setIsVisibleModalButton(true) 84 | } 85 | ``` 86 | 87 | [Back to top](#vuejs-best-practises) 88 | 89 | ### Should 90 | 91 | #### You should handle HTTP requests at Vuex actions only 92 | 93 | HTTP requests should be independent from components. The underlying protocol, URI or route may change and you have to rewrite code at multiple places. It is better to have API-related operations at a centralized place instead of doing HTTP requests at the component level. 94 | 95 | [Back to top](#vuejs-best-practises) 96 | 97 | #### You should use component lazy loading 98 | 99 | When you import a component the usual way, it gets loaded upfront, regardless of if it's needed. You can prevent that behavior with a function. 100 | 101 | ```javascript 102 | const Component = () => import('./Component') 103 | ``` 104 | 105 | [Back to top](#vuejs-best-practises) 106 | 107 | #### You should always define methods instead of cluttering hooks 108 | 109 | If you need to setup up something during one of the hooks upfront, you should define a method instead of writing everything into the hook. With that, you can easily reinvoke the method later if necessary. 110 | 111 | [Back to top](#vuejs-best-practises) 112 | 113 | #### You should use Vuex strict mode 114 | 115 | The Vuex is the centralized state of your application. To reason about state mutations, you must be aware of changes in an exact manner. When the strict mode is enabled and the state is mutated outside of a mutation handler, an error will be thrown. 116 | 117 | [Back to top](#vuejs-best-practises) 118 | 119 | #### You should not use `Vue.parent` 120 | 121 | In general, components should be loosely coupled. However, there are situations where components can also be tightly coupled. If you have a dedicated mother-child-relationship, it is perfectly okay to use `Vue.parent` to access the mother component from the child. Nevertheless, if components can stand on their own, you shouldn't use it. It could be that the relationship get's interrupted and the child component is wrapped around another component which makes `Vue.parent` useless. 122 | 123 | [Back to top](#vuejs-best-practises) 124 | 125 | #### You should not use watchers `deep: true` 126 | 127 | Using `deep: true` at a watcher leads to heavy calculations because Vue.js has to recursively check for property changes. It's better to use an explicit getter instead and watch for it. 128 | 129 | [Back to top](#vuejs-best-practises) 130 | 131 | #### You should follow an entity based workflow 132 | 133 | Using entities as component names can heavily simplify reasoning about your application. A component is responsible for doing one thing and the name should reflect that. Therefore, you may end up using names like `UserGet`, `ProductPatch` or `ProductPost`. For common logic use mixins. Furthermore, your states should be an image of your server API. Try avoiding abstraction of your API. 134 | 135 | [Back to top](#vuejs-best-practises) 136 | 137 | #### You should let mother components handling errors 138 | 139 | Consider a product page with a container component holding product components with another subcomponent loading product images. Thus, the responsibility chain goes down from the container component to the product component to the product images component. If there is an error with the product images, let the product component handle it. If the product component crashes, let the container decide what to do. This guarantees that the component with the most knowledge makes decisions. 140 | 141 | [Back to top](#vuejs-best-practises) 142 | 143 | ### May 144 | 145 | #### You may use a state constructor 146 | 147 | You might find yourself reseting a Vuex state. Instead of setting verbosely every property of the state, define a state constructor instead. To do so, wrap the state into an arrow function and use `Object.assign` to reset the state. 148 | 149 | ```javascript 150 | const stateConstructor = () => ({ 151 | entity: '' 152 | }) 153 | ``` 154 | 155 | Mutations: 156 | 157 | ```javascript 158 | RESET_ENTITY (state) { 159 | Object.assign(state, stateConstructor()) 160 | } 161 | ``` 162 | 163 | [Back to top](#vuejs-best-practises) 164 | 165 | #### You may not always use state managament for a component 166 | 167 | Most times, a component is a seperated, isolated unit of your application. Therefore, there is no need for such a component to be accessible from the outside or the other way around. You can save a lot of state managament if you ask yourself some questions: 168 | 169 | - Do I need the components data or state elsewhere? 170 | - Does an other component must mutate the components behaviour? 171 | - Do I need the component elsewhere? 172 | 173 | You may end up putting parts of your component into the store and the rest into the component. 174 | 175 | [Back to top](#vuejs-best-practises) 176 | 177 | #### You may use upper-case module names 178 | 179 | To distinguish between modules and entities, you may use an upper-case module name. If you have many nested modules, this prevents you from mutating the root state, rather than a dedicated object for the entity. To use an upper-case namespace, simply define your modules like this: 180 | 181 | ```js 182 | new Vuex.Store({ 183 | modules: { 184 | User: user, 185 | Product: product, 186 | Basket: basket 187 | } 188 | }) 189 | ``` 190 | 191 | [Back to top](#vuejs-best-practises) 192 | 193 | #### You may follow these naming conventions 194 | 195 | Vue.js provides several varying APIs for DOM and state mutations or event communications. Since you decide about the interface names (like methods, events or DOM elements), a consistent naming convention is recommended. 196 | 197 | ##### Components 198 | 199 | | Entity | Example | 200 | |---------------------|--------------------| 201 | | Component file name | `SearchBar`       | 202 | | Component | `` | 203 | | Events | `make-user-visible`| 204 | | Mixin file name | `SearchBarMixin` | 205 | | Mixin | `SearchBarMixin` | 206 | | Props               | `:is-red="true"` | 207 | | References         | `user-post`       | 208 | 209 | ##### Router 210 | 211 | | Entity | Example | 212 | |-------------|------------------| 213 | | Route name | `DashboardIndex` | 214 | 215 | ##### Vuex 216 | 217 | | Entity | Example | 218 | |--------------------|------------------| 219 | | Module file name | `user.module` | 220 | | Module | `User` | 221 | | Mutation file name | `user.mutations` | 222 | | Mutation functions | `SET_USER` | 223 | | Action file name | `user.actions` | 224 | | Action functions | `postUser` | 225 | | State file name | `user.state` | 226 | | State properties | `isUserVisible` | 227 | 228 | [Back to top](#vuejs-best-practises) 229 | --------------------------------------------------------------------------------