你想知道的前后端協(xié)作規(guī)范都在這了
一. 前言
你是否在為如何制定前后端協(xié)作規(guī)范而發(fā)愁?干貨來啦,一文帶你了解我們團(tuán)隊(duì)內(nèi)部沉淀并踐行已久的前后端協(xié)作規(guī)范,讀完本文,回去大膽拒絕你后端的不合理設(shè)計(jì)!
二. 為什么需要協(xié)作規(guī)范?
假如你要在團(tuán)隊(duì)內(nèi)部推一套規(guī)范,那么首先你得知道為什么需要制定協(xié)作規(guī)范呢?有規(guī)范會(huì)帶來什么好處呢?
隨著前后端分離開發(fā)模式大行其道,前端和后端已經(jīng)在兩個(gè)方向上漸行漸遠(yuǎn),各自深耕細(xì)作、術(shù)業(yè)專攻。前端更加關(guān)注交互視覺體驗(yàn),而后端對(duì)高并發(fā)、高性能、高擴(kuò)展上要求更高。這就導(dǎo)致大部分的前端和后端之間會(huì)存在所謂的"代溝",我不知道你的數(shù)據(jù)如何存儲(chǔ),你不知道我的頁面如何渲染。
因此,很有必要制定前后端開發(fā)上的規(guī)范來抹平代溝,有了協(xié)作規(guī)范,便有了前后端開發(fā)默契,也因此達(dá)到了提高開發(fā)效率、降低溝通成本的作用。
三. 協(xié)作流程規(guī)范
首先是協(xié)作的流程規(guī)范,相信每個(gè)團(tuán)隊(duì)在前后端協(xié)作中都有各自的開發(fā)模式和開發(fā)流程來保障效率和質(zhì)量,我們團(tuán)隊(duì)的前后端協(xié)作大致流程如下圖所示:
- 需求導(dǎo)入、交互視覺導(dǎo)入分析 :對(duì)產(chǎn)品導(dǎo)出的需求,參會(huì)各方包括產(chǎn)品、前端、后端、測試、UED,在對(duì)需求的認(rèn)知上要達(dá)成一致,這是開發(fā)的第一步。
- 接口設(shè)計(jì)、前后端對(duì)接接口:后端給出接口,前后端要在接口字段設(shè)計(jì)上達(dá)成大致方向上的一致。
- 技術(shù)方案評(píng)審 :在開發(fā)之前進(jìn)行技術(shù)方案評(píng)審,再次確保各方在需求的認(rèn)知上統(tǒng)一,并且雙方就接口字段可行性上再次確認(rèn)。
- 并行開發(fā) 、前后端自測:前后端并行開發(fā),在此階段前端可以 mock 數(shù)據(jù)進(jìn)行頁面渲染。
- 開發(fā)環(huán)境聯(lián)調(diào):前后端自測完成之后在開發(fā)環(huán)境上完成接口聯(lián)調(diào)。
四. 如何做接口規(guī)范?
- 前置約定:
- 后端接口定義 URL、出入?yún)⒅?,前后端需達(dá)成一致。
- 文檔規(guī)范:
- 接口注釋需要寫清楚:模塊、枚舉、必填/非必填、出參是否可能為 null
- 接口需要向下兼容,如果不兼容需要評(píng)估并且通知相應(yīng)的業(yè)務(wù)方
- 接口文檔上面有變更需及時(shí)同步前端
- 后端需保證文檔上定義的參數(shù),可以正常請(qǐng)求接口且功能正常穩(wěn)定
- 位約定:
- 時(shí)間:統(tǒng)一使用 13 位時(shí)間戳
- 金額:統(tǒng)一為分 ,可根據(jù)業(yè)務(wù)情況選擇
- 接口 URL & 請(qǐng)求方式
- Post 接口不允許使用 Get 傳參方式
- Post 接口必須使用 application/json 模式
- 接口命名應(yīng)盡量符合語義,接口命名不要過于相似,難區(qū)分,易混淆
- 入?yún)?/li>
- 保證同一應(yīng)用領(lǐng)域內(nèi),相同含義的字段,命名保持一致
- 業(yè)務(wù)編號(hào) / ID 必須為字符串類型,JS 對(duì)最大數(shù)字有限制
- 同一個(gè)頁面不同 Tab,接口盡量保證一致
- 出參
- 接口出參格式要統(tǒng)一
- 接口不要返回類似 "服務(wù)器內(nèi)部異常"、"網(wǎng)絡(luò)異常" 這種無法理解的錯(cuò)誤信息,非線上環(huán)境可以返回錯(cuò)誤堆棧,方便排查問題
- 前后端數(shù)據(jù)列表相關(guān)的接口,如果返回為空,則返回空數(shù)組 [] 或空集合 {},有利于數(shù)據(jù)層面上的協(xié)作更加高效,減少前端很多瑣碎的 null 值判斷,特殊情況特殊分析
- 接口出參根據(jù)頁面需求返回有效字段,避免吐出過多無用字段
- 枚舉值盡量返回中文和英文描述
五. 協(xié)作中的 Bad Case
以下總結(jié)了我們團(tuán)隊(duì)內(nèi)部在協(xié)作中遇到的比較典型的 Bad Case 以及解決方案,我相信大家在開發(fā)過程中也遇到過類似的痛點(diǎn)經(jīng)歷:
類型 1:前端過多的條件邏輯判斷
【現(xiàn)象】
- 按鈕、組件顯示與否,前端要通過大量的字段進(jìn)行條件邏輯判斷;同一頁面不同場景前端調(diào)用的接口不一樣:
// 按鈕文案、顯示邏輯
{((record.state === 'RESULT_CONFIRM' && isCurrentUserCreate) ||(record.state === 'RESULT_CHECK' && isCurrentUserCreate && currentUserCanCheck )) && <Button>確認(rèn)</Button>}
{['DREFT', 'AUDIT_FAILD', 'REVOKE'].includes(record.state) && isCurrentUserCreate && <Button>修改</Button>}
// A 場景調(diào)用接口 1,B 場景調(diào)用接口 2,C 場景調(diào)用接口 3 和 4
if (id) {
this.operation = '修改';
const res = await this.fetchInfo(id);
} else if (source) {
const res = await this.fetchSourceInfo(id: source);
} else {
const res = await this.fetchBasicInfo();
}
【解決】
- 控制前端顯示邏輯判定都放在后端去做處理,前端盡可能減少字段判定。
注:如果功能簡單,前端也可以做判斷,如何鑒定是否簡單?從代碼層面比如 If 判斷中超過 2 個(gè)條件,按鈕顯示超過 2 個(gè)條件,可視作復(fù)雜邏輯,邏輯移到后端處理。建議一開始就視作復(fù)雜去處理,這樣后期就不用再調(diào)整。
// 按鈕展示
前后端約定好 按鈕的顯示返回一個(gè)數(shù)組,數(shù)組具體返回哪些邏輯寫在后端。
[
{ name:'確認(rèn)',type:'resultConfirm'},
{ name:'修改',type:'edit' },
]
【好處】
- 將邏輯收斂到后端,出現(xiàn)問題或者更改邏輯時(shí)只需一方排查或修改。即能一端完成的,絕不讓兩個(gè)端干, 兩個(gè)就可能會(huì)出現(xiàn)不一致的問題。
類型 2:前端二次數(shù)據(jù)加工過多
【現(xiàn)象】
- 頁面上同一個(gè)表格展示的數(shù)據(jù)是兩個(gè)接口拼接而成
- 接口數(shù)據(jù)返回格式不符合前端渲染邏輯,需要二次加工
【解決】
1、后端做好數(shù)據(jù)的整合,避免數(shù)據(jù)在前端的重組。
2、Tree 數(shù)據(jù)展示的場景,如果數(shù)據(jù)不大后端全量返回,如果數(shù)據(jù)量過大異步返回,但異步返回存在后續(xù)的回顯和搜索展示方面問題。
3、同一個(gè)業(yè)務(wù)功能,一個(gè)接口搞定,不要分接口進(jìn)行,后端業(yè)務(wù)考慮復(fù)用可包裝新接口或原接口加參數(shù)兼容。
【好處】
減少前后端數(shù)據(jù)處理的成本,提高性能和用戶體驗(yàn)
類型 3:枚舉值、下拉框數(shù)據(jù)由前端維護(hù)
【現(xiàn)象】
- 列表頁單據(jù)狀態(tài)由前端維護(hù)枚舉值,如果新增枚舉都需要前后端更改,可能導(dǎo)致最終顯示狀態(tài)不統(tǒng)一
// 狀態(tài)值映射
const getStatusName = (status) => {
switch(status) {
case 0:
return '草稿';
case 1:
return '待部門審批';
case 2:
return '待財(cái)務(wù)審核';
case 3:
return '待單位審核';
case 4:
return '審核中';
default:
break;
}
}
【解決】
- 確保狀態(tài)可擴(kuò)展,后端已經(jīng)做了枚舉的情況下,前端不需維護(hù)狀態(tài)值,以后端提供接口為準(zhǔn)。
如果是狀態(tài)定死的情況下譬如:選項(xiàng)為【是、否】可無需后端返回。
// 由后端接口返回下拉框選項(xiàng)
{
result: [{
code: string
name: string
}]
}
【好處】
- 枚舉值變動(dòng)時(shí),只需后端更新,也避免了迭代過程中出現(xiàn)的前后端不一致的情況
類型 4:PC 端數(shù)據(jù)結(jié)構(gòu)不適用 App 端
【現(xiàn)象】
- App 端的布局樣式 是較 PC 端略復(fù)雜的,如果 App 端一味采用 PC 端的接口數(shù)據(jù),是需要前端做特殊處理的。比如,同一時(shí)間的單據(jù) App 端放在同一張卡片內(nèi),卡片內(nèi)部的標(biāo)題、內(nèi)容、按鈕展示也做了情況區(qū)分。
【解決】
- 判斷前端處理工作量,后端需新增接口實(shí)現(xiàn) App 不同的功能。
【好處】
- 減少前端處理邏輯的成本,提高 App 上的用戶體驗(yàn)
類型 5:同一業(yè)務(wù)領(lǐng)域同一含義的接口字段命名不統(tǒng)一
【現(xiàn)象】
- 關(guān)于返回結(jié)果:response.data、 response.result
- 關(guān)于時(shí)間:createAt、queryEffectStartingBeginTime、penaltyBeginTime
- 關(guān)于名稱:punishedInstitutionName、responderName、penaltyObjectName
- 關(guān)于 Id: punishedOrganizationId,penaltyObjectId
【解決】
- 前后端共同維護(hù)一份字段詞典,保持同一業(yè)務(wù)領(lǐng)域下命名一致,避免不必要的字段轉(zhuǎn)換。
類型 6:金額計(jì)算結(jié)果由前端提交給后端并入庫
【現(xiàn)象】
- 前端頁面中,輸入支付金額并除以總額,然后計(jì)算出支付比例,最后點(diǎn)擊保存按鈕將數(shù)據(jù)提交給后端接口;
【解決】
- 對(duì)于金額的計(jì)算:以是否入庫為界限,非入庫純展示可前端計(jì)算,入庫的統(tǒng)一由后端計(jì)算。
類型 7:前端維護(hù)業(yè)務(wù)配置類型的代碼
【現(xiàn)象】
- 由多個(gè)表單項(xiàng)(下拉框、輸入框、單選框等)的值作為條件判斷某一表單項(xiàng)(附件、單選框、輸入框等)是否必填、顯示或隱藏。因此,前端需要寫很多動(dòng)態(tài)校驗(yàn)邏輯,并且涉及到每個(gè)區(qū)劃的動(dòng)態(tài)校驗(yàn)邏輯還不一樣,有的校驗(yàn)條件還是寫死的。
【解決】
- 配置校驗(yàn)規(guī)則的頁面可根據(jù)區(qū)劃配置后生成標(biāo)識(shí)碼,然后后端可提供一個(gè)通用的校驗(yàn)接口,前端把值傳給后端,然后返回校驗(yàn)結(jié)果是否通過。
// 入?yún)ⅲ?/span>
{
code: '99900', // 區(qū)劃代碼
identity: '11111', // 標(biāo)識(shí)碼
datas:[ // 數(shù)據(jù)
{
key: 'catalog',
value: 'A07',
},
{
key: 'assetApproval',
value: 0,
}
]
}
// 返回值:
{
result: true
}
類型 8:前端直接調(diào)用其它業(yè)務(wù)線后端的接口
【現(xiàn)象】
- 業(yè)務(wù)線 A 列表頁面,點(diǎn)擊新建按鈕,彈框調(diào)用業(yè)務(wù)線 B那邊的接口。
由于 A 和 B 是不同業(yè)務(wù)線后端,接口對(duì)接以及后期的溝通維護(hù)成本會(huì)比較高。例如該接口發(fā)生改動(dòng),需要跨業(yè)務(wù)線通知到對(duì)應(yīng)的前端(該后端還不一定知道前端是哪位);并且接口返回的大量字段前端都用不到。
【解決】
- 后臺(tái)業(yè)務(wù)耦合的情況下需要自己業(yè)務(wù)線后端整合數(shù)據(jù);如果只是為了展示非自己業(yè)務(wù)的數(shù)據(jù)后端不處理
類型 9:后端分頁接口的數(shù)據(jù)返回格式不統(tǒng)一
【現(xiàn)象】
- 目前分頁接口的數(shù)據(jù)返回格式不統(tǒng)一,已有如下幾種形式:
// 形式一:
{
result: {
data: [],
total: 0,
}
}
// 形式二:
{
result: {
data: [],
pagination: {
total: 0,
pageSize: 10,
pageNo: 1
},
}
}
// 形式三:
{
result: {
data: [],
total: 0,
pageSize: 10,
pageNo: 1
}
}
【解決】
- 建議后端接口統(tǒng)一格式如形式三。
類型 10:后端一個(gè)接口拆分多個(gè)
【現(xiàn)象】
- 一個(gè)表單頁,在提交之前調(diào)用三個(gè)不同的校驗(yàn)接口。三個(gè)校驗(yàn)接口入?yún)⒁膊灰粯樱岸诵枰M裝各種類型的數(shù)據(jù)。
【解決】
- 多個(gè)校驗(yàn)接口和提交接口合并成一個(gè)提交接口。
- 校驗(yàn)不通過時(shí),接口返回值里區(qū)分阻塞式和提醒式
- 阻塞式:彈框告警,用戶只能關(guān)閉彈框
- 提醒式:彈框詢問,在用戶點(diǎn)擊"繼續(xù)提交"后,繼續(xù)調(diào)用提交接口,此時(shí)增加入?yún)?biāo)識(shí)跳過此步校驗(yàn)
六. 效果
基于一套合理可行的協(xié)作規(guī)范,前后端從開發(fā)到上線的各個(gè)階段都能夠看到諸多成效:
- 降低溝通成本,減少不必要的扯皮, 加快開發(fā)進(jìn)度;
- 縮短聯(lián)調(diào)時(shí)間,減少聯(lián)調(diào)階段的代碼調(diào)整,保證了開發(fā)效率;
- 減少測試階段的排查問題歸屬,加快測試進(jìn)度,保證質(zhì)量;
- 方便線上問題排查及修復(fù)。
七. 總結(jié)
一言以蔽之:如果你發(fā)現(xiàn)前端在處理大量的邏輯,那么就是協(xié)作規(guī)范存在問題啦!前端更多的是關(guān)注交互、渲染上的邏輯,應(yīng)盡量避免復(fù)雜的業(yè)務(wù)邏輯處理。萬事開頭難!推一套規(guī)范是需要時(shí)間去沉淀的,前端和后端同學(xué)都應(yīng)多些耐心,多些理解。