前端开发规范

文件编码

统一使用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 命名法;

相关推荐
前端大卫5 分钟前
【重磅福利】学生认证可免费领取 Gemini 3 Pro 一年
前端·人工智能
孜燃16 分钟前
Flutter APP跳转Flutter APP 携带参数
前端·flutter
脾气有点小暴28 分钟前
前端页面跳转的核心区别与实战指南
开发语言·前端·javascript
lxh011334 分钟前
最长递增子序列
前端·数据结构·算法
vipbic40 分钟前
我封装了一个“瑞士军刀”级插件,并顺手搞定了自动化部署
vue.js·nuxt.js
Youyzq1 小时前
前端项目发布到cdn上css被编译失效问题rgba失效和rgb失效
前端·css·算法·cdn
San30.1 小时前
深入 JavaScript 内存机制:从栈与堆到闭包的底层原理
开发语言·javascript·udp
Fantastic_sj2 小时前
Vue3相比Vue2的改进之处
前端·javascript·vue.js
vipbic2 小时前
解决npm publish的404/403和配置警告全记录
前端·npm·node.js
Bigger2 小时前
🚀 “踩坑日记”:shadcn + Vite 在 Monorepo 中配置报错
前端·react.js·vite