Small front end tangled --Vue project code organization and style conventions

Original Address

风格约定But not limited to 代码风格, there are other 默认约定.

Code organization and stratification

Code organization is a benevolent see benevolence and the wise see wisdom of the topic, there is no silver bullet . But no matter how change, guidelines or constant high cohesion and low coupling .

Strongly recommended two articles to broaden your horizons, take you to new heights.

Manage complexity with the front end of projects Feature First

Code organization Elegant, modular to be able to do a good job.

Stratified

According to the different functions of different dimensions hierarchy division after division level, further module division (in principle, each folder is a module)

Folder and file names

Choose their own style.

  1. Folders and files using the kebab-case

    kebab-case heavy user may select this.

  2. Folders using Kebab-Case , document use Pascal Case

    This is the recommended.

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

    • index files are not bound by the above
    • Tool automatically generates a file (consider yourself bound)

The modular principle

Modular code first to do hierarchical code, isolation, abstract.

Different modules perform different functions, coordination between the different functions of each other.

  1. Each module maintains an inlet and outlet

    For external module, the inlet try to ensure a

    For internal sub-module, the outlet try to ensure a

    If you follow the folder as a module boundaries, each folder has a lower outlet (may default index file)

  2. Entrance default index name of the module or folder name

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

    Internal module can also have base, common, components, helper, utils, filter, configand other levels (Ciqiong .....)

importAnd exportprinciples

  1. importImport, assigned to the file

    Specify the file to be able to increase the speed of compiler package

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

commonModule

Independent article explains.

routerModule

routerModule style conventions.

Modular structure

router
├── helper
│   ├── ImportRoute.ts
│   └── RouteGenerator.ts
├── modules
│   ├── AboutRoutes.ts
│   └── HomeRoutes.ts
├── router.ts
└── Routes.ts
复制代码
  • helper: Help tool methods

  • modules: different business modules

  • router: vue-router initialization place, but also the module inlet

  • Routes: RouteConfigoutlet, the other modules are available here routearranged, so as to achieve the purpose of decoupling, in particular different viewsinside of routing jumps, using Routesthe configuration to achieve the purpose of decoupling.

    example:

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

modulesSub-module

File name: viewsfolder name (module name) + the end of Routes

the modules file, folder views, and preferably one correspondence, and easy maintenance (a higher understanding segmentation module)

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

modulesCorrespondence is

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

Export Module Configuration:

// HomeRoutes.ts
export const HomeRoute = {
  path: '/',
  name: 'HomeRoute',
  component: 'Home',
};

// 必须导出一个数组,因为是一个模块的配置信息,可能有多个配置,还可以进行配置层级关系
export default [HomeRoute];
复制代码

Note: This is just a thought, the specific operation but also flexibility.

RouteConfigStyle Conventions

  1. RouteConfigvariable name

    vueRoute the end of the file name +

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

    And RouteConfigvariable names consistent

  3. componentAttributes

    If asynchronous loading , componentneed to use with respect to viewsthe pathformat, because in the ImportRouteprocess of unification.

    // 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. pathAttributes

    There is no best practice, it is best to use restfulstyle constraints

    If route.querythe parameter passed the like, breadcrumbs difficult to handle.

  5. metaAttributes

    Not best practice, there are so few properties in most cases

    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. propsAttributes

    Routing component parameter passing , more advanced usage, see the examples

    Use propsmanner componentsand $routedecoupled, so componentsmay be used alone or as a sub-assembly for use , and easy to test.

    Special scene may not be used props, for example, have never been common components, the components need to be used in combination Sons of, and can be routecoupled.

    If propsis set true, route.paramsit will be set to the component properties.

    // 函数式(动态)
    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 } 
        }]
    })
    复制代码

storeModule

vueApplication of module status

Modular structure

Refer to the official file structure

Official cart example

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

modules sub-module

File name: Module name + ending Store

Business store in modules, corresponding to the best folder and files under views, easy maintenance

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

Store modulePromise

Reference Store Module

The main convention moduleinternal code structure is as follows:

SystemStoreFor example:

  • You need to use the prefix where using the module name as a prefix

    Example the module name isSystem

Generally agreed statement following sequence : In addition some of the other state are optional

  • state declaration (for systemStateexample: )
  • getter types statement (optional, flexible use)
  • getters statement (optional, flexible use)
  • mutation types statement (optional)
  • mutations statement (optional)
  • action types statement (optional)
  • actions Statement (optional)
  • store options Export
// 例子: 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;
复制代码

Note : where the action types, mutation types and getter types are not mandatory, the es6use of the official environment mapState, mapGetters, mapActions, mapMutationsutility functions more convenient.

apiModule

apiModule is the interface service layer, mainly to do some conversion processing parameters, as well as other business decoupling layer.

Modular structure

Referring generally to modular structure

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

service modulePromise

File name: the end of the module name + Service

Business service under the modules, and the corresponding folder views, and easy maintenance

If too many service module interfaces corresponding to a single service files, folders can be further divided.

//例子: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 prefix convention:

Query: Use GETas a prefix (except in special circumstances)

Update: UseUPDATE

New: UseADD

Delete: UseDELETE

Other: EXPORT, IMPORT, UPLOAD, DOWNLOAD, etc.

service function name prefix convention:

  • For a single entity may consider using get, add, update, delete prefixed

i18nModule

The main recommendations are divided under the agreed code.

Modular structure

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

Language file organization

Organization language modules, according to the main module (folder hierarchy) to organize, the minimum can reduce the likelihood of conflict.

Language file conventions:

  • Dialect file in localsfolder
  • File dialect coded name

Examples of hierarchical organization:

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
}
复制代码

mockModule

mock data simulation module of work to do

Modular structure

Referring generally to the structure of the sub-modules

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

mock modulePromise

File Name: ending mock module name +

mock files in modules, and apithe module corresponding to the document, and easy maintenance

Methods conventions:

  • mockA method and a module servicerequest method corresponding to

    For example: LoginServiceThe signInmock module corresponds also calledsignIn

  • mock moduleYou need to provide a setupmethod

    Uniform external switch for call setup method, as a mock module

example:

// 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();
} 
复制代码

Other modules

Other modules such as filters, directives, testThere is no summary, after supplement.

Focus on micro-channel public number, find more exciting content

Reproduced in: https: //juejin.im/post/5d00bc9551882544d33ba037

Guess you like

Origin blog.csdn.net/weixin_34187822/article/details/93174781