前端开发规范

文件编码

统一使用utf-8无BOM格式;

命名规范

● 文件、类型、枚举类型、Interface使用PascalCase命名法, 如果文件内有且只有一个成员,则文件名应和该成员相同。

● 常量、枚举值使用UPPER_CASE 命名法

● 本地变量、函数、对象属性、函数形参使用camelCase命名法

● 资源文件命名使用camelCase命名法

● css的class命名名用纯小写单词, 多单词间用减号-隔开, 如下

js 复制代码
<templete>
    <view class="test">
        <text class="test-class">aaa</text>
        <img class="test-img"></img>
    <view>
</templete>

● Vue组件页面级别使用camelCase, 普通级别使用PascalCase

● 文件夹统一使用camelCase命名法

属性定义规范

● 禁止使用var声明变量, 推荐使用let和const分别声明可变和不可变的变量

ts 复制代码
class Demo {
    test() {
        var a: number = 1;       // 禁止
        let a: number = 1;       // 正确
        const a: number = 1;     // 正确
    }
}

● 定义属性时必须有类型

ts 复制代码
class Demo {
    num = 1;            // 禁止
    num: number = 1;    // 正确
}

● 避免使用any类型

除底层框架以外, 禁止在业务层代码中出现使用any类型定义的属性

ts 复制代码
interface DrugDetail {
    drugName: any;         // 禁止
    images: any[];         // 禁止

    drugName: string;      // 正确
    images: DrugImage[];   // 正确
}

interface DrugImage {
    id: number;
    url: string;
    action: string;
}

● 简单且确认不会被共用的类型可以使用匿名类型定义, 其他的必须用显示类型

ts 复制代码
interface PrescriptionDetail {
    patientInfo: { name: string, age: number };      // 禁止, 因为跟OrderDetail中的patientInfo类型重复
    
    patientInfo: PatientInfo;                        // 正确
}
interface OrderDetail {
    patientInfo: { name: string, age: number };      // 禁止, 因为跟PrescriptionDetail中的patientInfo类型重复
    
    patientInfo: PatientInfo;                        // 正确
}
interface PatientInfo {
    name: string;
    age: number;
}

● 业务上有可能为空的字段必须使用联合类型定义, 并注明什么场景下会为空

ts 复制代码
interface PatientInfo {
    /** 患者信息备注, 有可能为null */
    remark: string;                   // 禁止
    /** 患者可不填写自身备注信息 */
    remark: string|null;              // 正确
}

● 函数有返回值的必须声明返回值类型, 禁止使用类型推断特性

js 复制代码
class Demo {
    test() {           // 禁止
        return 1;
    }
    
    test(): number {   // 正确
        return 1;
    }
}

注释

注释不允许出现在包含代码的行内, 必须单独占用空行

● 类, 枚举类型, Interface必须声明注释, 使用/** */包裹注释内容, 注释内容包含: 作用、使用方法、作者等信息

js 复制代码
/**
 * 作用: 用来封装项目内的三级缓存策略流程
 * 用法: 继承Cache类, 定义泛型T为数据具体类型, 实现其中abstract方法, 建议完整子类都是单例模式
 * 作者: zhangdi
 */
class abstract Cache<T> {
    
}

● 函数(方法)必须声明注释, 使用/** */包裹注释内容, 注释内容包含: 作用、参数列表说明(如果有)、返回值说明(如果有)、作者等信息

js 复制代码
/**
 * ...
/*
class LoginService {
    
    /**
     * 根据患者id获取患者用户信息
     * @param patientId 患者id
     * @return 如果从缓存中能查到就返回PatientInfo对象, 否则返回null
     */
    private async getPatientInfo(patientId: number): Promise<PatientInfo|null> {
        ...
    }
    
    /**
     * 根据患者id获取患者用户信息
     * @return 需要展示返回true, 否则返回false
     */
    private shouldVisible(): boolean {
        ...
    }
}

● 类、Interface成员变必须声明注释, 使用/** */包裹注释内容, 注释内容为字段用途, 特殊字段需详细说明取值及作用

js 复制代码
interface OrderDetail {
    
    /** 订单id */
    id: number;
    
    /** 订单状态  1.未支付   2.已支付  3.已取消   4.已结束 */
    status: number;
}

class HomePage extends Vue {

    /** banner数据, 展示在页面顶部 */
    private banner: Banner[]|null;
    
}

● 代码块内, 如有较复杂逻辑需要特殊说明, 可以用单行注释// 内容或多行注释/* 内容 */声明

js 复制代码
class MergeSort {
    
    public static async login(): Promise<UserInfo> {
        let userInfo = await UserInfoCache.getInstance().get();
        // 登录接口的用户信息分了2级返回, 以前有的场景只保存了第一层数据, 为了兼容, 只保存第一层数据的账号让他重新登录下
        if (userInfo && userInfo.userId) {
          return userInfo;
        }
        return new Promise(async (resolve, reject) => {
          /*
           * 1. 先获取session, 如果获取不到直接抛出异常并返回
           * 2. 获取session成功后判断是否可以自动登陆, 如需自动登陆直接调接口登陆并返回用户信息
           * 3. 如果不能自动登录则跳转到登陆页面, 并把resolve和reject函数保存到LoginService.resolve和LoginService.reject中, 登陆成功后由登陆页面调用LoginService#loginSuccess方法触发promise返回值
           */
           
          // 获取session
          let session = await SessionCache.getInstance().get();
          if (!session) {
            reject(new UnknownError('获取Session失败'));
            return;
          }
        
          // 判断是否可以自动登录
          if (!session.autoLogin) {
            Tips.hideLoading();
            LoginService.resolve = resolve;
            LoginService.reject = reject;
            DNavigator.navigateTo(`/pages/login/index`);
          } else {
            Tips.showLoading();
            let userInfo = await Service.autoLogin(session.sessionId);
            UserInfoCache.getInstance().update(userInfo);
            Tips.hideLoading();
            resolve(userInfo);
            LoginObservable.notify(userInfo);
          }
        });
    }
    
}

TODO注释

因联调导致需要要提交功能未完善的代码, 必须在未完成的代码区块添加TODO注释, 署名并写清楚未来需要完善什么东西, 建议在写代码的过程中随手写TODO, 防止遗忘功能:

js 复制代码
startDrugDetail() {
    // TODO zhangdi: 等药品详情页面搞完实现跳转逻辑
}

相等条件

禁止使用和!=做相等条件判断, 应使用=和!==判断

js 复制代码
a == 1;        // 禁止
1 != 2;        // 禁止

a === 1;       // 正确
1 !== 2;       // 正确

空格

● 在单目运算符之间不追加空格:

js 复制代码
-b
!!b

● 如果单目运算符是一个单词,则添加一个空格:

js 复制代码
new Func
void 0
typeof x

● 在双目运算符左右追加一个空格:

js 复制代码
x + y * 3
a = b + 1;
-x + ++y

● 在冒号、逗号、分号后追加一个空格(末尾除外):

js 复制代码
a = 1, b = 2
var a: number = 1;

● 在 if、for、while、switch、with 语句的括号前后追加空格:

js 复制代码
if (a > 1) {

}
for (var i = 1; i < 2; i++) {

}
do {

} while (a < 1);

● 在函数声明的签名前后追加空格:

js 复制代码
function fn(a, b) {

}

● 在匿名函数的签名前不追加空格

js 复制代码
var fn = function(a, b) {

};

● 不建议为了对齐某些代码而手动添加空格,如下代码是不建议的:

js 复制代码
var hello = 1;
var hi    = 2; // 不建议在此次故意追加空格以保持 = 对齐。
var not   = 3; // 不建议在此次故意追加空格以保持 = 对齐。

● 但是可以为了对齐注释而添加空格。

js 复制代码
var hello = 1;   // 对齐的注释。
var hi = 2;      // 对齐的注释。
var not = 3;     // 对齐的注释。

新行

● 建议将 else、catch、finally 等子段写在一行:

js 复制代码
if (a > 1) {

} else if (b > 1) {

} else {

}

try {

} catch(e) {

} finally {

}

● 我们允许将空且永久为空的 {} 写在一行如:

js 复制代码
var obj = {};
function empty() {}

● 如果 {} 和 [] 内包含多个复杂的项,应分多行书写。

js 复制代码
var a = {
    b: 1,
    c: 2,
    d: [],
    e: [1, 2, 3],
    f: [
        "aaa",
        "bbb",
        "ccc",
        "ddd",
        "eee",
    ]
};

逗号

建议为{} 中的每个键值对末尾添加逗号。如果这个对象未来可扩展,在最后一个键值对也应追加逗号。

js 复制代码
var obj = {
    a: 1,
    b: 2, // 保留此逗号
};

enum A {
    a = 1,
    b = 2,
}

其他

语句应固定以 } 或 ; 结尾。一行内最多只能有一行语句。

不建议使用 with 语法。

对于无限循环,应使用 true 作为循环条件。

如果字符串内部是一段 HTML,则应该确保 HTML 使用双引号,外部字符串使用单引号。

允许使用 ?:、&& 和 || 代替 if 语句。

建议使用 `` 字符串书写多行字符串。

建议保持较低闭包嵌套级别。当闭包嵌套级别超过 5 时,建议您重新整理代码。

不建议使用 [] 获取常量属性。

js 复制代码
a["x"]   // 不建议
a.x      // 建议

git提交规范

commit日志:commit日志必须以add: , update: , delete: , fixed: 开头

● add: 用于开发新需求的提交, 如

add: 开发登陆页面, 逻辑未完善

● update: 用于文件修改或需求变更

update: **产品要求登陆时需要保存手机号, 登陆后把手机号保存到localstorage

● delete: 用于删除重要文件或页面

delete: **产品要求删除登录页面

● fixed:

fixed: 修复***bug, (有链接贴链接, 没链接写清楚bug描述), bug原因

日志内容尽量写为什么做, 而不是做了什么

js 复制代码
#推荐
update: 因服务端接口会在不同的接口上对patientId字段返回string和number类型, patientId不能再单纯的使用number

#不推荐
update: patientId兼容string类型

分支管理

● 纯净环境分支

  1. 测试环境: dev
  2. 生产环境: master/main

● 功能分支

所有的功能分支统一使用feature/ 开头

正确代码合并规范流程应该下面这样:

功能个人分支 => 功能总分支 => 不同的纯净环境分支

  1. 功能总分支
    统一使用 feature/xx 开头, 从master创建
    部署不同的环境时,统一使用功能分支合并到对应的纯净环境分支上面去(避免多余功能提前携带到生产)
  2. 功能个人分支
    建议使用 feature/xx_个人名字缩写 (方便排查开发者)

● 改bug分支

线上bug从master创建fixbug/开头的分支进行修复

● 提测流程

需要提测的feature或fixbug分支统一合并至dev分支, 如有并行提测需求但上线时间不同可按照dev/xx格式创建单独的提测分支, 同时新建流水线部署到不同的子文件夹进行测试

● 上线流程

测试通过的dev分支合并到master/main, 通过云效流水线发布后在master/main分支最后一次提交上打tag, 名称格式为release_功能名称, 打完tag删除feature/*分支

vue文件规范

● 文件里面需要统一在script标签上设置name,符合UPPER_CASE 命名法,作为当前文件的命名唯一性,方便开发者使用vue插件排查定位问题

js 复制代码
<script lang="ts" name="RevisitPreoperRevisitJob" setup>

● 响应式创建:

鉴于reaction只能基于集合类型进行响应式创建,对解构操作不友好,替换对象响应式容易丢失等问题

建议优先使用ref() 作为声明响应式状态的主要 API;

● 响应式标注类型:两种方法

js 复制代码
import { ref } from 'vue'
import type { Ref } from 'vue'
const year: Ref<string | number> = ref('2020')
// ********或者使用泛型直接声明***********
const year = ref<string | number>('2020')

菜单-路由配置

通过新增/编辑菜单,对菜单进行修改,其中常用的属性配置说明如下:

● 访问路径:当前菜单对应的访问前端路由;

(通常与前端项目目录访问地址保持一致,命名规范见"项目文件目录规范")

● 前端组件:对应@src/views目录下的默认页面对应的访问路径地址;

(通常与前端访问路径地址保持一致,例如:路由/revisit/customList,通常对应组件 地址:revisit/customList/index;命名规范见"项目文件目录规范")

● 组件名称:根据访问路径自动生成(前端组件name必须与此保持一致)

● 菜单图标:(可选),对应左侧Menus组件展示图标

● 排序:左侧菜单展示顺序(如1,2,3)配置

● 是否路由菜单:是否生成前端对应路由地址,非末级菜单可选择关闭(通常一级菜单不需要链接地址,只有末级菜单需要点击访问页面)

页面功能模块-权限配置

前端配置方向,主要分为两个方面,可见性和可编辑性

目前已封装统一函数针对两种不同场景进行处理

可见性:

按钮/权限名称:以"XXX的可见性"命名;

授权标识:使用蛇形命名法,命名规则 "一级菜单名(即功能模块名)_页面菜单名_字段名_show",如:revisit_reList_revisitTime_show

可通过hasPermission('权限编码')进行判断展示

可编辑性:

按钮/权限名称:以"XXX的可编辑性"命名

授权标识:使用蛇形命名法,命名规则 "一级菜单名(即功能模块名)_页面菜单名_字段名_edit",如:revisit_reList_revisitTime_edit

可通过isDisabledAuth('权限编码')来进行是否可编辑的配置

角色/用户配置

正确的权限规则流向:权限编码=》角色=》个人用户;

第一步:角色管理页面,对角色进行权限编码授权

第二步:用户管理页面,搜索用户,进行角色分配

第三步:刷新页面,完成当前用户的权限绑定关系

数据字典配置

新增/编辑字典

字典名称:使用蛇形命名规范,"一级菜单名(即功能模块名)_字典名"(中文)

字典编码:使用蛇形命名规范,"一级菜单名(即功能模块名)_字典名"(英文)

描述:对字段必要的解释说明

常用项:

列表可通过render.renderDict()实现对数据字典的引用,完成对应关系

下拉表单可通过设置dictCode属性,实现对数据字典的引用,完成对应关系

项目文件目录规范

紫色A:

● 业务一级菜单,为大功能模块前端路由一级路径命名,同时也是关联本次功能的总文件夹命名,使用camelCase命名法法

绿色B:

● 对应业务二级菜单,为大功能模块前端路由二级路径命名,使用camelCase命名法(若有三级菜单,以此类推......)

蓝色C:

● 为当前二级菜单对应的页面文件目录

● 同级放置对应二级菜单默认进入的页面,默认进入页面命名为index.vue(菜单对应页面)

● 同级放置非对应二级菜单默认进入的页面,例如edit.vue、detail.vue(若有:大多由默认页面跳转进入,非菜单对应页面)

● 同级放置当前功能页面的api文件,要求以XXX.api.ts格式命名,XXX符合UPPER_CASE 命名法;

● 同级放置当前功能页面的表格数据配置信息以及查询数据信息,配置信息多以大json为主;剩余包括页面对应的 Ts声明类型(interface、type、enum)等声明类型,要求以XXX.data.ts格式命名,XXX符合UPPER_CASE 命名法;

● 同级放置components文件夹,为页面提取的公共组件,以高复用性,高扩展性为基本原则进行提取,符合UPPER_CASE 命名法;

相关推荐
轻口味1 小时前
命名空间与模块化概述
开发语言·前端·javascript
前端小小王2 小时前
React Hooks
前端·javascript·react.js
迷途小码农零零发2 小时前
react中使用ResizeObserver来观察元素的size变化
前端·javascript·react.js
娃哈哈哈哈呀2 小时前
vue中的css深度选择器v-deep 配合!important
前端·css·vue.js
旭东怪3 小时前
EasyPoi 使用$fe:模板语法生成Word动态行
java·前端·word
ekskef_sef4 小时前
32岁前端干了8年,是继续做前端开发,还是转其它工作
前端
sunshine6415 小时前
【CSS】实现tag选中对钩样式
前端·css·css3
真滴book理喻5 小时前
Vue(四)
前端·javascript·vue.js
蜜獾云5 小时前
npm淘宝镜像
前端·npm·node.js