前端小纠结–Vue项目代码组织和风格约定
风格约定但不限于代码风格,还有一些其他的默认约定。
代码组织和分层
代码组织是一个仁者见仁,智者见智的话题,没有银弹。不过不管怎么变化,指导思想还是不变的高内聚,低耦合。
强烈推荐两篇文章,能够拓宽你的视野,带你走向新高度。
代码组织的优雅,模块化才能够做好。
分层
按照职能的不同进行不同维度进行层级划分,层级划分之后,进行进一步的模块划分(原则上,每一个文件夹都是一个模块)
文件夹和文件命名
选择适合自己的风格。
文件夹和文件都使用kebab-case
kebab-case重度使用者可以选择这种。
文件夹使用kebab-case, 文件使用Pascal Case
建议使用这种。
1
2文件夹:event-bus
文件:EventBus.ts例外
- index文件不受上述约束
 - 工具自动生成的文件(自己考虑是否受约束)
 
模块化原则
模块化代码首先要做到代码的分层、隔离、抽象。
不同模块完成不同的职能,不同职能之间相互协作。
每个模块保持一个入口和出口
对于外部模块来说,尽量保证一个入口
对于内部子模块来说,尽量保证一个出口
如果按照文件夹作为模块界限,每个文件夹下都有一个出口(可以默认为index文件)
模块的入口名称默认index或者文件夹名字的文件
1
2
3
4
5
6// 例如: group文件夹
group/
|---index.ts // A. 默认作为入口
|---group.ts // B. 也可以默认作为入口
二者任选其一就好,A方案应该是大家默认的方案;B方案,检索代码的时候更方便
模块内部分层
模块内部还可以有
base,common,components,helper,utils,filter,config等层级(词穷了…..)
import和export原则
import导入,指定到文件指定到文件能够提高编译打包的速度
1
2// 指定到index文件
import { Logger } from './common/index';
common模块
独立文章说明。
router模块
router模块的风格约定。
模块结构
1  | router  | 
helper: 帮助工具方法
modules: 不同的业务模块
router: vue-router初始化的地方,也是模块入口
Routes:
RouteConfig的出口,其它模块都从这里获取route配置,从而达到解耦的目的,尤其是不同的views里面的路由跳转,使用Routes配置达到解耦的目的。例子:
1
2
3
4
5
6import { HomeRoute } from 'Routes';
// 跳转,这样没有硬编码任何的route信息,全部都是从Routes配置来,达到解耦的目的。
this.$router.push({
name: HomeRoute.name
})
modules子模块
文件名:
views下文件夹名(模块名) + Routes结尾modules下的文件,最好和views下文件夹一一对应,方便维护(对模块切分有较高的理解)
1  | 例如:  | 
modules对应的就是
1  | 例如:  | 
导出模块配置:
1  | // HomeRoutes.ts  | 
注意: 这只是个思路,具体的操作还要灵活运用。
RouteConfig风格约定
RouteConfig变量名vue文件名+Route结尾1
2
3
4
5
6
7// 文件Home.vue
// 变量名HomeRoute
export const HomeRoute = {
path: '/',
name: 'HomeRoute',
component: 'Home',
};
name属性和
RouteConfig变量名保持一致component属性如果异步加载,
component需要使用相对于views的path格式,因为在ImportRoute中统一处理。1
2
3
4
5// ImportRoute.ts统一处理
export function importRoute(file: string) {
// @see https://github.com/webpack/webpack/issues/1949
return () => import(/* webpackChunkName: "chunk-[request][index]" */ '@/views/' + file + '.vue');
}
path属性没有最佳实践,最好使用
restful风格约束如果使用
route.query等之类的参数传递,面包屑导航很难处理。meta属性没有最佳实践,多数情况下有这么几个属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23export const HomeRoute = {
path: '/',
name: 'HomeRoute',
component: 'Home', // component: () => import('@/views/Home.vue')
meta: {
// title的值可以为`i18n`的语言文件key,方便做国际化
title: '首页', // 作为menu.title和breadcrumb.title备选
icon: '', // icon的class,作为menu.icon和breadcrumb.icon备选
menu: {
title: '首页',
visible: true,
icon: '', // icon的class
},
breadcrumb: {
title: '路径名',
visible: false, // 有的时候不需要在面包屑上渲染
icon: '', // icon的class
},
auth: {
roles: [1, 2, 3]
}
}
};
props属性使用
props方式把components和$route解耦,这样components既可以单独使用,也可以当作子组件使用,而且方便测试。特殊场景可以不使用
props,例如本来就不是通用的组件,是需要组合在一起使用的父子组件,是可以和route耦合的。如果
props被设置为true,route.params将会被设置为组件属性。1
2
3
4
5
6
7
8
9// 函数式(动态)
const router = new VueRouter({
routes: [{
path: '/search',
component: SearchUser,
// route是SearchUser内部的this.$route
props: (route) => ({ query: route.query.q })
}]
})1
2
3
4
5
6
7
8// 静态
const router = new VueRouter({
routes: [{
path: '/promotion/from-newsletter',
component: Promotion,
props: { newsletterPopup: false }
}]
})
store模块
vue应用的状态模块
模块结构
1  | store  | 
index: 作为入口StoreTypes作为类型的常量文件所以mutation和action的类型全部使用常量,方便其它模块和store模块的解耦。
modules子模块
文件名:模块名 + Store结尾
modules下的业务store,最好和views下文件夹对应,方便维护
1  | # 例子:  | 
Store module约定
主要约定
module内部代码结构大致如下:
SystemStore为例:
需要使用前缀的地方使用模块名作为前缀
例子中模块名为
System
约定声明大致顺序如下:除state部分外其他都是可选
- state声明(例如:
systemState) - getter types声明(可选,灵活运用)
 - getters声明(可选, 灵活运用)
 - mutation types 声明(可选)
 - mutations声明(可选)
 - action types声明(可选)
 - actions声明(可选)
 - store options导出
 
1  | // 例子: SystemStore.ts  | 
注意:其中的action types, mutation types和  getter types 不强制要求,在es6环境中使用官方的mapState,mapGetters, mapActions, mapMutations工具函数更方便。
api模块
api模块是接口服务层,主要做一些对参数的转换处理,同时解耦其它业务层。
模块结构
模块的大致参考结构
1  | api  | 
service module约定
文件名:模块名 + Service结尾
modules下的业务service,和views下文件夹对应,方便维护
如果单个service文件对应的业务模块接口太多,可以使用文件夹来进一步分割。
1  | //例子:LoginService  | 
URL前缀约定:
查询:使用
GET作为前缀(特殊情况例外)更新:使用
UPDATE新增:使用
ADD删除:使用
DELETE其它:EXPORT,IMPORT,UPLOAD,DOWNLOAD等
service 函数名前缀约定:
- 对于单个实体可以考虑使用get, add, update, delete作为前缀
 
i18n模块
主要是建议下代码的分割的约定。
模块结构
1  | i18n  | 
语言文件组织
语言模块的组织,主要按照模块(文件夹层次)来组织,能够最小的减少冲突可能性。
语言文件约定:
- 方言文件放置在
locals文件夹下 - 文件以方言编码命名
 
层级组织的例子:
1  | views  | 
1  | // views/feedback/locales/zh_CN.js  | 
1  | // views/locales/zh_CN.js  | 
1  | // i18n/locales/zh_CN.js  | 
mock模块
mock模块主要做些数据模拟的工作
模块结构
子模块的大致参考结构
1  | mock  | 
mock module约定
文件名:模块名 + mock结尾
modules下的mock文件,和
api模块下文件对应,方便维护
方法约定:
mock模块方法和service请求方法对应例如:
LoginService的signInmock模块对应也叫signInmock module需要提供setup方法setup方法供外部统一调用,作为mock module的开关
例子:
1  | // LoginMock.js  | 
1  | // mock.js  | 
1  | // main.js  | 
其它模块
其它模块如
filters,directives,test目前没有总结,以后补充。