Vue Modular
Helps you implement a folder-by-feature module structure in large Vue projects 👷‍👷‍
Building large Vue projects
When Vue projects grow, the classic folder-by-type folder structure can become unmanageable because dependencies across files are hard to refactor and mentally compute.
Instead, you should consider structuring your code into "features" that are encapsulated with their own components, vuex module, routes and more. Think of it as a structural design pattern that encourages encapsulation of related code.
The main purpose of this repo is simply to prescribe a scalable project structure. To ease the setup, this simple Vue plugin helps you do that easily by extracting vuex stores and router definitions from each module and registering them globally.
Plugin installation
Install the NPM module
yarn add vue-modular
In your main.js (or equivalent), add
 // simplified for example purposeconst router = const store =  // install the plugin with modules and router + store referencesVue
The plugin currently achieves three things:
- Vuex stores defined in each module are registered on the global vuex instance
- Vue router definitions in each module are merged and registered on the global router instance
- A vue instance helper
vm.$modules
is injected into all components which makes it easy to access custom values that each module can export
Recommended setup
Please see the example folder for a reference setup
Choose a folder structure that will with your local modules. Each module can be simple (foo
below) or complex (bar
below), depending on your need.
modules/
│
├── foo/
│ ├── index.js
│ ├── router.js
│ ├── store.js
│ └── ComponentA.vue
│
└── bar/
├── index.js
├── router.js
├── store/
│ ├── mutations.js
│ ├── actions.js
│ └── getters.js
│
├── views/
│ ├── PageA.vue
│ └── PageB.vue
│
├── services/
│ ├── datasource.js
│ └── tracking.js
│
└── tests/
├── services.spec.js
└── views.spec.js
There's technically no restriction on how you structure your directory tree or what you name your files, as long as each module exports an object like this
// modules/foo/index.js     router // module vue router (if any)  store // module vuex store (if any)  custom:     // optional extra info you to share    foo: 'bar' Â
The router and store objects are exactly what you'd expect and know from Vuex modules and Vue Router definitions
// modules/foo/router.js     routes:           path: '/foo'      name: 'foo'      component: ComponentA         {      }
// modules/foo/store.js    namespaced: true  state:     foo:     mutations:      {      statefoo = foo    }    getters:      {      return statefoo    } Â
When to use it
Don't over-engineer prematurely
For small and medium-sized projects, the folder-by-type approach is usually simpler to navigate because it's helpful to have all your routes and stores in one place.
If you're a one or two people building a small Vue app, that's usually fine.
LIFT your code base
When projects grow to hundreds of components with large developer teams working on the same code base, things start to change.
What you're looking to achieve can be abbreviated LIFT: Structure the app such that you can Locate code quickly, Identify the code at a glance, keep the Flattest structure you can, and Try to be DRY
Do remember though that not all your code can or should be self-contained: base UI components, API client, general purpose utilities etc. should probably not be modules.
But an authentication module with it's own routes (ex. /login), store (ex. currentUser), tests (ex. login.spec.js) and view components (ex. Login.vue) is ideal for encapsulation.
Tips & tricks
Exposing stuff from modules
As a thumb rule, the more isolated you can keep your modules from each other and the rest of your codebase, the easier it will be to maintain.
However, sometimes you do want to export a component, service or utility function for other code to use. In these cases, I recommend that you do it explicitly and only from your module's index.js
// modules/foo/index.js Â
//Â somewhere-else.jsÂ
This achieves two things:
- The import statement reads well and is self explanatory i.e. import x from module foo
- It's a lot easier to refactor and modify your module afterwards, because it's explicit what is exposed outside of your module. You can even change file names and still keep the named exports outwards in order to not break other stuff while refactoring.
Lazy load modules async
When your code base grows, bundling everything together in one giant chunk results in a big up-front download for code that the client might never use.
Instead, consider using code splitting by loading your modules only when needed. You can do this inside each module with lazy loading routes, or you can load in entire module definitions with the registerModules
function and webpack's dynamic imports
 const  default: foo  = await import'./modules/foo'
Contribute
For the repo and install dependencies
# Serve example app yarn serve # Production build yarn build # Run unit tests yarn test:unit # Lint files yarn lint
PRs and issues are welcome!
License
Use it, fork it, change it, do what you want.