Productivity Boosts
Hello World! π
I was watching this lecture about Secret Vue Patterns and I thought about sharing with you people some vue patterns that I learned! This time it will be only about productivity and how to improve it!
Hope you like it!
Let's start with...
π΅Smarter watchers
Imagine we are working in a search input component, so let's say we want to fetch on created, then watch the input.
created() {
this.fetchUserList()
},
watch: {
searchText() {
this.fetchUserList()
}
}
We can improve this code by making the watcher accept method names as strings making it look like this:
created() {
this.fetchUserList()
},
watch: {
searchText: 'fetchUserList'
}
This reduces a few lines and makes the code cleaner. π€
The next improvement we can make is by making the watcher call themselves on created:
watch: {
searchText: {
handler: 'fetchUserList',
immediate: true
}
}
Notice that now the searchText was a string and now is an object!
Let's analyze some topics:
- handler: Is the function (or string that has the name of the method) that we want to call.
- immediate: When true means that we don't need to use created hook anymore because the handler will be called as soon as the component is ready, it will be immediately.
Now, since everything is in a watcher now you can reduce the surface area for bugs in your code. Writing this way makes your code cleaner.
π΅Component Registration
We see in almost every project the following pattern in component registration:
import BaseButton from './base-button'
import BaseIcon from './base-icon'
import BaseInput from './base-input'
export default {
components: {
BaseButton,
BaseIcon,
BaseInput,
}
}
It might be a lot of code at first, but we can make it cleaner and more productivity-focused using it like this:
// Can be used on main.js or in a components/_globals.js
import Vue from 'vue'
import upperFirst from 'lodash/upperFirst'
import camelCase from 'lodash/camelCase'
// Requeire in a base component context
const requireComponent = require.context(
'.', false, /base-[\w-]+\.vue$/
)
requireComponent.keys().forEach(fileName => {
// Get component config
const componentConfig = requireComponent(fileName)
// Get PascalCase name of component
const componentName = upperFirst(
camelCase(fileName.replace(/^\.\//, '').replace(/\.\w+$/, ''))
)
// Register component globally
Vue.component(componentName, componentConfig.default || componentConfig)
First: This code we usually place in the main.js file, but we can also create a file called '_globals.js' and place it at our components directory.
Second: The following part gets all the files with the 'base' prefix and is a '.vue' file, which is commonly used to create custom vue components that are used everywhere. But hey, you can change it if you want to.
const requireComponent = require.context(
'.', false, /base-[\w-]+\.vue$/
)
After that, we need to get the component config and the pascal case version of that component:
requireComponent.keys().forEach(fileName => {
// Get component config
const componentConfig = requireComponent(fileName)
// Get PascalCase name of component
const componentName = upperFirst(
camelCase(fileName.replace(/^\.\//, '').replace(/\.\w+$/, ''))
)
And then we register the component at the bottom:
Vue.component(componentName, componentConfig.default || componentConfig)
This is a global registration, which means it will be available to the whole app.
Also, I need to say that this tip only works with webpack
Extra advanced webpack tip:
In this part of the code:
Vue.component(componentName, componentConfig.default || componentConfig)
If in your .vue component you export default that means your component options will be under component.config.default
, which means it will be under the default option at the exported module.
If you are importing using the ES6 syntax (import something from 'something'), that automatically will look for a default. When you are using required tho, it doesn't.
So we have to specify that we want the default or, if you never exported default in your .vue component (module.exports =
), then your component options will be under componentConfig.
Also, if your vue component doesn't have a script tag, then your component will also be under componentConfig.default
.
After all, using that pattern, we don't need to import any 'base' component, just use it when needed.
But why not then import all the components like that?
Well, there's a reason, when you import everything globally, the components that you are not using will be in the distribution bundle when building using webpack. So, imagine that you make a specific navbar for a specific category of the user since every component is imported globally, even if you are not showing it to the final user, it's at the webpack bundle.
That's bad because your bundle will be big and over time will be difficult to maintain.
π΅Module Registration
A bit similar to the previous tip, but this time is for modules.
For those who don't know, vuex modules are little pieces of state management that you can bring to your application.
PS: this isn't a pattern exclusive to vuex, you can find it in redux for example.
You will find it in projects like this:
import auth from './modules/auth'
import posts from './modules/posts'
import comments from './modules/comments'
// ...
export default new Vuex.Store({
modules: {
auth,
posts,
comments,
// ...
}
})
That's one way to improve the code:
// index.js
import camelCase from 'lodash/camelCase'
const requireModule = require.context('.', false, /\.js$/)
const modules = {}
requireModule.keys().forEach(fileName => {
// Don't register this file as a Vuex module
if (fileName === './index.js') return
const moduleName = camelCase(
fileName.replace(/(\.\/|\.js)/g, '')
)
modules[moduleName] = requireModule(fileName)
})
export default modules
First:
We are requiring all of the js files, inside the current folder and we are going through each of those files, and if the file name is index.js we jump it. It's the only thing in that folder that won't be a module.
// modules/index.js
import camelCase from 'lodash/camelCase'
const requireModule = require.context('.', false, /\.js$/)
const modules = {}
We then camel case the file name and add it to a modules hash, or modules object. Requiring the module by its file name.
requireModule.keys().forEach(fileName => {
// Don't register this file as a Vuex module
if (fileName === './index.js') return
const moduleName = camelCase(
fileName.replace(/(\.\/|\.js)/g, '')
)
modules[moduleName] = requireModule(fileName)
})
Then we just export all those modules:
export default modules
Extra tip
If you want to use namespaced modules, you can change them to this:
// [...]
modules[moduleName] = {
namespaced: true,
...requireModule(fileName),
}
})
Now our store file will look like this:
import modules from './modules'
export default new Vuex.Store({
modules
})
Much better.
Hope you enjoyed the reading! Any tips or mistakes I committed, please feel free to write them down below!
See ya next time!π€
Top comments (2)
Nice tips, the only thing I'm not so keen on is using a string as a method name in the watcher rather than the method itself. I've not tried it but I'd imagine the IDE would struggle to identify if a mistake had been made.
Yeah, thinking about, I think it would struggle to identify, but it's something nice to know ;)