没有规矩,不成方圆,对于团队来说,有一套完整的开发规范可以减少团队协作成本和维护成本,让代码阅读起来更容易。
一、代码风格插件
编辑器必备插件(以vscode为例)
- editorconfig (有助于为不同 IDE 编辑器上处理同一项目的多个开发人员维护一致的编码风格。)
- Prettier (是一款强大的代码格式化工具)
- eslint (强大的代码语法、格式/风格检测工具)
如果安装完以上的插件后没有格式化代码,请参考下面的解决方案。
① 保存代码无法格式化。 原因:可能VSCode有多个保存自动格式化的插件,未设置默认的插件,VSCode不知道选择哪个插件进行格式化。 解决方法:右键->格式化文档 会提醒没有设置默认的格式化插件,选择prettier插件即可,再自动保存时就可以自动格式化了。
② 保存代码无法格式化,但是右键->格式化文档可以格式化。 原因:可能VSCode未设置保存文件格式化。 解决方法:文件->设置->搜索栏搜索save->找到Editor: Format On Save并勾选上即可。
注:以上插件的相关配置已经在项目中同步,请勿随意修改。
二、开发规范
1、公共开发规范
1.1 命名
文件命名,函数命名,变量命名要有描述性,少用缩写,尽量达到望文生义,看其名,知其意。
注:所有命名(包括项目,目录,文件和函数以及变量)必须以字母开头,禁止以数字或符号开头和结尾。
👎 反例:2ndHandTemplate、 _pagesNewHouse、value1, value2
javascript
// 获取详情信息
getDetail() {
this.loading2 = true
this.loading3 = true
this.loading4 = true
let _this = this
queryMilitaryDetail({
id: _this.$route.params.id
}).then(res => {
this.detail = res
this.loading2 = false
queryHouseMilitaryId({
militaryId: res.id
}).then(res => {
this.billDataList = res.content
this.loading3 = false
})
})
buyNewhousePreOpt({ militaryId: _this.$route.params.id }).then(res => {
// console.log('buyNewhousePreOpt', res.content)
this.militaryHousePreList = res.content
this.loading4 = false
})
}
1.1.1 项目命名
全部采用小写方式, 以中划线分隔。
👍 正例:mini-anju、mini-agent
👎 反例:miniAnju、 mini_agent
1.1.2 目录命名
全部采用小驼峰式(camelCase)命名,除第一个单词小写之外,其他单词首字母大写。
:warning: 注:项目主目录主要以业务板块为主,不要随意创建目录。业务目录下面尽量用子目录区分页面和组件,不要在一个目录下创建不相关的很多文件。
👍 正例:pagesNewHouse、pagesPublic、propertyComplaint
👎 反例:PagesNewHouse、PAGESPUBLIC、Property_complaint、 bill-manage
1.1.3 文件命名
参照目录命名规则。
例:complaintManage.vue、userCenter.js、dataPanel.vue
1.1.4 函数命名
参照目录命名规则。前缀为动词
动词 | 含义 | 返回值 |
---|---|---|
can | 判断是否可执行某个动作 (权 ) | 函数返回一个布尔值。true:可执行;false:不可执行 |
has | 判断是否含有某个值 | 函数返回一个布尔值。true:含有此值;false:不含有此值 |
is | 判断是否为某个值 | 函数返回一个布尔值。true:为某个值;false:不为某个值 |
get | 获取某个值 | 函数返回一个非布尔值 |
set | 设置某个值 | 无返回值、返回是否设置成功或者返回链式对象 |
例:
javascript
function getUserInfo() {
// some codes
}
1.1.5 变量,常量命名
常量
常量采用大驼峰式命名,所有字母大写,以下划线分隔。(力求语义表达完整清楚,不要嫌名字长)
👍 正例:
javascript
const REGION_SCALE = 13
const GIS_MAP_SOURCE_ENUM = 'TENCENT'
👎 反例:
javascript
let SCALE = 13
let SOURCE_ENUM = 'TENCENT'
变量
变量采用小驼峰式(Camel-Case)命名,除第一个单词小写之外,其他单词首字母大写。
例:
javascript
let userProfile = {}
1.1.6 静态资源(图片)命名
采用小写方式, 以中划线分隔。可以根据位置,大小,功能等方面组合命名。
例:home-head-bg.png、icon-contact-small.png
1.1.7 路由命名
变量采用小驼峰式(Camel-Case)命名,除第一个单词小写之外,其他单词首字母大写。
1.1.8 PC、H5
父路由必须保函业务关键词,子路由根据业务和功能命名
:warning: 注:子路由的path前面不要加/(斜杠),不然子路由会升级为一级路由(父路由的path无效),最终可能会出现很多重复的路由
例:
javascript
{
path: '/helpCenterManage',
name: 'helpCenterManage',
// some attributes
children: [{
path: 'index'
// some attributes
}, {
path: 'manageType'
// some attributes
}]
}
1.1.9 小程序
因为小程序只有一级路由,所以必须要确保唯一性,根据业务和功能命名,严禁用一个单词命名。
👍 正例:
javascript
{
name: 'newHouseSearch',
desc: '新房搜索',
url: 'pagesNewHouse/newHouse/search/index'
}
👎 反例:
javascript
{
name: 'type',
desc: '贷款方式',
url: 'subPackages/preApprove/type/index'
},
{
name: 'list',
desc: '预审贷记录',
url: 'subPackages/preApprove/list/index'
}
1.2 注释
1.2.1 html、wxml 注释
模块注释
html
<!--底部固定按钮-->
<view class="fixed-btn-group flex">
···
</view>
1.2.2 js、vue 注释
单行注释
在代码上面注释,必须独占一行。// 后跟一个空格,缩进与下一行被注释说明的代码一致。
javascript
// 小程序更新
this.handleUpdateProgram()
后缀注释
在一段语句后面后缀进行注释, // 前后都跟一个空格,用于对某个语句的说明。
javascript
{
loanBank: 'CIB', // 贷款银行
bankCardInfo: {} // 用户银行卡信息
}
多行注释,对于自行定义的一些对象参数必须进行注释说明。(主要用于函数注释)
javascript
// 带[]的非为必传参数
/**
* 3.3. 修改密码
* @param {String} [params.apsMemberId] 通联会员id
* @param {String} params.oldPassword 原密码加密字符串(MD5)
* @param {String} params.newPassword 新密码加密字符串(MD5)
*/
export const modifyPassword = params => axios.post('/password/modify', params)
1.3 资源文件
- web端使用一倍图,移动端使用ios二倍图;
- 对于不需要透明格式的图片,推荐采用 jpg/jpeg 格式(文件更小)来代替 png 格式;
- cdn图片在上传之前需要通过tinypng 网站进行压缩再上传;
- 小图标先看下vant中有没有,没有再使用设计图中的图标;
- 通过dfs获取图片时,要设置合适的width和height,避免图片过大加载缓慢。必要时还可以设置水印isWater防止盗图;
html
// 小程序图片示例
<van-image
width="339rpx"
height="315rpx"
fit="cover"
src="{{ util.getImageUrl({ ids: info.banner[0], envVersion, isWater: true, width: 339, height: 315}) }}"
/>
1.4 关于template
- 循环语句(例如:v-for)需要添加key;
- 页面中需要频繁切换时候,使用v-show(小程序:hidden)判断,不要用v-if(小程序:wx:if);
- 页面结构要简洁清晰,并给出必要的注释;
html
<!-- 导航栏 -->
<div class="nav"></div>
<!-- 各业务板块内容 -->
<div class="wrap"></div>
- 页面内容进行合理的组件拆分,不要把所有逻辑写在一个页面中,增加维护成本;
- 不要在wxml中写复杂的表达式,正确的做法是放到计算属性里面判断;
👎 反例:
html
<button :disabled="!orderInfo?.id || ['PAYING', 'FAIL'].includes(orderInfo?.orderStatus)">锁定福利</button>
👍 正例:
html
<button :disabled="!canLock">锁定福利</button>
js
computed: {
canLock () {
// 未创建订单,或者订单支付中, 支付失败==》可以锁定福利
return !this.orderInfo?.id || ['PAYING', 'FAIL'].includes(this.orderInfo?.orderStatus)
}
},
- 小程序自定义组件控制其出现时,不支持hidden属性,可以在外层套个view标签,或者使用wx:if判断;
1.5 关于js
- 不要在生命周期函数中写过多的业务代码,要抽离成方法依次执行;
- 尽量避免多层循环嵌套逻辑,数据结构与预期差距较大时,跟后端沟通改结构;
- 避免繁琐的代码逻辑,在保证代码可读性前提下,能用一行写的不用两行;
- 枚举值条件判断时使用code,不要通过desc文字判断,防止后端改了desc出现bug;
- 避免一个函数过于臃肿,要进行适当拆分,对重复代码要提取成单独函数;
- 给出必要的注释,对于一些业务性很强的代码,或者一些非常规操作必须要给出注释。
1.6 关于css
- 多用公共样式表,减少冗余代码;
- 一般情况下,不要偷懒使用内联样式style,需放到样式表中,更好地实现结构与样式分离;
- 同一个模块的css文件放在一起,关键样式需要注释;
- 及时清除无用的css代码;
- less和scss语法注意使用规范。
👎 反例:
css
.head-search-wrap {
width: 100%;
display: flex;
}
.head-search-wrap .input-wrap {
border: 5rpx solid #ff5c00;
padding: 0 32rpx;
}
👍 正例:
css
.head-search-wrap {
width: 100%;
display: flex;
.input-wrap {
border: 5rpx solid #ff5c00;
padding: 0 32rpx;
}
}
- 在移动端,对点击区域比较小的元素,建议使用padding改变点击区域大小,或使用伪元素改变单击区域大小;
- 一些简单的吸顶,直接使用position:sticiy就可以实现,没必要用van-sticky;
1.7 UI
- UI还原度(尤其是小程序端)要保证90%以上,不能私自修改UI,如果有好的建议先跟产品和设计师沟通后再加以修改。
- 结构、表现和行为相分离。
1.8 使用骨架屏
- 页面加载合理使用骨架屏,避免请求后台数据时出现白屏
1.9 兜底
- 所有涉及到跟后台有交互的赋值。
javascript
// 查询条件初始化
const params = {
region: 'URUMQI',
limit: 30,
pageNum: 1
}
// 提倡使用结构赋值语法
const [err, res = {} ] = await promise2async(getProjectList(params))
// 小程序
this.setData({
projectList: res.content
})
// pc
this.projectList = res.content
1.10 开发约定
注:不能私自修改全局配置(包括路由,请求封装,项目相关的配置和部分公共组件),如果有修改需求请在前端群里提出。
-
优先使用es6+语法(比如async/await,结构赋值等),不要跟Peomise/then和async/await 混用 。
-
样式库里面定义的样式能满足大部分场景,所以能用样式库完成的需求尽量少些自定义样式。
👎 反例:
javascript
// async/await/promise-then混用,生命周期函数内部不整洁
async onLoad() {
await getDelayList({ supervisionId: this.$router.params.id }).then(res => {
this.setData({
detalyList: res
})
})
}
// 优化示例
async getDelayList() {
const { detalyList = [] } = await queryDelayList({
supervisionId: this.$router.params.id
})
this.setData({
detalyList
})
}
1.10.1 生命周期
- 灵活使用生命周期函数,保持内部整洁,不要把大量的业务逻辑放到生命周期中处理。
1.10.2 条件判断
- 除了if后面紧跟着return语句,其他情况都要把处理逻辑用大括号括起来。
👍 正例:
javascript
async getUserRoles() {
const jsessionId = wx.getStorageSync('jsessionId')
if (!jsessionId) return
const { AGENT:userAgentRoles = [] } = await queryUserRoles()
}
👎 反例:
javascript
handleChange(e) {
const { type, number } = e.currentTarget.dataset
if (typeof e.detail === 'object' && 'value' in e.detail) this.setData({ [type]: number ? Number(e.detail.value) : e.detail.value })
else this.setData({ [type]: number ? Number(e.detail) : e.detail })
}
// 优化示例
handleChangeReportType({ currentTarget, detail }) {
const { type, number } = currentTarget.dataset
if (isObject(detail) && detail.value) {
// some codes
} else {
// some codes
}
}
if/switch语句里面的条件尽量简写,太长了抽成变量再判断。
javascript
if (userRoles && userRoles.length > 0) {
// some codes
}
// 可简写
if (userRoles?.length) {
// some codes
}
1.10.3 可选链
- 在js中优先使用可选链操作符(?.)
- 不可滥用
可选链操作符
,需根据实际场景使用
1.10.4 新增,编辑操作
编辑跟新增理论上只差id,所以除非特别复杂的业务场景或需求外,禁止把这两块逻辑分开写,会增加后期维护成本。
1.10.5 多用少些
- 多看看公共util方法和公共组件,避免重复造轮子。
- 多用已有全局样式表,少写自定义样式。
1.10.6 资源压缩
- 引入第三方js文件尽可能选择压缩版本。
- cdn图片在上传之前需要通过tinypng 网站进行压缩再上传。
- 小程序端如果页面自定义样式比较多,优先使用用less。
1.11 安全相关
- 表单基础数据校验
- 用户信息展示
1.12 依赖包管理
- 私有库组件、hooks更新会在群里通知,项目负责人跟进更新状态。
- 请不要私自修改项目依赖包版本、安装新依赖。
2、小程序开发规范
2.1 合理使用生命周期函数
- 页面/组件初始化数据的操作要写在
onLoad/attached
中,禁止写在onReady/ready
中,因为页面渲染完成再调接口更新数据,会造成二次渲染的CPU资源浪费; - 通过wx:if渲染组件时,慎用
selectComponent
调用子组件中的方法,可能获取不到,也禁止使用定时器延迟操作,不能保证百分百会执行成功,建议直接在attached中初始化。如果部分操作需要等待父组件结果,可以使用observer进行数据监听,然后执行对应的操作; onLoad
中执行一次性事件,onShow
中进行需要实时更新的操作;onHide/onUnload/detached
中进行清除操作,例如清除定时器等;- 组件生命周期在
lifetimes
字段内进行声明(这是推荐的方式,其优先级最高);
js
Component({
lifetimes: {
attached: function() {},
detached: function() {},
}
})
组件中,页面的生命周期,在pageLifetimes
定义段中定义
js
Component({
pageLifetimes: {
show: function() {},
hide: function() {}
}
})
2.2 合理使用setData
data中只包含与渲染有关的数据(即直接在 wxml 中出现的字段)
- 与渲染有关的,定义在data中,通过setData设置
js
Component({
data: {
a: '与渲染有关的字符串'
}
})
- 与渲染无关的,定义在非data字段下,通过this.xxx设置
js
Component({
data: {},
c: '与渲染无关的字符串'
})
- 一般情况下禁止在Page外定义变量,因为页面销毁时,变量不销毁
js
let count = 0
Page({
onLoad() {
console.log(count) // 第一次进入页面0,第二次进入页面1,并没有被重新初始化
count++
}
})
控制setData的频率,多次setData操作可以合并为一个
👎 反例:
js
this.setData({ a: 1 })
this.setData({ b: 2 })
👍 正例:
js
this.setData({ a: 1, b: 2 })
- 避免在forEach等遍历语句中使用setData,可以将数据处理好再进行setData
👎 反例:
js
let { list } = this.data
list.forEach((item, index)=>{
this.setData({
[`list[${index}].year`]: '2024'
})
})
👍 正例:
js
list.forEach((item, index)=>{
item.year = '2024'
})
this.setData({ list })
- 频繁触发的用户事件(如 PageScroll 、 Resize 事件)中调用 setData ,合理的利用函数防抖(debounce) 和 函数节流(throttle)
典型案例:页面中某个元素滚动到一定位置才显示/隐藏
👎 反例:
js
onPageScroll({ scrollTop }) {
// 以下setData操作会以极高频率触发,严禁这样写
this.setData({
showToTop: scrollTop >= 500
})
}
👍 正例:
js
// 方式一:节流
onPageScroll: debounce(function ({scrollTop}){
this.setData({
showToTop: scrollTop >= 500
})
}, 100)
// 方式二:只在关键节点setData(这种方式下不会有延迟)
onPageScroll({ scrollTop }) {
let isShow = scrollTop >= 500
if (this.data.showToTop === isShow) return false
// 以下代码只在某个关键节点触发一次
this.setData({
showToTop: isShow
})
}
也可以将以上两种方式结合,性能更优
setData只传发生变化的数据
建议以数据路径形式改变数组中的某一项或对象的某个属性,而不是每次都更新整个对象或数组
js
let { list } = this.data
this.setData({
'list[2].status': '更新后的值',
'a.b.c.d': '更新后的值'
})
// 如果key中包含变量
this.setData({
[`list[${index}].status`]: '更新后的值'
})
选择合适的setData范围
组件的 setData 只会引起当前组件和子组件的更新,可以降低虚拟 DOM 更新时的计算开销。需要频繁更新的页面元素可封装为独立的组件
控制后台态页面的setData
后台态页面去 setData 也会抢占前台页面的运行资源,且后台态页面的的渲染用户是无法感知的,会产生浪费;
页面切后台后的更新操作,应尽量避免,或延迟到页面 onShow 后延迟进行。
严禁在切后台后仍进行高频的 setData,例如倒计时更新。
2.3 小程序图片
- 小程序中所有静态资源上传到CDN;
- 避免滥用图片的widthFix和heightFix模式,会引起重绘;
- 开启图片懒加载,设置 lazy-load 属性,避免屏外图片的请求影响小程序启动耗时;
- 调分页接口展示图片列表时,建议设置合适的limit(不传时后端默认为30),避免一次加载过多图片;
2.4 关于wxml
- 控制wxml节点数量。建议一个页面 WXML 节点数量应少于
1000
个,节点树深度少于30
层,子节点数不大于60
个; - 对于页面里面的标签,能用text的,就不用view;能直接用文本的,就不用text标签;对于一些循环渲染的列表,能用block标签的,就不要浪费一些view标签;
- 小程序自定义组件控制其出现时,不支持hidden属性,可以在外层套个view标签,或者使用wx:if判断;
2.5 关于wxss
- 组件内部统一使用class选择器,不要使用id、属性、标签选择器,慎用后代选择器.a .b及子元素选择器.a>.b,可能会不起作用或者出现意想不到的结果;
- background-image背景图片不支持本地图片,只能用网络url或者base64;本地图片要用image标签才行;
2.6 关于js
- 字符串转Date对象,应同时考虑安卓和苹果;
👎 反例:
js
// 在部分ios系统下不支持
let startTime = new Date('2021-10-18 23:00:00').getTime();
👍 正例:
js
let startTime = new Date('2021/10/18 23:00:00').getTime();
2.7 关于事件绑定
- 使用catch代替bind,因为在大多数情况下,我们的事件不需要冒泡,所以bind很浪费性能,catch是不会冒泡的;
- 减少自定义数据dataset的运输量,需要什么传什么,不要一刀切,图省事,传递一个很大的对象,如果需要整个对象,建议使用index进行传递;
- 组件绑定事件用冒号分隔开,例如catch:tap;
- 不需要监听事件时,不写空的onPageScroll;
2.8 避免同步操作
在小程序生命周期函数中避免多次同步操作,因为同步操作会阻塞主线程,导致小程序耗时严重。
2.9 组件/插件的引入
- 避免引入未使用的组件/插件;
- 避免全局app.json中引入自定义组件/插件;
- 通常插件占用主包大小过大,可以放到分包中,通过分包异步化进行引入;
2.10 及时清理无用的资源和文件
- 意外引入的第三方库;
- 版本迭代中被废弃的代码或依赖、产品环境不需要的测试代码;
- 未使用的组件、插件、扩展库;
2.11 避免内存泄漏问题
存在内存泄露问题会导致小程序在运行过程中内存占用持续增长,引起小程序闪退或被被微信强制销毁。
2.12 页面h5化
非必要不使用小程序开发页面,一些h5实现不了的功能再使用小程序开发,比如像分享到朋友圈等功能。
注意:如果仅分享给朋友,h5现在是支持的,有封装好的组件ShareButton
2.13 合理使用分包
- 按业务线或者功能进行分包,主包或者单个分包大小最好不要超过1.5M;
- 对启动性能要求比较高且功能比较独立的页面进行独立分包,比如活动页,广告页;
- 主包进入分包如果要提升加载速度,可进行分包预下载;
- 在A分包中引入B分包的组件或者require,可使用分包异步化,异步加载;
分包
在小程序启动时,默认会下载主包并启动主包内页面,当用户进入分包内某个页面时,客户端会把对应分包下载下来,下载完成后再进行展示。
在app.json中配置subPackages进行分包
json
{
"pages": [
"pages/index/index"
],
"subPackages": [
{
"root": "packageA",
"pages": [
"index/index"
]
}
]
}
独立分包
启动独立分包时,不需要下载主包
在app.json的subpackages字段中对应的分包配置项中定义independent字段声明对应分包为独立分包。
json
{
"pages": [
"pages/index/index"
],
"subPackages": [
{
"root": "indep",
"pages": [
"index/index"
],
"independent": true
}
]
}
分包预下载
示例:进入首页后,自动下载分包packageA,提升了启动分包packageA的速度
json
{
"pages": [
"pages/index/index"
],
"subPackages": [
{
"root": "packageA",
"pages": [
"index/index"
]
}
],
"preloadRule": {
"pages/index/index": {
"network": "wifi",
"packages": ["packageA"]
}
}
}
分包异步化
示例:为list组件设置占位组件,在list组件显示之前,先渲染其占位组件,等list组件下载完成后替换为真正的组件
html
<list wx:if="{{ visible }}" />
json
{
"usingComponents": {
"list": "packageA/components/list/index"
},
"componentPlaceholder": {
"list": "view"
}
}
2.14 小程序路由
注: 页面跳转必须用name跳转,不用页面路径。
新增页面后除了在app.json对应配置下新增页面路径外,在router目录的对应业务路由文件中也要新增页面页面相关信息,如:
app.json
javascript
"pages": [
"pages/index/index",
"pages/login/index"
]
router/public.js
javascript
export default [
{
name: 'home',
desc: '安居广厦-首页',
url: 'pages/index/index'
},
{
name: 'login',
desc: '登录',
url: 'pages/login/index'
}
]
3、vue3开发规范
3.1 代码结构
为符合代码阅读习惯,原则上需按照vue生命周期执行顺序去布局代码结构
js
<script setup>
import { reactive, onUnmounted, onMounted, onBeforeMount } from 'vue'
import { useRouter, useRoute } from 'vue-router'
const router = useRouter()
const route = useRoute()
// 组件挂载完成后执行的函数
onMounted(() => {
// some codes
})
// ------------------- 业务代码 ---------------------
// 用户基本信息
const baseInfoFn = reactive({
fn()
})
// 表格fn
const tableFn = reactive({
fn()
})
watch(() => tableFn, val => {
console.log(val)
})
// ------------------- end ---------------------
// 组件卸载后
onUnmounted(() => {
// some codes
})
</script>
API结构布局优先级高于生命周期函数,defineExpose
、defineProps
、defineEmits
等例如下:
js
<script setup>
import { defineEmits, defineProps, reactive } from 'vue'
const router = useRouter()
const route = useRoute()
// 对外暴露方法
defineExpose({
initPopup: () => {
dataFn.queryData()
}
})
const props = defineProps({
// 表格数据
rowData: Object,
show: {
type: Boolean,
default: false
}
})
const emits = defineEmits(['update:show', 'cancel'])
onMounted(() => {
// some codes
})
</script>
若页面功能复杂,需采用reactive({})
按不同的功能进行模块化代码布局,例如:
js
// ---------- 用户信息 -------------
const userGender = [
{ desc: '男', code: 'NAN' },
{ desc: '女', code: 'NUI' }
]
let name = ref('') // 基本类型
let age = ref('') // 基本类型
const show = computed(() => {
let isShow = baseInfoFn.userInfo.id
return isShow
}),
watch(() => baseInfoFn.userInfo, val => {
console.log(val)
})
// 用户基本信息
const baseInfoFn = reactive({
// 响应式
userInfo: {
name: '王二',
age: '18'
}
})
// 引用数据类型推荐使用toRefs解构
const { userInfo } = toRefs(baseInfoFn)
function fn() {}
// 禁止使用const声明式定义函数
const fn1 = () => {}
// ----------------- end ----------------
// ----------------- 表格 ---------------
watch(() => tableFn.tableName, val => {
console.log(val)
})
const tableFn = reactive({
data: [],
tableName: '',
})
function tableFn() {}
// --------------- end -------------------
反例,页面整体结构不清晰,注释缺失,难以维护:
js
const _MemberList = shallowRef([])
const _UserList = shallowRef([])
const _entUserList = shallowRef([])
const _groupList = shallowRef([])
const selectedRef = ref()
const selectedRect = shallowRef()
const lastChildRect = shallowRef()
const activeTab = ref('')
const checkedMembers = ref([])
const MembersLoading = ref(false)
const keywords = ref('')
const historyitemsHeight = ref(64)
const historyScene = computed(() => props.historyScene || 'common')
const filterMemberList = computed(() => {
return _MemberList.value.filter(item => item.name.includes(keywords.value))
})
watch(filterMemberList, val => {
MembersFinished.value = false
MemberListForShow.value = val.slice(0, pageSize)
if (val.length <= pageSize) MembersFinished.value = true
})
watch(filterEntUserList, val => {
entUsersFinished.value = false
entUserListForShow.value = val.slice(0, pageSize)
if (val.length <= pageSize) entUsersFinished.value = true
})
watch(filterGroupList, val => {
groupsFinished.value = false
groupListForShow.value = val.slice(0, pageSize)
if (val.length <= pageSize) groupsFinished.value = true
})
若页面复杂度不高且功能单一,可采用ref
搭配fn
的方式进行页面布局
js
<script setup>
import { ref } from 'vue'
const emits = defineEmits(['handleSaveUpload']
const show = ref(false)
/** 隐藏弹窗 */
function hidePopup() {
show.value = false
}
/** 附件上传 */
function upDateImages(ids) {}
/** 删除链接 */
function handleDeleteLink(item, index) {}
/** 确认选择 */
function handleSave() {}
</script>
3.2 defineProps
在使用defineProps
声明组件的props时,统一使用props
命名,不可自定义其他名称
接收组件的props时,根据实际业务场景,建议通过工厂函数赋值默认值
js
const props = defineProps({
// 表格数据
rowData: {
type: Object,
default: () => {}
},
// 用户列表
userList: {
type: Array,
default: () => []
}
})
若声明的组件props
仅在<template>
中使用,可不使用const
创建props
对象
html
<template>
<div>
<div>{{ rowData.name }}</div>
</div>
</template>
<script setup>
import { defineProps } from 'vue'
defineProps({
// 表格数据
rowData: {
type: Object,
default: () => {
return {}
}
}
})
<script>
3.3 defineEmits
在使用defineEmits
声明组件的自定义事件时,统一使用emits
命名,不可自定义其他名称
js
const emits = defineEmits(['update:show', 'cancel', 'submit'])
3.4 路由
- 页面初始化时请求参数为必传参数的情况下优先使用动态路由,其他参数用query或param(敏感信息用param)。
- 页面缓存(keep-alive),除了特殊场景外(产品经理提出来),其他情况都设置页面缓存。
- 不要在子路由的path加前缀'/',不然会升级为顶级路由。
👍 正例:
javascript
{
path: "/parentRoot",
children: [
{
path: "childPage",
}
]
}
👎 反例:
javascript
{
path: "/parentRoot",
children: [
{
path: "/childPage",
}
]
}
3.5 避免 v-if
和 v-for
一起使用
javascript
// bad
<ul>
<li v-for="item in orgList" v-if="item.orgType === 'ETPS'" :key="item.id">
{{ item.orgName }}
</li>
</ul>
// good
<ul>
<template v-for="item in orgList" :key="item.id">
<li v-if="item.orgType === 'ETPS'">
{{ item.orgName }}
</li>
</template>
</ul>
三、代码提交规范
git commit规范
通常我们的git commit会按照统一的风格来提交,这样可以快速定位每次提交的内容,方便之后对版本进行控制。
在项目中已经配置了git钩子工具,提交文件之前会自动检测代码语法和格式并自动格式化,如果有语法错误或提交格式不规范会拒绝提交。提交格式如下:
javascript
git add . // 暂存更改
git commit -m "type: 提交描述" // 提交描述(注:冒号后有空格)
git pull // 从远程分支同步
git push // 同步至远程分支
type 可选值:
Type | 作用 |
---|---|
feat | 新增特性 (feature) |
perf | 改善性能(A code change that improves performance) |
fix | 修复 Bug(bug fix) |
style | 代码格式修改(white-space, formatting, missing semi colons, etc) |
test | 测试(when adding missing tests) |
revert | 代码回退 |
util | 工具: 开发工具变动(构建、脚手架工具等) |
docs | 修改文档 (documentation) |
refactor | 代码重构(refactor) |
build | 变更项目构建或外部依赖(例如 scopes: webpack、gulp、npm 等) |
ci | 更改持续集成软件的配置文件和 package 中的 scripts 命令,例如 scopes: Travis, Circle 等 |
chore | 变更构建流程或辅助工具(比如更改测试环境) |
四、发布规范
1、发布规范
分支说明:
所有项目都有主分支(master),测试分支(sit-for-test),开发/业务分支(dev-*)和其他备份相关的分支。
注:千万不要把测试分支合到master分支或业务分支,只能由业务分支合到测试分支。
注:发布生产或测试之前要保证代码能够在本地正常运行。
1.1 测试发布流程
- 提交当前分支。
- 切换到sit-for-test分支,拉取最新代码。
- 把业务分支(或自己的分支)合并到sit-for-test分支并推送。(如果有冲突。处理的时候注意不要把别人的代码删没了)
- 如果是PC或H5,到Jenkins后台进行构建。如果是小程序,先从sit-for-test分支上传,然后到小程序后台把体验版切换到自己刚发布的版本。
注:测试分支保证先拉取最新版本再合并自己的分支,除了保证自己的代码上测试外还要不能影响其他人的测试。
1.2 生产发布流程
- 提交当前分支;
- 切换到master分支,拉取最新代码;
- 切换到原先(业务分支或自己的分支),把master分支合并到当前分支并推送到远程分支;
- 到git对应的仓库创建合并请求(从业务分支合并到master);
- 如果是PC或H5,上一部结束就可以通知产品经理跟进。 如果是小程序,回到编辑器把分支切换到master分支,拉取最新代码并上传,然后到小程序后台提审;
- 等master分支同步完了之后切换到测试分支,把业务分支合并到测试分支。(用于同步测试分支,减少冲突)
1.3 小程序合理规划版本发布
小程序启动时如果检测到版本更新,会进行以下操作,影响启动耗时
- 重新获取小程序的基础信息
- 进行小程序代码包的增量更新
- 重新生成 JS 代码的 Code Cache
- 重新生成初始渲染缓存
过于频繁的新版本发布可能会导致部分用户每次使用都需要进行小程序的更新,导致平均启动耗时变长。
在不影响小程序正常功能迭代的前提下,我们建议开发者提前做好版本规划,控制版本发布的频率。