前端小纠结–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
的signIn
mock模块对应也叫signIn
mock module
需要提供setup
方法setup方法供外部统一调用,作为mock module的开关
例子:
1 | // LoginMock.js |
1 | // mock.js |
1 | // main.js |
其它模块
其它模块如
filters
,directives
,test
目前没有总结,以后补充。