前端开发规范

151 阅读8分钟

项目规范

项目目录结构

// 项目根目录
sx-ts-template
|--src

        |--assets                                                    // 资源文件配置目录
                |--icons                                            // 图标资源目录 (存放 icon & iconfont 资源文件)
                        |-- iconfont.js                     // 原则上一个项目只有一个iconfont资源文件
                |--images                                            // 图片资源目录,存放项目使用的所有图片资源
                |--ssss                                                // scss全局配置目录
                        |--abstracts                            // 存放全局 mixins & variables & placeholder & functions 声明
                        |--common                                    // 基础样式 (全局样式重置)
               |--components                            // 存放全局组件相关样式 (例: 多个页面用到相同样式或组件抽取样式写成独立组件样式文件)
               |--animation                            // 存放全局动画样式
               |--vendor                                    // 存放第三方库的样式导入

        |--config                                                    // 配置文件存放目录
            |--config.ts                                    // 后台请求连接地址配置文件 (具体链接地址通过 .env 传入)
        |--const.ts                                        // 全局常量文件
        |--bus.ts                                            // 事件总线 (vue版本)
        |--elementConfig.ts                        // element-ui 按需导入文件
        |--iviewConfig.ts                            // iview 按需导入文件
        |--http.ts                                        // axios 实例配置文件
        |--vuexConfig.ts                            // vuex plugin 配置文件

        |--router                                                    // 路由配置
            |--index.ts                                        // vue-router 实例配置文件
            |--routes.ts                                    // 路由配置文件

        |--service                                                // 请求接口管理
            |--api.ts                                            // 请求接口声明文件

        |--store                                                    // vuex管理 (文件夹 & 文件 命名:小写肉串格式)
            |--modules                                        // vuex modules文件存放目录
        |--index.ts                                        // vuex统一配置文件(modules在这里统一引入)

        |--ts                                                            // ts 配置 & 声明目录
            |--declare                                        // 全局类型声明配置目录 (包含 *.d.ts 文件)
        |--type.ts                                        // 数据结构类型声明文件

        |--utils                                                    // 工具函数管理
            |--index.js                                        // 工具函数统一导出接口文件

        |--layouts                                                // 布局组件目录 (布局命名: 首字母大写驼峰格式)
        |--Index.vue                                    // 默认布局 (路由组件默认显示在该布局上)
        |--Login.vue                                    // 登陆布局

        |--views                                                    // 路由组件目录
                |--FloorConfig.vue                        // 路由组件 (命名: 首字母大写驼峰格式)

        |--components                                            // 路由组件对应的子组件存放目录 (文件夹 & 子组件 命名:小写肉串格式)
                |--ui                                                 // 存放项目中必要的 ui 基础组件
                |--common                                         // 存放项目中共用的逻辑组件
                |--floor-config                                // 路由组件对应子组件文件夹
                        |-- area-detail.vue             // 子组件

|--.env.development                                        // 开发环境变量声明文件
|--.env.production                                        // 生产环境变量声明文件
|--.env.test                                                    // 测试环境变量声明文件

路由配置

  • 路由组件配置统一配置在 @/src/router/routes.ts
  • 使用异步组件方式引入路由组件并配置 webpackChunkName (路由引入变量 = 路由组件名称 = webpackChunkName)
const FloorConfig = () => import(/* webpackChunkName: "FloorConfig" */ 'views/FloorConfig.vue')
  • 二级页面 通过 动态路由匹配 & 嵌套路由 来构建 (例:一级分类列表页 & 二级分类列表页)
    • 路由组件通过设置 来嵌套子组件
    • 嵌套子组件存放在 components 中对应逻辑组件文件夹中
    • 路由组件 & 嵌套子组件共用相同的 webpackChunkName
    • 嵌套子路由引入变量名带上路由组件名称并 首字母大写驼峰式
const FlowType = () =>
  import(/* webpackChunkName: "FlowType" */ 'views/FlowType.vue')
const FlowTypeList = () =>
  import(/* webpackChunkName: "FlowType" */ 'components/flow-type/list.vue')
const FlowTypeDetail = () =>
  import(/* webpackChunkName: "FlowType" */ 'components/flow-type/detail.vue')
  • Layout 布局级路由组件用做根路由组件 (其余路由组件属于 Layout 的二级路由)
/****** Layout ******/
const Index = () => import(/* webpackChunkName: "index" */ 'layouts/Index.vue')
const Login = () => import(/* webpackChunkName: "login" */ 'layouts/Login.vue')

const routes: RouteConfig[] = [
  {
    path: '/',
    name: 'Index',
    component: Index,
    children: [...],
  },
  {
    path: '/login',
    name: 'Login',
    component: Login,
  },
]

Menu 配置

  • 通过构建 menu 配置来快速生成 顶部栏 & 侧边栏 & 逻辑路由配置 (通过对应的 Builder 转化 menu 配置)
// index menu 配置
export const IndexRoutes = [
  { // 无 tab 菜单项 (默认菜单)
    sides: [ // 侧边栏配置
      { // menuItem
        title: '菜单项', // menuItem title
        url: 'menu-item', // 组件路由 (必须为驼峰式: route.name 通过 url 自动生成 例: url: config-set => name: ConfigSet)
        component: Test, // 路由对应的组件
        icon: 'icon-xxx', // menuItem icon
      },
      { // subMenu
        title: 'subMenu',
        children: [ // 子菜单配置
          { // subItem
            title: '子菜单项A',
            url: 'sub-item-a',
            component: SubItemA,
            icon: 'icon-xxx',
          },
          {
            title: '子菜单项B',
            url: 'sub-item-b',
            component: SubItemB,
            icon: 'icon-xxx',
            /* 存在二级页面操作 (通过设置嵌套路由实现) */
            children: [
              { path: '', component: SubItemList }, // 默认展示该嵌套子路由
              { path: ':id', component: SubItemDetail },
            ],
          },
        ],
      },
    ],
  },
  { // 具名 tab (显示在顶部栏中)
    title: 'title',
    url: 'mall', // 一级路由: 与组件路由(sides‘url)拼成完整路由
    hide: false, // 隐藏菜单项 (若设置, 则 tab 不在顶部显示,必须手动进入才能展示对应侧边栏)
    sides: [...],
  },
    /* 外部链接菜单按钮 直接跳出 */
  {
    title: 'screen',
    url: 'http://www.baidu.com',
  },
]

const routes: RouteConfig[] = [
  {
    path: '/',
    name: 'Index',
    component: Index,
    children: [
      { path: '', redirect: { name: 'menuItem' } },
      ...routeBuilder(IndexRoutes), // 使用路由生成器自动生成路由
    ],
  },
  {
    path: '/login',
    name: 'Login',
    component: Login,
  },
]

Layout 路由组件规范

  • 所有布局级路由组件 统一放在 @/src/layouts 文件夹中
  • 所有 layout 路由组件命名 首字母大写驼峰格式 形式
  • 所有 layout 路由组件 class 名称与文件名称相同

普通路由组件

  • 所有普通路由组件 统一放在 @/src/views 文件夹中
  • 所有普通路由组件命名 首字母大写驼峰格式 形式
  • 所有普通路由组件 class 名称与文件名称相同

普通(子)组件

  • 通常一个路由组件逻辑被拆分成多个普通子组件
  • 普通组件统一使用名称形式命名并满足 小写肉串格式
  • 每个路由组件在 @/src/components 文件夹下创建同名 小写肉串格式 形式文件夹,用于存放其逻辑子组件
  • 所有普通子组件 class 名称 = <路由组件名称> + 自身文件名称 (满足 首字母大写驼峰格式 形式, 名称过长可省略路由组件名称)

环境相关配置

  • package.json 中配置不同打包脚本
"scripts": {
  "dev": "vite", // dev 模式
  "build": "vite build", //项目打包生成dist文件夹
  "deploy:test": "export UPLOAD=true && yarn build --mode test", // production 生产模式打包并部署
  "build:test": "vue-cli-service build --mode test", // test 测试模式打包并部署
  "test:unit": "vue-cli-service test:unit", // 单元测试
  "lint": "vite lint", // eslint 校验
},

  • 通过项目中 .env.[environment] 文件来配置不同模式下的部分自定义环境变量
// .env.development
# 开发环境配置
VUE_APP_HTTP_URL = 'http://39.174.88.209:10888'
VUE_APP_IMG_URL = ''

// .env.production
# 生产环境配置
VUE_APP_HTTP_URL = 'http://39.174.88.210:12488'
VUE_APP_IMG_URL = ''

// .env.test
# 测试环境配置
VUE_APP_HTTP_URL = 'http://39.174.88.209:10888'
VUE_APP_IMG_URL = ''
  • 项目中通过 process.env.[XXXX] 来引用不同模式下的自定义环境变量
// @/src/config/config.ts
export const HTTP_URL = process.env.VUE_APP_HTTP_URL
export const IMAGE_URL = process.env.VUE_APP_IMG_URL

export default {
  HTTP_URL,
  IMAGE_URL,
}

代码规范

script中JS(TS)代码规范

牢记:

  • 函数尽量不要超过15行
  • 一个函数做一件事情。
  • 并且在函数上方添加备注描述这个函数的功能,必要时解释函数的输入输出参数的意义
  • 一个组件的代码控制在400行内,实现界面使用组件化的思想
  • 遵循DRY原则,Don't repeat yourself!
  • 拒绝使用魔法值(莫名其妙出现在代码中的数字,需要定义成常量并加注释)

包导入顺序(在导入时添加注释区分下列四个模块,不要堆在一起)

  1. 第三方库或组件
  2. 本地的js模块
  3. 本地的vue组件
  4. 本地的资源文件

样例

// 第三方库与组件
import Swiper from 'swiper'
// 本地js模块
import { httpUrl, imageUrl } from "@/tools/globals"
import { login, getBrandInfoList } from "@/service/restApi"
import {
  appendImageUrl, 
  appendPrefixStaticUrl, 
  getImageSize, 
  getParameterByName } 
from "@/tools/utils"
// 本地组件
import RankingList from "@/components/RankingList"
  • 包导入方式
    • 所有导入使用alias的绝对路径导入,禁止使用相对路径
    • 按需导入,不要导入多余的函数
// 导入1~2个函数,写在一行,{} 前后各空一格
import { httpUrl, imageUrl } from "@/tools/globals"
// 导入3个以上函数时
import {
    appendImageUrl,
    appendPrefixStaticUrl,
    getImageSize,
    getParameterByName
 } from "@/tools/utils"
  • 组件导入时首字母大写
import RankingList from "@/components/RankingList"
  • 导入实例时,首字母小写,驼峰
import store from '@/store/store'

props属性使用时候需注明对应的类型,有必要时应给出默认值,禁止使用简写形式

// 错误用例
props: ['userName', 'userPassword']

// 正确写法
props: {
    // 用户名
    userName: {
      type: String,
      default: ''
    },
    // 用户密码
    userPassword: {
      type: String,
      default: '123456'
    }
  },

methods

  • methods书写顺序,把所有的restful 请求写在methods的最前面.
  • methods书写使用es6的写法
methods: { 
      /**
      * 请求平面图和所有品牌logo
      */
      async getBrandInfoList() {
        let res = await getBrandInfoList()
        if (res && res.code === 200) {
          this.brandInfoList = res
        }
      },
      ...
}
  • api的请求函数命名与restful的method名一致
  • restful请求函数之后,书写initControl函数,用于逻辑控制。逻辑的每一部分都用1、2、3...显示地注释
async initControl() {
        // 1. 请求数据
        await this.getBrandInfoList()
        // 2. 获取店铺图的信息
        await this.getStoreInfo()
        // 3. 设置店铺图
        this.renderStore()
        // 4. 格式化所有的品牌以及对应热区图的信息
        this.getBrandList()
        // 5. Dom上设置这些品牌
        // DOM是异步更新的,需要用nextTick
        this.$nextTick(this.renderBrandList)
 },
  • 有时使用原生js或者第三方库操作DOM元素时,对于View层进行操作的函数,函数名使用render开头
/**
   * 设置店铺平面图展示element的宽高背景图
*/
renderStore() {
        let storeEl = document.querySelector('.store-hot')
        // 设置背景图的宽高
        storeEl.style.height = this.storeHeight + 'px'
        storeEl.style.width = this.storeWidth + 'px'
        storeEl.style.backgroundImage = appendImageUrl(this.storeImageUrl)
        storeEl.style.backgroundImage = appendImageUrl(this.storeImageUrl)
        storeEl.style.backgroundSize = 'cover'
 }

html书写规范

  • 尽量使用 class 而非 style
  • 遵守 BEM 规范, 合理书写节点 (格式: 块 - 元素 - 修饰符)
  • 组件根元素设置与组件名称相同的 class (满足小写肉串形式)
  • 使用抽象的概念去设计html结构,活用v-for语法。特别是列表一类的,避免重复的元素占用过大篇幅
<template>
  <div class="user-reviews">
    <figure class="review">
       <blockquote class="review-tip">...</blockquote>
       <figcaption class="review-user">
          <img src="img/user-1.jpg" alt="User 1" class="user-photo">
          <div class="review-card">
            <p class="user-name">Nick Smith</p>
            <p class="user-date">Feb 23rd, 2017</p>
             </div>
             <div class="user-rating">7.8</div>
       </figcaption>
    </figure>
  </div>
</template>

SCSS书写规范

  • 统一使用scss
  • scss的class命名使用BEM规则
  • scss书写时层次尽量控制在3层,最多不要超过4层
  • 全局变量、函数、mixins统一写在_variables.scss_functions.scss_mixins.scss文件中
  • 全局对iview组件的修改统一写在_.iview.scss文件中
  • 动画样式抽离到 assets/scss/animation/xxx.scss 并通过对应 index.scss 引入
  • 编写每个组件的style时,最外层使用这个组件的名称的连接线形式,比如组件名称为i18nLocation那么,我们的代码应该是
<style lang="scss" scoped>
.i18n-location {
    ...
}
  • 组件内部 style 块 必须设置 scoped
    • 组件内部 style 书写统一包在 组件根元素类 下
    • 通过 :deep() 操作符更改子组件样式
<style lang="scss" scoped>
.flow-type-list { // 组件根元素类
  .content {
    &-bar {
      @include flex($x: flex-start);
      background-color: $color-white;
      height: 54px;
      padding: 0 30px;
      box-shadow: 1px 1px 4px 0 rgba(29, 49, 82, 0.08);
    }
    :deep(.table-header) {
      @include text(
        $font-size: 12px,
        $color: $text-color,
        $font-family: $medium-font-family
      );
    }
  }
  .page {
    @include flex($x: flex-end);
    margin-top: 10px;
  }
}
</style>

git工作流规范

工作流程

uAawQg.png

git提交规范

前言

多人协作开发项目中,Git 提升了项目开发进度的可控性,通过对提交、分支等浏览能快速了解项目整体进度开发流程中的各类信息。其中对于程序猿来讲比较重要的就是提交信息的准确程度,只有规范的提交信息才能在 review 时能够清楚了解提交的目的,方便后续开发。

​ 因此通过约定规范的形式让提交信息更准确、更科学,基于 AngularJS 规范

提交格式

包括三个部分:Header(必填),Body 和 Footer

<type>(<scope>): <subject>
<BLANK LINE>
<body>
<BLANK LINE>
<footer>

提交文本每行不得超过100个字符、各部分需空行分隔

  1. Header
  • Type: 提交类型,用于说明commit的提交性质。常用的有feat、fix、refactor。
    • feat: A new feature
    • fix: A bug fix
    • docs: Documentation only changes
    • style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
    • refactor: A code change that neither fixes a bug nor adds a feature
    • perf: A code change that improves performance
    • test: Adding missing or correcting existing tests
    • chore: Changes to the build process or auxiliary tools and libraries such as documentation generation
  • Scope: 提交内容的影响范围 (文件、路径、功能、上下文等)。scope依据项目而定,例如在业务项目中可以依据菜单或者功能模块划分,如果是组件库开发,则可以依据组件划分。scope可以省略。
  • Subject: 概括提交内容的精简语句 (不得超过50个字符)
  1. Body 对本次提交的详细描述:可以包含提交动机、版本差异等

  2. Footer

备注信息, 通常是 BREAKING CHANGE、修复的 bug 的链接 或 关闭 issue 等

  1. 特殊格式

revert: 当前 commit 是用来撤销以前某个 commit 时,须以 revert: 开头后面跟着被撤销 commit 的 Header


工具配置说明

安装commitizen

标准化 git commit 信息工具: 通过命令行方式快速选择提交类型按规范生成提交信息

安装适配器

commitizen 支持不同适配器的扩展,从而去满足不同的构建需求.更多适配器

# 全局安装
$ npm install -g cz-conventional-changelog
# 添加.czrc配置文件(全局设置/项目根目录)
# 内容: { "path": "cz-conventional-changelog" }

# 本地安装
$ npm install cz-conventional-changelog --save-dev
# 或使用 commitizen 工具
$ commitizen init cz-conventional-changelog --save-dev --save-exact
# package.json中添加配置相应的配置 (commitizen 工具自动添加)
"config": {
    "commitizen": {
        "path": "cz-conventional-changelog"
    }
}

命令行执行

# 替代 git commit 操作
$ npx git cz
# 全局模式不需要 npx
$ git cz

commitlint

标准化 git commit 信息验证工具: 检验提交信息是否符合规范

commitlint 也支持多种配置,按约定使用 angular 配置。更多配置

# 全局安装
$ npm install -g @commitlint/cli @commitlint/config-angular
# 本地安装
$ npm install -D @commitlint/cli @commitlint/config-angular

# 项目中添加配置文件/package.json中添加commitlint字段
$ echo "module.exports = {extends: ['@commitlint/config-angular']};" > commitlint.config.js

husky

Git Hooks 便捷实用工具

  • 安装
$ npm install --save-dev husky
  • 配置

在 package.json 配置Git提交验证

// package.json
{
  "husky": {
    "hooks": {
      "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
    }  
  }
}

代码风格规范

搜索关键字 prettiereslinteditorconfig在代码编辑器以及项目中配置。