前端小糾結--Vue項目代碼組織和風格約定

原文地址javascript

風格約定但不限於代碼風格,還有一些其餘的默認約定html

代碼組織和分層

代碼組織是一個仁者見仁,智者見智的話題,沒有銀彈。不過無論怎麼變化,指導思想仍是不變的高內聚,低耦合前端

強烈推薦兩篇文章,可以拓寬你的視野,帶你走向新高度。vue

用 Feature First 的方式管理前端項目複雜度java

代碼組織的優雅,模塊化纔可以作好。webpack

分層

按照職能的不一樣進行不一樣維度進行層級劃分,層級劃分以後,進行進一步的模塊劃分(原則上,每個文件夾都是一個模塊)git

文件夾和文件命名

選擇適合本身的風格。es6

  1. 文件夾和文件都使用kebab-casegithub

    kebab-case重度使用者能夠選擇這種。web

  2. 文件夾使用kebab-case, 文件使用Pascal Case

    建議使用這種。

    文件夾:event-bus
    文件:EventBus.ts
    複製代碼
  3. 例外

    • index文件不受上述約束
    • 工具自動生成的文件(本身考慮是否受約束)

模塊化原則

模塊化代碼首先要作到代碼的分層、隔離、抽象。

不一樣模塊完成不一樣的職能,不一樣職能之間相互協做。

  1. 每一個模塊保持一個入口和出口

    對於外部模塊來講,儘可能保證一個入口

    對於內部子模塊來講,儘可能保證一個出口

    若是按照文件夾做爲模塊界限,每一個文件夾下都有一個出口(能夠默認爲index文件)

  2. 模塊的入口名稱默認index或者文件夾名字的文件

    // 例如: group文件夾
    group/
    |---index.ts   // A. 默認做爲入口
    |---group.ts   // B. 也能夠默認做爲入口
    
    兩者任選其一就好,A方案應該是你們默認的方案;B方案,檢索代碼的時候更方便
    複製代碼
  3. 模塊內部分層

    模塊內部還能夠有base, common, components, helper,utils,filter,config等層級(詞窮了.....)

importexport原則

  1. 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風格約定

  1. RouteConfig變量名

    vue文件名+Route結尾

    // 文件Home.vue
    // 變量名HomeRoute
    export const HomeRoute = {
      path: '/',
      name: 'HomeRoute',
      component: 'Home',
    };
    複製代碼
  2. name屬性

    RouteConfig變量名保持一致

  3. component屬性

    若是異步加載component須要使用相對於viewspath格式,由於在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');
    }
    複製代碼
  4. path屬性

    沒有最佳實踐,最好使用restful風格約束

    若是使用route.query等之類的參數傳遞,麪包屑導航很難處理。

  5. 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]
          }
      }
    };
    複製代碼
  6. props屬性

    路由組件傳參,更多高級用法,請查看例子

    使用props方式把components$route解耦,這樣components既能夠單獨使用,也能夠看成子組件使用,並且方便測試。

    特殊場景能夠不使用props,例如原本就不是通用的組件,是須要組合在一塊兒使用的父子組件,是能夠和route耦合的。

    若是 props 被設置爲 trueroute.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            # 入口
複製代碼

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導出
// 例子: 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環境中使用官方的mapStatemapGetters, 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 函數名前綴約定:

  • 對於單個實體能夠考慮使用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.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請求方法對應

    例如:LoginServicesignIn 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();
} 
複製代碼

項目結構(2018-08-12更新)

領域驅動設計在前端中的應用用 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
複製代碼

其它模塊

其它模塊如filtersdirectives,test目前沒有總結,之後補充。

歡迎加入羣聊

若是入羣失敗,添加我的微信,拉你入羣,驗證消息:前端交流

關注微信公衆號,發現更多精彩內容

微信公衆號
相關文章
相關標籤/搜索