DEV Community

Cover image for 3 Secret Vue Patterns for Productivity Boost!
Matheus Gomes πŸ‘¨β€πŸ’»
Matheus Gomes πŸ‘¨β€πŸ’»

Posted on

3 Secret Vue Patterns for Productivity Boost!

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()
    }
}
Enter fullscreen mode Exit fullscreen mode

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'
}
Enter fullscreen mode Exit fullscreen mode

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
    }
}
Enter fullscreen mode Exit fullscreen mode

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,
    }
}
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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$/
)
Enter fullscreen mode Exit fullscreen mode

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+$/, ''))
)
Enter fullscreen mode Exit fullscreen mode

And then we register the component at the bottom:

Vue.component(componentName, componentConfig.default || componentConfig)
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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,
        // ...
    }
})
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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 = {}
Enter fullscreen mode Exit fullscreen mode

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)
})
Enter fullscreen mode Exit fullscreen mode

Then we just export all those modules:

export default modules
Enter fullscreen mode Exit fullscreen mode

Extra tip

If you want to use namespaced modules, you can change them to this:

// [...]
    modules[moduleName] = {
        namespaced: true,
        ...requireModule(fileName),
    }
})
Enter fullscreen mode Exit fullscreen mode

Now our store file will look like this:

import modules from './modules'

export default new Vuex.Store({
    modules
})
Enter fullscreen mode Exit fullscreen mode

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)

Collapse
 
rcoundon profile image
Ross Coundon

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.

Collapse
 
matheusgomes062 profile image
Matheus Gomes πŸ‘¨β€πŸ’»

Yeah, thinking about, I think it would struggle to identify, but it's something nice to know ;)