当一个 Vue 项目源文件达到 400 以上时,发现开发起来已经显得有些臃肿,本文就在多个方面进行一些模块化的尝试。

更低程度的模块化

通常 CLI 生成的项目结构都比较简单,易于上手。一般来说都会安装 Vuex、Vue Router 包进行配合开发,他们的默认结构大体是这样的:

1
2
3
4
5
6
├── App.vue
├── assets
├── main.js
├── pages
├── router
└── store

在小型项目中,开发完全没有问题,即使大型项目也是可以继续开发的,但是我们会发现一些问题,比如:

  • router 中的 router.js 路由文件越来越大,十分复杂
  • store 中管理非常多的状态,混乱度增加
  • pages 主要业务文件夹中,页面列表越来越长,业务内聚力差

在业务成长一定程度后,基调必定会确定下来,通过分析思考,我们可以在业务里提取出模块。例如:

1
2
3
4
5
6
7
8
9
10
├── App.vue
├── assets
├── main.js
├── modules # 原pages
│   ├── Business
│   ├── Device
│   ├── Search
│ └── ...
├── router
└── store

在此基础上,将 router 和 store 的配置,下放到每一个 module 去。这样主目录下的 router 和 store 都会变得比较简单。而在每个 module 中,router 和 store 都更密切的关联该模块。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
├── App.vue
├── assets
├── main.js
├── modules # 原pages
│ ├── Business
│ │ ├── pages
│ │ ├── index.js
│ │ ├── router.js
│ │ └── store.js
│ ├── Device
│ ├── Search
│ └── ...
├── router
└── store

视图、业务逻辑分离

Vue 快速开发的特色之一,就是在单一的一个 vue 文件中,可以写入 template、js 和 css,开发者不需要切换多个页面。但是在大型的表单类页面中,需求上设计的就非常复杂,不考虑各种优化的情况下,页面代码量可能达到 1000 行以上,在这种页面中进行修改和增加新的代码极容易出现问题。

当然,解决方案之一是组件化,但是对于表单来说,整个表单就是多个组件组合而成,而且本身表单组件也复杂,所以组件化只能解决部分问题。

这里我提出的方案,灵感来自于 AngularJS 的文件结构和 Vue Router 中的mapActions方法:

1
2
3
user
├── index.vue
└── index.service.js

将部分复杂的逻辑,抽取到index.service.js中,如何引入到index.vue中呢?

非常简单:

File: index.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<template>
<!-- ... -->
</tempate>
<script>
import mapServices from '@/utils/service.js'
import service from './index.service.js'

export default {
created() {
this.fetchData() // 直接调用map进来的方法
},
methods: {
...mapServices(service)([
'login',
'logout'
'register',
'fetchData'
])
}
}
</script>

File: index.service.js

1
2
3
4
5
6
7
8
9
10
11
function login() {}
function logout() {}
function register() {}
function fetchData() {}

export default {
login,
logout,
register,
fetchData,
};

File: @/utils/service.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
export default function mapServices(services) {
if (!services) {
return () => {};
}

return (props) => {
if (!Array.isArray(props)) {
throw new Error("mapServices only accept an Array");
}

let funcs = {};
props.forEach((propName) => {
services[propName] && (funcs[propName] = services[propName]);
});
return funcs;
};
}

这种方式即可将逻辑分散到其他文件中,并在 vue 主文件中,也不失关联性的,能够自然开发。