你接手的第一版企業(yè)小程序,很可能是在兩周內(nèi)拼湊出來的——功能能跑,界面能用,但所有邏輯都塞在幾個(gè)巨大的頁(yè)面文件里。當(dāng)運(yùn)營(yíng)部門提出第三次促銷規(guī)則調(diào)整時(shí),你會(huì)發(fā)現(xiàn)改一行文案牽出三個(gè)隱藏 bug;當(dāng)管理層要求接入新的 CRM 數(shù)據(jù)源時(shí),開發(fā)評(píng)估周期直接超過首次開發(fā)的工時(shí)。這就是企業(yè)小程序最常見的死亡螺旋:初期交付越“快”的項(xiàng)目,長(zhǎng)期維護(hù)成本越高。

問題不在于小程序技術(shù)本身,而在于團(tuán)隊(duì)把小程序的開發(fā)當(dāng)成了“一次性活動(dòng)頁(yè)”來執(zhí)行,忽略了企業(yè)級(jí)軟件最基本的工程原則:可變性設(shè)計(jì)。企業(yè)小程序承載的是不斷變化的業(yè)務(wù)流程、組織權(quán)限和第三方系統(tǒng)對(duì)接,它需要的是和后臺(tái)系統(tǒng)同等級(jí)別的模塊化、可測(cè)試性和可交付性。

理解問題:為什么你的小程序“改不動(dòng)”

小程序框架(無論微信、支付寶還是其他平臺(tái))提供了一套頁(yè)面、組件和 API 的規(guī)范,但并沒有強(qiáng)制你寫出可維護(hù)的代碼。以下幾個(gè)癥狀幾乎出現(xiàn)在所有陷入困境的項(xiàng)目中:

  • 頁(yè)面“巨石”:一個(gè)頁(yè)面文件承載數(shù)據(jù)請(qǐng)求、狀態(tài)管理、業(yè)務(wù)判斷和 UI 渲染,單文件超過 800 行是常態(tài)。改動(dòng)任一環(huán)節(jié)都需要理解全部上下文。
  • 接口直連,零抽象層:每個(gè)頁(yè)面直接調(diào)用后端接口,字段名、數(shù)據(jù)結(jié)構(gòu)完全裸露。后端服務(wù)拆分或者字段調(diào)整,前端必須逐頁(yè)修改,無法做集中隔離。
  • 重復(fù)的業(yè)務(wù)邏輯:相同的價(jià)格計(jì)算、權(quán)限判斷、日期格式化邏輯散落在 12 個(gè)頁(yè)面里。修正一個(gè)計(jì)算誤差要像掃雷一樣排查所有副本。
  • 無自動(dòng)化驗(yàn)證:回歸測(cè)試完全依賴手工點(diǎn)擊。發(fā)布前的檢查清單越來越長(zhǎng),最后還是靠運(yùn)氣。

這些癥狀的根源是將“小程序”視為簡(jiǎn)單頁(yè)面集合,而非需要獨(dú)立架構(gòu)設(shè)計(jì)的客戶端應(yīng)用。只要這個(gè)認(rèn)知不扭轉(zhuǎn),任何“快速開發(fā)平臺(tái)”或“低代碼工具”都只是把問題延后放大,而不是解決。

解決方案:為可變性而架設(shè)三層結(jié)構(gòu)

企業(yè)小程序需要在一開始就建立三層分離的架構(gòu),即便團(tuán)隊(duì)只有兩名開發(fā)者,這個(gè)結(jié)構(gòu)也會(huì)在第三個(gè)月帶來成倍的效率提升。

1. 數(shù)據(jù)訪問層:把所有接口封裝成可替換的模塊

不要在任何頁(yè)面或組件里直接調(diào)用 wx.request 或平臺(tái)對(duì)應(yīng)的網(wǎng)絡(luò) API。你應(yīng)該創(chuàng)建一個(gè)獨(dú)立的 services/ 目錄,每個(gè)業(yè)務(wù)域一個(gè)文件。例如:

// services/orderService.js
import apiClient from '../utils/apiClient';

export async function fetchOrderList(params) {
  const { data } = await apiClient.get('/oms/v2/orders', params);
  // 在此處統(tǒng)一做字段映射或默認(rèn)值處理
  return data.map(item => ({
    id: item.order_id,
    statusText: ORDER_STATUS_MAP[item.status] || '未知',
    totalAmount: item.total_amount / 100,
  }));
}

這樣做帶來三個(gè)直接收益:當(dāng)后端字段名重構(gòu)時(shí),你只需改這一個(gè)函數(shù);你可以為測(cè)試環(huán)境切換 mock 實(shí)現(xiàn),而不動(dòng)業(yè)務(wù)代碼;所有調(diào)用方拿到的是已經(jīng)清洗過的穩(wěn)定數(shù)據(jù)結(jié)構(gòu)。

2. 領(lǐng)域狀態(tài)層:剝離業(yè)務(wù)規(guī)則,不依賴 UI

小程序的頁(yè)面 data 不是存放業(yè)務(wù)狀態(tài)的好地方。把規(guī)則判斷、計(jì)算邏輯和狀態(tài)轉(zhuǎn)換提取到純函數(shù)或簡(jiǎn)單的狀態(tài)管理模塊中。例如一個(gè)會(huì)員等級(jí)折扣計(jì)算:

// domain/membership.js
export function computeDiscount(level, originalPrice) {
  const rateMap = { silver: 0.98, gold: 0.95, platinum: 0.88 };
  const rate = rateMap[level] || 1;
  const discounted = Math.round(originalPrice * rate * 100) / 100;
  return { discounted, rate };
}

這個(gè)函數(shù)與渲染無關(guān),可以在單元測(cè)試?yán)镏苯域?yàn)證。當(dāng)運(yùn)營(yíng)調(diào)整折扣規(guī)則時(shí),你修改的是這個(gè)單一真實(shí)來源,所有引用它的頁(yè)面自動(dòng)生效。

3. 組件層:用“組合”而非“復(fù)制”構(gòu)建頁(yè)面

小程序支持自定義組件,但很多團(tuán)隊(duì)只把它用于通用 UI 控件。你應(yīng)該把業(yè)務(wù)組件也組件化——例如“訂單卡片”“員工選擇器”“可審批操作欄”。這類組件接收明確的屬性,通過事件向外拋出動(dòng)作,內(nèi)部維護(hù)自己的展示邏輯。

一個(gè)可復(fù)用的員工選擇器組件示意:

// usingComponents 聲明后使用
{
  "usingComponents": {
    "employee-picker": "/components/employee-picker/index"
  }
}
<!-- 頁(yè)面中調(diào)用 -->
<employee-picker
  mode="multiple"
  selected="{{approvers}}"
  bind:change="onApproverChange"
/>

把業(yè)務(wù)語(yǔ)義封裝在組件里,頁(yè)面只負(fù)責(zé)編排和事件轉(zhuǎn)發(fā)。當(dāng)審批人選擇規(guī)則從“單選”變?yōu)椤岸噙x+排除自己”時(shí),只有該組件內(nèi)部需要改動(dòng),所有使用場(chǎng)景同步更新。

執(zhí)行中的關(guān)鍵約束與邊界條件

平臺(tái)審核與能力限制是小程序架構(gòu)必須考慮的硬邊界。你的業(yè)務(wù)模塊拆分得再優(yōu)雅,也不能繞過下述事實(shí):

  • 包體積限制:微信小程序主包限制 2 MB,總包 20 MB。大規(guī)模的項(xiàng)目必須采用分包加載,將非核心業(yè)務(wù)、低頻頁(yè)面打入子包。在架構(gòu)初期就要規(guī)劃分包策略:哪些頁(yè)面屬于核心流程必須在主包,哪些可延遲加載。不要等體積超限再?gòu)?qiáng)行拆分,那會(huì)導(dǎo)致路由重構(gòu)和依賴亂套。
  • API 權(quán)限與上架審核:涉及用戶敏感數(shù)據(jù)、支付、位置等接口需要提前申請(qǐng)權(quán)限并填寫使用場(chǎng)景說明。如果你的業(yè)務(wù)組件封裝了定位功能,但小程序類目存疑,整個(gè)功能可能被拒。提前對(duì)照平臺(tái)《服務(wù)類目及資質(zhì)要求》設(shè)計(jì)功能邊界,避免開發(fā)完成才發(fā)現(xiàn)不可上線。
  • 多端差異:如果你的企業(yè)需要同時(shí)維護(hù)微信、支付寶、字節(jié)等多個(gè)小程序,不要指望一套代碼無痛跨端。即使使用 Taro、uni-app 等跨端框架,原生 API 的差異仍需業(yè)務(wù)層封裝隔離。你的數(shù)據(jù)訪問層應(yīng)當(dāng)基于自建 apiClient 適配不同平臺(tái)的底層請(qǐng)求方法,而不是直接依賴框架的跨端網(wǎng)絡(luò)模塊。

團(tuán)隊(duì)交付節(jié)奏上,架構(gòu)必須支持并行開發(fā)。三個(gè)開發(fā)人員同時(shí)做三個(gè)業(yè)務(wù)需求,不該在同一個(gè)頁(yè)面文件里產(chǎn)生沖突。按照你的三層結(jié)構(gòu),一個(gè)人寫 service,一個(gè)人寫組件,一個(gè)人寫頁(yè)面集成,沖突面被收斂到顯式的接口約定上。

遺留頁(yè)面整合是現(xiàn)實(shí)難題。你可能面臨一個(gè)已經(jīng)存在的巨石小程序,全量重寫成本不被接受。此時(shí)采用“絞殺者模式”:新功能嚴(yán)格按三層架構(gòu)開發(fā),老頁(yè)面逐步抽取。每次修改巨石頁(yè)面上的某個(gè)模塊時(shí),就地將其重構(gòu)為獨(dú)立組件或 service 函數(shù),同時(shí)補(bǔ)上測(cè)試。半年后巨石自然被掏空。

行動(dòng)建議:從下一個(gè)需求開始建立工程基線

你不需要暫停業(yè)務(wù)、花三個(gè)月重構(gòu)。更實(shí)際的做法是在下一個(gè)迭代中強(qiáng)制執(zhí)行三條基線,之后每一個(gè)需求都加固一次這些基線:

  1. 引入代碼審查卡點(diǎn):任何新的頁(yè)面文件超過 200 行,或者任何 API 調(diào)用直接寫在頁(yè)面內(nèi),PR 直接打回。
  2. 為關(guān)鍵業(yè)務(wù)函數(shù)編寫單元測(cè)試:利用 Jest 等工具在 CI 中運(yùn)行。先從 computeDiscount、權(quán)限判斷這類純函數(shù)開始,測(cè)試覆蓋不需要高,但必須覆蓋變更頻繁的核心規(guī)則。
  3. 建立接口契約文檔驅(qū)動(dòng)開發(fā):在動(dòng)手寫 service 之前,與后端確定該需求的接口字段、錯(cuò)誤碼和邊界情況,寫入共享文檔。接口聯(lián)調(diào)時(shí)直接用文檔校對(duì),不要靠口頭確認(rèn)。

企業(yè)小程序的長(zhǎng)期成本,在你第一次為了省時(shí)間把邏輯塞進(jìn) onLoad 時(shí)就已經(jīng)被決定了。工程基線不是奢侈的學(xué)術(shù)要求,而是保護(hù)你未來 12 個(gè)月迭代速度的唯一手段。它讓你的小程序從“一個(gè)項(xiàng)目”變成一個(gè)可持續(xù)演進(jìn)的業(yè)務(wù)載體,而這正是企業(yè)數(shù)字化資產(chǎn)與一次性營(yíng)銷頁(yè)面的本質(zhì)區(qū)別。

← 上一篇 高端網(wǎng)站建設(shè)失效的根因:把“奢華設(shè)計(jì)”當(dāng)成全部解決方案 下一篇 → 你的企業(yè)需要B2B2C商城嗎?——適用邊界與決策清單