# frontend-development-specifications
**Repository Path**: xiangyongjun/frontend-development-specifications
## Basic Information
- **Project Name**: frontend-development-specifications
- **Description**: 前端开发规范
- **Primary Language**: TypeScript
- **License**: MIT
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 2
- **Forks**: 0
- **Created**: 2023-09-04
- **Last Updated**: 2023-09-05
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# 前端开发规范(2023-09-03)
### 作者:向永俊
## 一、命名规范
### 1. 项目、目录、文件命名、(vue 文件除外)
```
采用全小写形式,每个单词以中线分割
例:mall-management-system、image-util.ts、home-background.png
```
### 2. vue 文件命名
```vue
```
### 3. 路由命名
```typescript
{
path: '/sys-manage', // 全小写,中线分割
name: 'SysManage', // 大驼峰
component: Layout,
meta: {
title: '系统设置',
icon: 'home-2-line',
breadcrumbHidden: true,
},
},
```
### 4. css 命名
```scss
```
### 5. 变量、函数命名
```typescript
// 采用小驼峰形式
const userInfo: string = 'abc'
const getUserInfo = () => {
return userInfo
}
```
### 6. 类、方法、属性命名
```typescript
// 类名使用大驼峰
class Human {
// 属性使用小驼峰(开头下划线是私有属性,ts 新特性)
_realName: string
// 方法使用小驼峰
setRealName(realName: string) {
this._realName = _realName
}
}
```
### 7. 目录结构
```typescript
项目
|--node_modules
|--public
|--src
|--api // 请求接口目录
|--assets // 静态资源目录
|--config // 配置目录
|--dictionary // 字典目录
|--hooks // 自定义钩子目录
|--directives // 自定义指令目录
|--components // 公共组件目录
|--utils // 工具目录
|--pages // 页面目录
|--home
|--Home.vue
|--components // 页面子组件目录
|--HomeTree.vue
|--HomeTable.vue
|--stores // 页面共享数据目录
|--index.ts
|--router // 路由目录
|--stores // 仓库目录
|--models // 模型目录
|--styles //样式目录
```
## 二、JavaScript 规范
### 1. 定义变量尽量使用 const,避免使用 var
```typescript
// 当变量是个对象时,请使用 const
const a: Object = {}
// 当变量是个数组时,请使用 const
const b: Array = []
// 当变量定义后不会再改变,请使用 const
const projectName: string = '测试项目'
```
### 2. 如需改变原数组数据结构,但长度不变,请使用 Array.map
```typescript
interface Human {
name: string
age: number
}
// 人类集合
const humans: Array = [{ name: '测试1', age: 10 }, { name: '测试2', age: 55 }]
// 姓名集合
const names: Array = a.map(item => (item.name))
// 输出结果:names: ['测试1', '测试2']
```
### 3. 如需对数组元素进行累加操作,请使用 Array.reduce
```typescript
const apples = [
{
id: 1,
amount: 10
},
{
id: 1,
amount: 100
}
]
const totalAmount = apples.reduce((pre, cur) => (pre + cur.amount), 0)
// 输出结果:totalAmount: 110
```
### 3. 尽量使用 async、await 语法糖将代码扁平化增加代码阅读性
```typescript
import { userLogin, getUserInfo } from '../api/user'
// 反面教材
const login = (username: string, password: string) => {
userLogin({ username, password }).then(res => {
if (res.code === 200) {
getUserInfo({ token: res.data.token }).then(res => {
if (res.code === 200) {
console.log(res.data)
}
})
}
})
}
// 正确写法
const login = async (username: string, password: string) => {
const { code, data: { token } } = await userLogin({ username, password })
if (code !== 200) return
const { code: code2, data: data2 } = await getUserInfo({ token: res.data.token })
if (code !== 200) return
console.log(data2)
}
```
### 4. loading 需要在 finally 中关闭
```typescript
import { userLogin, getUserInfo } from '../api/user'
// 反面教材
const login = async (username: string, password: string) => {
wx.showLoading({ title: '正在登录...', mask: true })
const { code, data: { token } } = await userLogin({ username, password })
wx.hideLoading()
}
// 正确写法
const login = async (username: string, password: string) => {
wx.showLoading({ title: '正在登录...', mask: true })
const { code, data: { token } } = await userLogin({ username, password }).finally(() => uni.hideLoading())
}
// 正确写法
const login = async (username: string, password: string) => {
wx.showLoading({ title: '正在登录...', mask: true })
try {
const { code, data: { token } } = await userLogin({ username, password })
if (code !== 200) return
const { code: code2, data: data2 } = await getUserInfo({ token: res.data.token })
if (code !== 200) return
} catch {
} finally {
wx.hideLoading()
}
console.log(data2)
}
```
### 5. 尽量使用 === 代替 ==
```typescript
console.log(10 == '10') // true
console.log(10 === '10') // false
```
### 6. && 运算符的妙用
```typescript
const isLogin = true
const hasVIP = true
const joinRoom = () => {
console.log('成功加入房间')
}
isLogin && hasVIP && joinRoom()
// 输出结果:成功加入房间
```
### 7. || 逻辑运算符的妙用
```typescript
const result1 = null
const result2 = false
const result3 = 'result3'
console.log(result1 || result2 || result2)
// 输出结果:'result3'
```
### 8. ?? 空值合并操作符的妙用
```typescript
const result1 = null
const result2 = false
const result3 = 'result2'
console.log(result1 ?? result2 ?? result2)
// 输出结果:false
```
### 9. 函数简写
```typescript
// 优化前
async function sleep(time: number) {
return new Promise((resolve) => {
setTimeout(function() {
resolve()
}, time)
})
}
// 优化后
const sleep = async (time: number) => new Promise(resolve => setTimeout(resolve, time))
```
### 10. 三元运算符
```typescript
// 优化前
const a = 1
if (a === 1) console.log('是1')
else console.log('非1')
// 优化后
console.log(a === 1 ? '是1' : '非1')
```
### 11. 可选链操作符
```typescript
// 优化前
const a = {
b: null
}
if (a && a.b && a.b.c) console.log('c节点存在')
// 优化后
if (a?.b?.c) console.log('c节点存在')
```
### 12. 优化 if、else if 、else 分支
```typescript
// 优化前
const light = 'green'
if (light === 'red') console.log('红灯')
else if (light === 'green') console.log('绿灯')
else if (light === 'yellow') console.log('黄灯')
else console.log('灯坏了')
// 优化后
const lights = {
'red': '红灯',
'green': '绿灯',
'yellow': '黄灯'
}
console.log(lights[light] || '灯坏了')
```
### 13. 多个请求同时发送时,请使用 Promise.all
```typescript
import { getHeaderData, getFooterData } from '../api/home'
const getData = async () => {
wx.showLoading({ title: '加载中...', mask: true })
const res = await Promise.all(getHeaderData(), getFooterData()).finally(() => wx.hideLoading())
if (res.some(item => item.code !== 200)) return
console.log('数据加载完毕', res[0].data, res[1].data)
}
```
### 14. 尽量使用高阶函数解决问题
```typescript
filter、map、find、forEach、findIndex、reduce、some、every、sort 等
```
## 三、Vue 组件通讯规范
### 1. 父传子、子传父(仅一对一时使用)
```vue
```
```vue
...
```
### 2. 爷传孙(一对一、一对多时使用)、父传子(一对多时使用)
```vue
```
```vue
...
```
### 3. 同一业务、页面组件共享数据、方法的封装
```vue
```
```vue
```
```vue
...
```
```typescript
// home/store/index.ts
import { ref } from 'vue'
import { getTreeData, getTableData } from '@/api/home'
import { ElLoading } from 'element-plus'
interface Tree {
id: number
label: string
children?: Tree[]
}
const treeData = ref>([])
const tableData = ref>([])
const curTreeNode = ref()
const getHomeTreeData = async () => {
const loading = ElLoading.service({ fullscreen: true, text: '加载中' })
const { code, data } = await getTreeData().finally(() => loading.close())
if (code !== 200) return
treeData.value = data
}
const treeHandleClick = async (node: Tree) => {
curTreeNode.value = node
const loading = ElLoading.service({ fullscreen: true, text: '加载中' })
const { code, data } = await getTableData({ id: data.id }).finally(() => loading.close())
if (code !== 200) return
tableData.value = data
}
export const useHomeStore = () => {
return {
treeData,
tableData,
getHomeTreeData,
treeHandleClick
}
}
```
### 4. Vuex、Pinia 用作全局状态管理,例如用户数据、菜单数据、购物车数据等
### 5. EventBus 用作全局通讯,除非迫不得已,尽量不要使用,溯源困难、维护困难
## 四、Vue 公共组件开发规范
### 1. 目录结构规范
```bash
项目
|--src
|--components
|--组件文件夹(大驼峰命名)
|--组件(大驼峰命名).vue
|--README.md
```
### 2. 组件文档编写规范
````markdown
# 组件名称
组件功能描述
## 组件属性
| 属性名 | 说明 | 类型 | 可选值 | 默认值 |
| ----- | ---- | ---- | ----- | ----- |
| | | | | |
## 组件事件
| 事件名 | 说明 | 回调参数 |
| ----- | ---- | ------- |
| | | |
## 组件方法
| 方法名 | 说明 | 参数 | 类型 |
| ----- | ---- | ---- | ---- |
| | | | |
## 组件插槽
| 插槽名 | 说明 | 作用域参数 |
| ----- | ---- | --------- |
| | | |
## 组件示例1
示例描述
```vue
...
```
## 组件示例2
示例描述
```vue
...
```
````
## 五、字典维护规范
```typescript
// src/dictionary/index.ts
function get(this: unknown, value: unknown) {
const keys = Object.keys(this)
const values = Object.values(this)
return keys[values.findIndex((item) => item === value)]
}
// 性别字典
export const Sex = {
'未知': 0,
'男': 1,
'女': 2,
get
}
console.log(Sex['男']) // 输出:1
console.log(Sex.get(1)) // 输出:男
```
## 六、表格 formatter 维护规范
```vue
```
```typescript
// src/utils/formatter.ts
import { Sex } from '@/dictionary'
import dayjs from 'vue'
export const sexFormatter = ({ cellValue }: any) => Sex.get(cellValue) || '-'
export const dateFormatter = ({ cellValue }: any) => cellValue ? dayjs(cellValue).format('YYYY-MM-DD') : '-'
```
## 七、api 编写规范
```typescript
// src/api/admin/user.ts
import request from '@/utils/request'
import { UserDto, UserVo } from '@/models/user-model.ts'
const module = '/admin/user/'
// 获取用户信息
export function GetUserInfo(params: UserDto) {
return request({
url: `${module}GetUserInfo`,
method: 'post',
params
})
}
```
## 八、models 生成规范
+ 模型请使用 [apifox](https://apifox.com/) 生成
1. 导入项目 - URL 导入(如已导入可略过)
2. 项目概览 - 代码生成 - 立即生成 - TypeScript - Axios
3. 生成代码 - 打开目录 - 将生成的 models 目录复制到项目的 src 文件夹下
```typescript
项目
|--src
|--models
```