日更笔记

Vue项目代码组织和风格约定

2019-06-12  本文已影响0人  MrZac_

代码组织和分层

代码组织是一个仁者见仁,智者见智的话题,没有银弹。不过不管怎么变化,指导思想还是不变的高内聚,低耦合

强烈推荐两篇文章,能够拓宽你的视野,带你走向新高度。

用 Feature First 的方式管理前端项目复杂度

代码组织的优雅,模块化才能够做好。

分层

按照职能的不同进行不同维度进行层级划分,层级划分之后,进行进一步的模块划分(原则上,每一个文件夹都是一个模块)

文件夹和文件命名

选择适合自己的风格。

文件夹和文件都使用kebab-case

kebab-case重度使用者可以选择这种。

文件夹使用kebab-case, 文件使用Pascal Case

建议使用这种。

文件夹:event-bus文件:EventBus.ts复制代码

例外

index文件不受上述约束

工具自动生成的文件(自己考虑是否受约束)

模块化原则

模块化代码首先要做到代码的分层、隔离、抽象。

不同模块完成不同的职能,不同职能之间相互协作。

每个模块保持一个入口和出口

对于外部模块来说,尽量保证一个入口

对于内部子模块来说,尽量保证一个出口

如果按照文件夹作为模块界限,每个文件夹下都有一个出口(可以默认为index文件)

模块的入口名称默认index或者文件夹名字的文件

// 例如: group文件夹group/|---index.ts  // A. 默认作为入口|---group.ts  // B. 也可以默认作为入口二者任选其一就好,A方案应该是大家默认的方案;B方案,检索代码的时候更方便复制代码

模块内部分层

模块内部还可以有base,common,components,helper,utils,filter,config等层级(词穷了.....)

import和export原则

import导入,指定到文件

指定到文件能够提高编译打包的速度

// 指定到index文件import{ Logger }from'./common/index';复制代码

common模块

独立文章说明。

router模块

router模块的风格约定。

模块结构

router├── helper│   ├── ImportRoute.ts│   └── RouteGenerator.ts├── modules│   ├── AboutRoutes.ts│   └── HomeRoutes.ts├── router.ts└── Routes.ts复制代码

helper: 帮助工具方法

modules: 不同的业务模块

router: vue-router初始化的地方,也是模块入口

Routes:RouteConfig的出口,其它模块都从这里获取route配置,从而达到解耦的目的,尤其是不同的views里面的路由跳转,使用Routes配置达到解耦的目的。

例子:

import{ HomeRoute }from'Routes';// 跳转,这样没有硬编码任何的route信息,全部都是从Routes配置来,达到解耦的目的。this.$router.push({name: HomeRoute.name})复制代码

modules子模块

文件名:views下文件夹名(模块名) + Routes结尾

modules下的文件,最好和views下文件夹一一对应,方便维护(对模块切分有较高的理解)

例如:views├── group│   ├── xxx1.vue│   └── xxx2.vue├── report│   ├── xxx3.vue│   └── xxx4.vue├── Home.vue复制代码

modules对应的就是

例如:router├── modules│   ├── GroupRoutes.ts|  ├── ReportRoutes.ts│   └── HomeRoutes.ts复制代码

导出模块配置:

// HomeRoutes.tsexportconstHomeRoute = {  path:'/',  name:'HomeRoute',  component:'Home',};// 必须导出一个数组,因为是一个模块的配置信息,可能有多个配置,还可以进行配置层级关系exportdefault[HomeRoute];复制代码

注意:这只是个思路,具体的操作还要灵活运用。

RouteConfig风格约定

RouteConfig变量名

vue文件名+Route结尾

// 文件Home.vue// 变量名HomeRouteexportconstHomeRoute = {  path:'/',  name:'HomeRoute',  component:'Home',};复制代码

name属性

和RouteConfig变量名保持一致

component属性

如果异步加载,component需要使用相对于views的path格式,因为在ImportRoute中统一处理。

// ImportRoute.ts统一处理exportfunctionimportRoute(file:string){// @see https://github.com/webpack/webpack/issues/1949return()=>import(/* webpackChunkName: "chunk-[request][index]" */'@/views/'+ file +'.vue');}复制代码

path属性

没有最佳实践,最好使用restful风格约束

如果使用route.query等之类的参数传递,面包屑导航很难处理。

meta属性

没有最佳实践,多数情况下有这么几个属性

exportconstHomeRoute = {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将会被设置为组件属性。

// 函数式(动态)constrouter =newVueRouter({routes: [{path:'/search',component: SearchUser,// route是SearchUser内部的this.$routeprops:(route) =>({query: route.query.q })    }]})复制代码

// 静态constrouter =newVueRouter({routes: [{path:'/promotion/from-newsletter',component: Promotion,props: {newsletterPopup:false}    }]})复制代码

store模块

vue应用的状态模块

模块结构

参考官方文件结构

官方购物车例子

store├── StoreTypes.ts# actions mutations getters类型├── Actions.ts# 根级别的 action├── Mutations.ts# 根级别的 Mutations├── Getters.ts# 根级别的 getters├── modules# 模块│   ├── xxxStore.ts# 子模块│   ├── SystemStore.ts# SystemStore子模块├── index.ts# 入口复制代码

index: 作为入口

StoreTypes作为类型的常量文件

参考使用常量替代-mutation-事件类型

所以mutation和action的类型全部使用常量,方便其它模块和store模块的解耦。

modules子模块

文件名:模块名 + Store结尾

modules下的业务store,最好和views下文件夹对应,方便维护

# 例子: LocaleStore.ts# i18n模块LoginStore.ts# 登录模块UserStore.ts# 用户模块复制代码

Store module约定

参考Store Module

主要约定module内部代码结构大致如下:

SystemStore为例:

需要使用前缀的地方使用模块名作为前缀

例子中模块名为System

约定声明大致顺序如下:除state部分外其他都是可选

state声明(例如:systemState)

getter types声明(可选,灵活运用)

getters声明(可选, 灵活运用)

mutation types 声明(可选)

mutations声明(可选)

action types声明(可选)

actions声明(可选)

store options导出

注意:其中的action types, mutation types和  getter types 不强制要求,在es6环境中使用官方的mapState,mapGetters,mapActions,mapMutations工具函数更方便。

api模块

api模块是接口服务层,主要做一些对参数的转换处理,同时解耦其它业务层。

模块结构

模块的大致参考结构

api├── api.ts# 入口└── modules# 子模块├── DictionaryService.ts# 具体的业务模块├── GroupService.ts    ├── HistoryService.ts复制代码

service module约定

文件名:模块名 + Service结尾

modules下的业务service,和views下文件夹对应,方便维护

如果单个service文件对应的业务模块接口太多,可以使用文件夹来进一步分割。

//例子:LoginService// 声明url,导出url方便mock模块使用exportconstGET_SIGN_IN ='/api/login';// 声明service函数exportfunctionsignIn(data: {account:string; pass:string}){returnajax({      url: GET_SIGN_IN,      data,      method:'post'});}复制代码

URL前缀约定:

查询:使用GET作为前缀(特殊情况例外)

更新:使用UPDATE

新增:使用ADD

删除:使用DELETE

其它:EXPORT,IMPORT,UPLOAD,DOWNLOAD等

service 函数名前缀约定:

对于单个实体可以考虑使用get, add, update, delete作为前缀

i18n模块

主要是建议下代码的分割的约定。

模块结构

i18n├── i18n.ts├── index.d.ts└── locales    ├── en_US.js    ├── modules    │   └── actions    │      ├── en_US.js    │      └── zh_CN.js    └── zh_CN.js复制代码

语言文件组织

语言模块的组织,主要按照模块(文件夹层次)来组织,能够最小的减少冲突可能性。

语言文件约定:

方言文件放置在locals文件夹下

文件以方言编码命名

层级组织的例子:

views├── account│   ├── Account.vue│   ├── locales│   │   ├── en_US.js│   │   └── zh_CN.js│   └── XXX.vue├── feedback│   ├── locales│   │   ├── en_US.js│   │   └── zh_CN.js│   └── Suggestion.vue├── locales│   │   ├── en_US.js│   │   └── zh_CN.js复制代码

// views/feedback/locales/zh_CN.jsexportconstfeedbackModule = {// 语言模块属性:模块名+module结尾// 可选的方式就是:严格按照文件夹层次来构造属性层级(缺点取属性值时太长,后期可以使用__dirname自动生成)label:'您的意见:',textarea: {placeholder:'请写下您的意见与建议, 500字符以内'}  };复制代码

// views/locales/zh_CN.jsimportfeedbackModulefrom'../feedback/locales/zh_CN.js';exportdefault{// 如果模块众多建议加上views  // views: {//    ...feedbackModule,// }// 如果模块少,直接feedbackModule,};复制代码

// i18n/locales/zh_CN.jsimportviewsLocalesfrom'../../views/locales/zh_CN';exportdefault{    ...viewsLocales}复制代码

mock模块

mock模块主要做些数据模拟的工作

模块结构

子模块的大致参考结构

mock├── mock.js# 入口└── modules# 子模块├── LoginMock.js# 具体的业务模块复制代码

mock module约定

文件名:模块名 + mock结尾

modules下的mock文件,和api模块下文件对应,方便维护

方法约定:

mock模块方法和service请求方法对应

例如:LoginService的signInmock模块对应也叫signIn

mock module需要提供setup方法

setup方法供外部统一调用,作为mock module的开关

例子:

// LoginMock.jsimportMockfrom'mockjs';import{ genSuccessResult, genFailResult }from'./mock-utils';import{ GET_SIGN_IN }from'../api/api';importCookiesfrom'js-cookie';constsignIn =(data) =>{    Cookies.set('SESSIONID','mock SESSIONID');returngenSuccessResult({msg:'登陆成功',    });}exportdefault{  setup() {      Mock.mock(GET_SIGN_IN,'post', signIn);  }}复制代码

// mock.jsimportloginMockerfrom'./modules/login';exportconststart =function(){  loginMocker.setup();};复制代码

// main.jsimportmockerfrom'./mock/mock'if(process.env.NODE_ENV ==='development') {  mocker.start();}复制代码

其它模块

其它模块如filters,directives,test目前没有总结,以后补充。

上一篇下一篇

猜你喜欢

热点阅读