原文地址javascript
風格約定
但不限於代碼風格
,還有一些其餘的默認約定
。html
代碼組織是一個仁者見仁,智者見智的話題,沒有銀彈。不過無論怎麼變化,指導思想仍是不變的高內聚,低耦合。前端
強烈推薦兩篇文章,可以拓寬你的視野,帶你走向新高度。vue
代碼組織的優雅,模塊化纔可以作好。webpack
按照職能的不一樣進行不一樣維度進行層級劃分,層級劃分以後,進行進一步的模塊劃分(原則上,每個文件夾都是一個模塊)git
選擇適合本身的風格。es6
文件夾和文件都使用kebab-casegithub
kebab-case重度使用者能夠選擇這種。web
文件夾使用kebab-case, 文件使用Pascal Case
建議使用這種。
文件夾:event-bus
文件:EventBus.ts
複製代碼
例外
模塊化代碼首先要作到代碼的分層、隔離、抽象。
不一樣模塊完成不一樣的職能,不一樣職能之間相互協做。
每一個模塊保持一個入口和出口
對於外部模塊來講,儘可能保證一個入口
對於內部子模塊來講,儘可能保證一個出口
若是按照文件夾做爲模塊界限,每一個文件夾下都有一個出口(能夠默認爲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.ts
export const HomeRoute = {
path: '/',
name: 'HomeRoute',
component: 'Home',
};
// 必須導出一個數組,由於是一個模塊的配置信息,可能有多個配置,還能夠進行配置層級關係
export default [HomeRoute];
複製代碼
注意: 這只是個思路,具體的操做還要靈活運用。
RouteConfig
風格約定RouteConfig
變量名
vue
文件名+Route結尾
// 文件Home.vue
// 變量名HomeRoute
export const HomeRoute = {
path: '/',
name: 'HomeRoute',
component: 'Home',
};
複製代碼
name
屬性
和
RouteConfig
變量名保持一致
component
屬性
若是異步加載,
component
須要使用相對於views
的path
格式,由於在ImportRoute
中統一處理。
// 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
屬性
沒有最佳實踐,多數狀況下有這麼幾個屬性
export 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
將會被設置爲組件屬性。
// 函數式(動態)
const router = new VueRouter({
routes: [{
path: '/search',
component: SearchUser,
// route是SearchUser內部的this.$route
props: (route) => ({ query: route.query.q })
}]
})
複製代碼
// 靜態
const router = new VueRouter({
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和action的類型所有使用常量,方便其它模塊和store模塊的解耦。
文件名:模塊名 + Store結尾
modules下的業務store,最好和views下文件夾對應,方便維護
# 例子:
LocaleStore.ts # i18n模塊
LoginStore.ts # 登陸模塊
UserStore.ts # 用戶模塊
複製代碼
Store module
約定主要約定
module
內部代碼結構大體以下:
SystemStore
爲例:
須要使用前綴的地方使用模塊名做爲前綴
例子中模塊名爲
System
約定聲明大體順序以下:除state部分外其餘都是可選
systemState
)// 例子: SystemStore.ts
// 聲明state
const systemState: SystemState = {
initialized: false,
};
// 聲明GetterTypes
export const SystemGetterTypes = {
IS_SYSTEM_INITIALIZED: 'IS_SYSTEM_INITIALIZED',
};
// 聲明getters
const getters = {
[SystemGetterTypes.IS_SYSTEM_INITIALIZED](state: SystemState, getters: any, rootState: any){
return state.initialized;
}
};
// 聲明MutationTypes
export const SystemMutationTypes = {
SYSTEM_SET_INITIALIZED: 'SYSTEM_SET_INITIALIZED',
};
// 聲明mutations
const mutations: MutationTree<SystemState> = {
[SystemMutationTypes.SYSTEM_SET_INITIALIZED]: (
state: SystemState,
payload: boolean
) => {
state.initialized = payload;
logger.log('system-store.initialized: ' + payload);
},
};
// 聲明ActionTypes
export const SystemActionTypes = {
SYSTEM_UPDATE_INITIALIZED: 'SYSTEM_UPDATE_INITIALIZED',
SYSTEM_RESET: 'SYSTEM_RESET',
SYSTEM_INIT: 'SYSTEM_INIT',
};
// 聲明actions
const actions = {
[SystemActionTypes.SYSTEM_UPDATE_INITIALIZED]: (
{ commit }: ActionContext<SystemState, any>,
initialized: boolean
) => {
commit(SystemMutationTypes.SYSTEM_SET_INITIALIZED, initialized);
},
[SystemActionTypes.SYSTEM_RESET]: ({
commit,
dispatch,
}: ActionContext<SystemState, any>) => {
// 清空全部使用store儲存的數據.
},
[SystemActionTypes.SYSTEM_INIT]: (
{ commit, dispatch }: ActionContext<SystemState, any>,
payload: {
user: UserModel;
userCookie: UserCookie;
}
) => {
// 初始化數據
},
};
// 導出storeOptions
const storeOptions = {
state: systemState,
getters,
mutations,
actions,
};
export default storeOptions;
複製代碼
注意:其中的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模塊使用
export const GET_SIGN_IN = '/api/login';
// 聲明service函數
export function signIn(data: {account: string; pass: string}) {
return ajax({
url: GET_SIGN_IN,
data,
method: 'post'
});
}
複製代碼
URL前綴約定:
查詢:使用
GET
做爲前綴(特殊狀況例外)更新:使用
UPDATE
新增:使用
ADD
刪除:使用
DELETE
其它:EXPORT,IMPORT,UPLOAD,DOWNLOAD等
service 函數名前綴約定:
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.js
export const feedbackModule = {
// 語言模塊屬性:模塊名+module結尾
// 可選的方式就是:嚴格按照文件夾層次來構造屬性層級(缺點取屬性值時太長,後期可使用__dirname自動生成)
label: '您的意見:',
textarea: {
placeholder: '請寫下您的意見與建議, 500字符之內'
}
};
複製代碼
// views/locales/zh_CN.js
import feedbackModule from '../feedback/locales/zh_CN.js';
export default {
// 若是模塊衆多建議加上views
// views: {
// ...feedbackModule,
// }
// 若是模塊少,直接
feedbackModule,
};
複製代碼
// i18n/locales/zh_CN.js
import viewsLocales from '../../views/locales/zh_CN';
export default {
...viewsLocales
}
複製代碼
mock
模塊mock模塊主要作些數據模擬的工做
子模塊的大體參考結構
mock
├── mock.js # 入口
└── modules # 子模塊
├── LoginMock.js # 具體的業務模塊
複製代碼
mock module
約定文件名:模塊名 + mock結尾
modules下的mock文件,和
api
模塊下文件對應,方便維護
方法約定:
mock
模塊方法和service
請求方法對應
例如:
LoginService
的signIn
mock模塊對應也叫signIn
mock module
須要提供setup
方法
setup方法供外部統一調用,做爲mock module的開關
例子:
// LoginMock.js
import Mock from 'mockjs';
import { genSuccessResult, genFailResult } from './mock-utils';
import { GET_SIGN_IN } from '../api/api';
import Cookies from 'js-cookie';
const signIn = (data) => {
Cookies.set('SESSIONID', 'mock SESSIONID');
return genSuccessResult({
msg: '登錄成功',
});
}
export default {
setup() {
Mock.mock(GET_SIGN_IN, 'post', signIn);
}
}
複製代碼
// mock.js
import loginMocker from './modules/login';
export const start = function() {
loginMocker.setup();
};
複製代碼
// main.js
import mocker from './mock/mock'
if (process.env.NODE_ENV === 'development') {
mocker.start();
}
複製代碼
領域驅動設計在前端中的應用和用 Feature First 的方式管理前端項目複雜度 基於上面的文章從新設計項目結構
fe-domain
└── src
├── api # 全局api,能夠隔離模塊內部api和外部api
│ ├── api.js
│ └── modules
│ └── UserPublicApi.js
├── App.vue
├── common # 公共模塊
│ └── http.js
├── components # 組件
│ ├── Nav.vue
│ └── Table.vue
├── main.js
├── views # 模塊
│ └── user
│ ├── api # user 模塊api層,public + private api都在這裏集中
│ │ └── UserApi.js
│ ├── common
│ │ └── utils.js
│ ├── components
│ │ ├── Avatar.vue
│ │ └── List.vue
│ ├── modules # 子模塊 , 能夠依賴上層資源(user和project級別)
│ │ └── profile
│ │ └── views
│ │ └── Profile.vue
│ ├── service
│ │ └── UserService.js
│ ├── transformer
│ │ └── UserTransformer.js
│ └── views
│ └── User.vue
└── others
複製代碼
其它模塊如
filters
,directives
,test
目前沒有總結,之後補充。
歡迎加入羣聊
若是入羣失敗,添加我的微信,拉你入羣,驗證消息:前端交流
關注微信公衆號,發現更多精彩內容