客服訂單詳情頁體驗升級之路
一、背景
作為客服域訪問量最大的頁面之一,訂單詳情頁在客服的日常工作中被用來查閱用戶的訂單信息,以此為進線的買賣家用戶提供更好的購買服務,進而提升用戶的滿意度。無論是一二線客服還是客服管理者,都能在日常使用的系統中直接訪問到詳情頁,因此客服訂單詳情頁的入口也比較多,目前已經超過了10處。
圖片
隨著得物業務的快速發展,客服訂單詳情頁需要展示的信息越來越多,需要支持的操作也越來越多,在頁面改版前,一個頁面首屏就需要展示80條訂單信息,具體數量會根據訂單類型、交易狀態、物流狀態等因素而改變。一個頁面最多情況會有200條信息、60個按鈕以及20個訂單標簽(不含用戶標簽、商品標簽),所以會出現一個現象,一個1920*1080分辨率的顯示器(客服常用的分辨率),鼠標滾輪需要滾2次才能從頁面的最上方滑到最下方,詳情頁信息截圖如下所示。
圖片
另一方面,貿然調整反而會降低客服同學的工作效率。頁面內容、交互變化太大對客服同學也存有不小的培訓成本,因此秉持著變更影響最小化的原則,信息量一直保持著只增不減。
經過一段時間的沉淀與打磨,客服訂單詳情頁無論是客服的使用體驗,還是開發體驗都得到了顯著提升,訂單相關的TS問題近半年一直保持清零狀態。這期間做了很多嘗試和漸進式優化,本文主要從以下三點具體聊聊對客服訂單詳情頁體驗升級做的一些思考和優化。
- 這么信息全的頁面,幾乎所有客服域平臺都要能直接訪問查閱,一個頁面如何多個系統使用,在保障客服同學使用體驗的同時,還能節省開發同學維護成本。
- 信息量大數據源多導致頁面加載慢,客服同學經常反饋卡頓,如何對頁面首屏進行秒開優化,進一步提升客服同學的使用體驗。
- 簡單的改動也需要投入資源,信息模塊復用率低,如何建設信息自由編排、信息模塊拔插的能力,最大程度解放產研運同學生產力。
二、多入口的頁面復用
1、多實例iframe
最早訂單詳情頁只是客服工單系統中的一個頁面,使用的是Vue2、ElementUI的技術棧。客服工單系統的定位是后臺管理系統,使用門檻較高,更適合管理者使用,所以需要一個面向一二線客服的平臺,章魚工作臺就誕生了。章魚工作臺是基于qiankun微應用搭建的,其子應用最開始使用Vue3、Vite和Ant Design,那時,如果要在章魚工作臺中訪問客服訂單詳情頁屬于跨應用、跨技術棧通信,使用iframe是成本最小,也是初期最合適的一種方式,這也是頁面復用的第一個階段:在子應用使用多個iframe容器嵌入工單系統中的訂單詳情頁。
圖片
所有訂單詳情頁的使用方只需要在本地創建iframe容器,然后在嵌入訂單詳情頁的訪問地址時傳一些必要的參數如訂單號、求購單號就能夠正常訪問了,十分便捷。還可以支持一些高級配置供使用方選用,如支持通過url query傳參的方式控制頁面樣式,當用于外部嵌入時隱藏主應用的菜單和頂部tab欄等等。
圖片
這個階段,初步解決了頁面復用的問題。但大家都知道iframe的弊端,一二線客服同學的日常工作中會出現大量的頁面切換,這樣一來,iframe的內存占用高、加載緩慢的缺點就被放大開來,這個階段一二線客服經常會反饋頁面卡頓,所以迫切需要進一步優化。
2、單實例iframe搭配MF遠程組件
對客服工單系統、章魚工作臺兩個平臺的用戶特點、作業行為進行分析,發現章魚工作臺的用戶對頁面的體驗要求更高,于是對詳情頁做了第一次優化,步入了第二個階段:將訂單詳情頁遷移至章魚工單工作臺,其構建方式也由Vite變更為Webpack5,子應用之間利用Webpack5的Module Federation特性通過遠程組件的方式進行數據通信。另一方面,盡管iframe的缺點明顯,但仍是跨技術棧應用間頁面通信的不錯選擇,將iframe控制在單實例容器中可以最大程度限制其對內存的占用。
圖片
這樣一來,詳情頁的入口雖然有10多個,但通信方式都收攏成了三種:遠程組件、單實例iframe、本地組件。所有場景都能覆蓋到,后續各個入口有復雜交互的變更或者自定義事件,都能夠在頁面主體做到監控和收口,不需要頁面的使用方做額外開發。
3、技術實現
3.1、單實例iframe通信
內容提供方
- 詳情接口響應后注冊message事件。
- 監聽iframe父級攜帶數據變化,更新本地頁面數據。
- 本地頁面交互事件被遠端觸發,發送當前的數據給遠端做自定義交互。
/** Vue3 */
/** 1. 詳情接口響應后注冊message事件 */
onMounted(() => {
/** 請求詳情接口 */
fetchOrderDetail(() => {
/** query上打上iframe標簽,用于確定注冊時機 */
route.query.iframeRoute && watchParentMessage()
})
})
/** 2. 監聽iframe父級攜帶數據變化,更新本地頁面數據 */
const watchParentMessage = () => {
window.addEventListener('message', event => {
const orderNo = event.data.data.orderNo
if (event.data.type === 'ORDER_CHANGE' && orderNo) {
/** 更新訂單信息*/
initStream(orderNo)
}
})
}
/** 3. 本地頁面交互事件被遠端觸發,發送當前的數據給遠端 */
window.parent.postMessage(
/** payload: 攜帶的數據*/
{
type: 'workbenchRoute',
params: {
/** 跳轉退貨詳情頁 */
name: 'refundDetail',
query: {
// 攜帶數據
},
},
},
/** orgin: 如果想要傳遞給任意窗口,可以將這個參數設置為'*' ,為了安全起見,不建議設置為'*'*/
'*'
)
內容使用方
- 頁面初始化時注冊message事件。
- 監聽本地訂單單號變化,將新的數據傳給遠端。
- 監聽遠端交互和數據變化,根據交互類型做不同的本地處理。
/** Vue2 */
/** 1. 頁面初始化時注冊message事件*/
mounted() {
window.addEventListener('message', this.callBack, false)
},
/** 2. 監聽本地訂單單號變化,將新的數據傳給遠端*/
watch: {
orderNo(newOrderNo) {
/** 在iframe的contentWindow屬性上掛載postMessage方法*/
detailIframeRef.contentWindow.postMessage(
/** payload: 攜帶的數據*/
{
type: 'ORDER_CHANGE',
data: {
orderNo:newOrderNo,
//其他數據
},
},
/** orgin: 如果想要傳遞給任意窗口,可以將這個參數設置為'*' ,為了安全起見,不建議設置為'*'*/
'*',
)
},
},
/** 3. 監聽遠端交互和數據變化,根據交互類型做不同的本地處理 */
method: {
/** callBack: 遠端事件被觸發后,處理本地回調邏輯 */
callBack(event) {
try {
if (event.data.type === 'workbenchRoute') {
switch (event.data.params.name) {
case 'orderdetail':
//跳轉訂單詳情的handler
break
case 'detail':
//跳轉工單詳情的handler
break
// 其他交互
default:
//兜底處理
}
}
} catch(error) {
//異常處理
}
}
內容提供方
- 配置webpack的MF插件,將整個訂單詳情頁exposes出去
- 維護詳情頁的props
/** 配置webpack MF插件,將訂單詳情頁exposes出去*/
new ModuleFederationPlugin({
filename: 'remoteEntry.js?[hash]',
library: { type: 'window', name: 'app_ticket' },
name: 'app_ticket',
shared: {
/** 需要共享的依賴 */
},
exposes: {
/** 提供訂單詳情遠程組件*/
'./OrderDetail': './src/views/orderdetail/Index.tsx',
},
}),
- webpack配置需要建議通信的遠端應用
- 使用defineAsyncComponent注冊組件
- 像本地組件一樣使用遠程組件
/** 1.webpack配置遠端應用 */
remotes: {
app_ticket: getRemoteUrl('app_ticket'),
},
/** 2. 使用defineAsyncComponent注冊組件*/
'OrderDetail': defineAsyncComponent(() => import('app_ticket/OrderDetail')),
/** 3. 像本地組件一樣使用遠程組件*/
<OrderDetail orderNo={orderNo} {...props} />
4、總結
頁面使用iframe的首屏耗時平均在7076ms,非首屏在2594ms,而MF的首屏只需要1279ms,非首屏更是只需428ms,渲染時間降低了6倍。
圖片
單個頁面的內存占用減少到了之前的1/10以內,關于模塊聯邦和遠程組件的更多細節可以查看Module Federation 在得物客服工單業務中的最佳實踐。
圖片
三、首屏秒開優化
上一章節說到使用MF的方式解決了架構層面的卡頓問題,但無緩存下頁面仍要2~3s甚至更久才能刷出訂單信息,這時要怎么辦?是的,可以改交互、拆接口。但如果數據依賴了大量外域服務、沒有外域產研資源介入,且要在一周時間做到有效的優化,那還能做些什么呢?
1、緩慢原因
由于一些歷史原因,客服訂單詳情頁需要同時展示100+的訂單信息,所有的訂單信息、訂單操作涉及的字段接近200個,而這么多字段其中90%都在一個http接口里面,這個大接口包含了36個dubbo接口,這些接口來自交易正向、逆向、供應鏈、商家、商品、用戶以及其他BU。并行的調用一定會出現短板效應,只要有一個接口RT(Reaction Time,響應時間)慢,就會拉慢整個http接口的響應速度,同時出現Timeout的概率也會上升,再加上頁面本身對資源的渲染時間,無緩存下仍要2~3s甚至更久才能刷出訂單信息。這個大接口的平均RT在500ms,P99線的RT達到了1.3s,下圖就是生產環境下某一次的調用詳情,耗時在782ms,降RT優化首屏渲染刻不容緩。
圖片
除了接口RT耗時高的問題,還有首屏接口并行調用的問題。90%的字段都在一個大接口里面,剩下10%都是在零零散散的一些小接口里,把這些小接口加起來頁面首屏接口超過6個。我們知道一個Chrome頁簽最多并行處理6個http請求,如果有第7個接口就會進入到Stalled(熄火)狀態,等待前面的某一個接口響應完畢后再發起請求,下圖是一個示例,getTrackTicketInfo接口是頁面首屏的第7個接口,255.14ms就是需要等待的時間。
圖片
根據上述問題現狀,初步的方案就是接口先聚合再拆分,把所有接口的數據聚合到一塊,然后再根據信息模塊拆分成若干個接口,前端再根據業務場景和用戶行為,去對拆分出來的接口的調用時機進行優化。但是,理想很豐滿,現實很骨感,數據量擺在這,很難在短時間去做到這件事情。光把字段梳理全,數據來源理清楚就用掉了兩天時間,再考慮到成本和收益后,我們的最終方案就是新增快慢接口,快接口的RT要在200ms以內,所有拖慢RT的數據都放到慢接口中,前端再根據接口的特性將所有接口分為2個梯隊在不同時間進行調用,最大程度的減少頁面的首屏渲染時間。
2、接口調用優化
2.1、技術方案
為了控制頁面并行請求接口數量和頁面數據渲染次數,將除了快慢接口之外,所有零散小接口分為如下兩個梯隊:不依賴詳情大接口反參的首屏信息接口;依賴詳情大接口反參的首屏信息接口、非首屏信息接口。
最終詳情頁首屏接口調用情況示意圖如下,在能夠聚合依賴大接口反參的首屏信息接口的情況下,頁面只會渲染兩次,第一次是在快詳情接口和第一梯隊接口請求回來之后(使用Promise.all控制數據的渲染時機),第二次就是在慢詳情接口請求回來之后。
圖片
使用Promise.all來保證快詳情接口和第一梯隊的接口信息同時渲染,減少頁面渲染次數,從而減少頁面抖動的情況。Promise.all有個缺點就是其中有一個Promise異常,整個就會拋出異常,所以需要對Promise.all包裹的Promise進行二次封裝,保證有一個Promise報錯不會干擾其他接口的請求,具體代碼實現方式如下:
/** 處理promise,保證promise.all使用時相互獨立 */
export const handlerPromise = (api, params) => {
return new Promise(resolve => {
api(params)
.then(resolve)
.catch(() => resolve({ error: true }))
})
}
/** 詳情頁首屏請求函數 */
const fetchOrderDetail = (callback?) => {
/** 使用handlerPromise封裝過的promise,保證有一個報錯不干擾其他接口請求 */
Promise.all([quickDetail(), firstLevel1(), firstLevel2()])
.then(([quickDetailData, firstLevelData1, firstLevelData2]) => {
// 快詳情接口
!quickDetailData?.error && quickDetailHandler(quickDetailData)
// 第一梯隊接口調用:不依賴詳情反參的首屏信息接口
!firstLevelData1?.error && firstLevelHandler1(firstLevelData1)
!firstLevelData2?.error && firstLevelHandler2(firstLevelData2)
// 執行回調
nextTick(callback)
})
.then(() => {
// 第二梯隊接口調用:依賴詳情反參的首屏信息接口、非首屏接口
secondLevelHandler1()
secondLevelHandler2()
})
// 執行慢接口
fetchSlowOrderDetail()
}
接口調用順序確認后還有一點需要注意,因為慢接口響應耗時較高,在客服同學快速查詢的工作場景中,可能會在慢接口還在pending,就已經切換到下一個訂單了,頁面單實例的場景下,這個時候如果不處理可能會將出現數據串臺的情況,不屬于該訂單的數據顯示了出來,關于這一點,需要在慢接口的handler上做如下處理。
/** 慢詳情接口請求 */
const fetchSlowOrderDetail = () => {
slowLoading.value = true
orderApi
.getDetail(params)
.then(slowData => {
/** 防止快速切換訂單導致的數據串臺問題 */
if (slowData?.topInfo?.orderNo === orderNo.value) {
orderDetail.value = slowData
}
slowLoading.value = false
})
.catch(() => {
slowLoading.value = false
})
}
2.2、最終效果
優化后的Waterfall圖就如下所示,不會再出現灰色的stalled耗時了,而且在228ms后首屏的數據就已經請求回來了。
圖片
3、大接口優化
上面一點解決了首屏接口的調用問題,接下來是對大詳情接口具體做的一些優化:
- 接口協議由POST改為GET請求,GET的總耗時是POST的三分之二;Chrome下如果檢測到GET請求的是靜態資源,則會緩存,如果兩次傳輸的數據相同,第二次以后耗費的時間將在10ms以內。另一方面也為后續工作臺引入Service Worker技術打下基礎。
- 新增快詳情接口,將大接口中的響應耗時較高的字段整理出來,快接口不再包含這些字段。這些高耗時的字段新增字段級別的loading效果,為了避免快慢接口耗時差異較大,導致一些經驗豐富的客服同學誤以為快接口沒返回數據的字段是空數據,但是這個loading數量不會超過3處,保持頁面的整潔易讀。
4、總結
經過上述優化,快詳情接口RT只需要平均190ms,從之前大接口的470ms下降了41%,首屏渲染時間從873ms降至376ms,下降了57%,95分位567ms,下降了62%。
圖片
首屏優化效果明顯,很難再看到反饋詳情頁加載緩慢的VOC了,一定程度地提升了客服的平臺體驗滿意度。
四、信息編排、模版插拔能力建設
解決了頁面卡頓和首屏加載慢的問題,但仍存在一些問題。這一次在產研運同學的通力協作下,如何進一步提升技術同學的開發體驗和客服同學的使用體驗呢。
1、仍面臨的問題
雖然詳情頁中堆疊的字段已經多達200處,隨著業務高速發展仍會存在部分信息缺失、不準確的情況,對客服日常作業產生了一定的負向影響。另一方面,在開發訂單類需求中,約60%的都是配合外域或者內部進行字段增刪改,如果建立了訂單信息的編排能力,后續字段類需求將可配置,從而能夠解放這部分需求的產研生產力,達到降本增效;同時若前端模塊能夠支持模塊拔插能力,也能為后續訂單信息模塊復用到坐席輔助及其他客服工作臺提供技術支撐。
2、信息編排能力建設
將訂單基本信息及關聯信息通過統一Schema維護。大家知道Schema(結構化的數據類型)只要約定的足夠復雜是可以用來描述所有場景的數據的,所以使用Schema第一步就是要控制好這個邊界,在能夠覆蓋大部分業務場景前提下不能太復雜。首先可將訂單信息做3層細分:信息塊、信息組、信息元素。做到配置信息塊、信息元素對于訂單詳情頁的場景來說都不太合適,所以這里選擇約定到信息組的格式。
圖片
2.1、Schema格式
上面圖示中兩組信息組可用下述Schema描述出來,利用數組的有序性,從左到右、從上到下對信息組進行渲染,實現訂單信息的編排配置能力。
schemaData : [
{
label: '訂單類型'
text:'普通現貨'
children: [
{
id: 'orderTypeDetail'
text: '詳情',
show: true,
toolType: 'linkBtn', /** linkBtn, primaryBtn, tag */
eventType: 'click', /** dbclick, hover*/
interactiveType: 'popover', /** modal, popover, message*/
children: [
//** popover彈出的內容 */
{ label: , text: '', children: //..}]
{ label: , text: ''}
]
},
{
text: '晚到必賠',
show: true
toolType: 'tag',
},
{
text: '退運服務',
show: true
toolType: 'tag',
},
]
},
{
id: 'tradeStatus'
label: '支付狀態'
text:'已經支付'
children: [
{
text: '七天風控',
show: true
toolType: 'tag',
},
]
}
],
使用Schema可以滿足大部分的字段渲染場景,但是對于一些復雜的交互和自定義的樣式仍需要前端去實現,這個時候每個信息元素中的id就發揮了作用,前端可以根據id去綁定交互事件和自定義樣式,具體實現如下:
/** 信息元素枚舉*/
enum INFO_ElEMENT_MAP {
/** 訂單類型詳情按鈕 */
ORDERTYPE_DETAIL: 'orderTypeDetail'
}
/** 信息元素*/
const infoElementMap = {
INFO_ElEMENT_MAP.ORDERTYPE_DETAIL: {
/** 綁定事件 */
onClick: () => {
orderTypeDetailClickHandler()
},
/** 綁定樣式 */
className: [styles.marginLeft],
},
/** 其他需要添加復雜交互和樣式*/
}
2.3、模版解析
約定好了Schema和規范,前端再編寫對應模版解析代碼去渲染頁面,對應渲染圖如下所示。
圖片
最外層的渲染器代碼如下:
const SchemaRender = () => {
//TODO 健壯性代碼
return (
<div>
{schemaData.length ? (
<Row>
{schemaTemplate.map(item => {
// 分隔符
if (item.key === TemplateKeyEnum.dividedLine) {
return <Divider />
}
return (
<Col span={12}>
<InfoItem
key={item.key}
label={item.label}
text={item.text}
infoList={item.children.map(child => {
return {
text: popoverRender(child),
hide: !child.show,
}
})}
/>
</Col>
)
})}
</Row>
) : (
<Skeleton title={false} active paragraph={{ rows: 3 }} />
)}
</div>
)
}
最終,Schema加上模版渲染就能渲染出訂單詳情頁的信息,后續此類型的需求除了和外域約定字段,就不再需要額外資源投入了。
3、模塊插拔能力建設
實現了信息快速編排,還有信息模塊高耦合的問題。其實不同角色的客服同學關注的信息是不一樣的,所以新的訂單詳情頁要根據客服的身份去展示不同的信息;而且隨著屏幕大小的不同,所適合布局也不同。另一方面,工單詳情、坐席輔助都需要展示訂單信息的某一個信息模塊(整個頁面展示就太重了),這時就需要訂單信息模塊有可插拔的能力了。
3.1、技術方案
初步方案是后端維護一個信息模塊池,提供出一個接口,前端通過傳一個標識,能夠返回對應標識需要的模塊組合,然后根據數據組合渲染數據。這個方案可以實現信息可拔插能力。
圖片
上述方案可以解決問題,也比較簡單,但是控制數據的還是前端,這其實違背了最初建設信息編排能力的初衷。于是最終改為后端同學從客服同學的登錄態拿到userId,根據id拿到其所在處理組,是買家處理組就返回買家版訂單信息,賣家版就返回賣家版訂單信息。另一方面,前端也根據屏幕大小做布局的自適應。
3.2、最終效果
- 大屏下頁面布局:
圖片
- 小屏下頁面布局(1440*900以下):
圖片
- 工單詳情使用訂單詳情中的物流記錄、服務記錄訂單信息模塊:
圖片
4、總結
從改版以來近8個迭代的資源投入數據來看,訂單需求開發成本降低了66.7%。
五、灰度和埋點方案
1、灰度方案
新版詳情頁改動較大需要根據客服所在處理組進行灰度,但是一線和二線的處理組分配又不太一樣,所以需要根據入口來源判斷使用哪套AB方案接口。另一方面,訂單詳情頁的入口非常多,所以在每個入口做灰度不太現實,改動較大,所以選擇收口到詳情頁主頁面區分新老頁面。
1.1、技術方案
- 根據來源區分使用一線AB方案還是二線AB方案,偽代碼如下:
/** 獲取來源區分IM、工單灰度組信息 */
watch(
() => props.platformCode,
code => {
try {
switch (code) {
case PLATFORM_TYPE.IM:
/** 一線灰度走一線灰度接口 */
isGray.value = true
break
case PLATFORM_TYPE.TICKET:
/** 二線灰度走二線灰度接口 */
isGray.value = true
break
default:
isGray.value = false
} catch {
isGray.value = false
}
},
{
immediate: true,
}
)
- 在主頁面根據灰度情況渲染詳情模版:
return () => isGray.value ? (
<>
<Button type="link" notallow={clickHandler} >
返回{isOld.value ? '新版' : '老版'}
</Button>
{isOld.value ? <OldDetail {...props} /> : <Detail {...props} />}
</>
) : <OldDetail {...props} />
1.2、總結
根據培訓進度開放灰度名單給客服使用新版頁面,同時對新老版頁面的數據進行監控 。支持可監控、可灰度、可回滾,確保了在頁面大改動情況下的系統質量穩定。
2、埋點方案
為了體現訂單信息優化的收益和價值,需要對客服同學在新老訂單詳情頁的停留時間、跳出訂單詳情頁次數進行比對。
- 訂單詳情頁停留時間:有效的停留時間越長一定程度能說明頁面的查閱費力度越高。
- 訂單詳情頁跳出率:跳出率越高說明當前訂單詳情信息不能滿足客服的查閱需求,需要去其他頁面查看。是信息不全、不清晰的一種體現。而且跳出頁面會重新加載新的頁面,等待時間會長于頁面內獲取信息,增加客服獲取信息的時間。
2.1、頁面停留時間
使用監聽路由的方法去計算頁面停留時間。這里只對其中一種做分析,其他兩種類似。
- 確定路由:https://xxx-xxx.xxx/orderdetail/:id
- 只需要考慮上一個路由是訂單頁面的情況
- 數據過濾:小于3s,大于30min視為無效數據
- 如果最近的路由是訂單頁面,則重置時間
/** 數據上報 */
const uplog = (current, last, constant, type) => {
/** b: 只用考慮上一個路由是訂單頁面的情況 */
if (constant === last) {
const nowTime = getNowTime()
const stayTime = nowTime - stayOrderDetailTime.currentTime
/** c: 小于3s,大于30min視為無效數據 */
if (stayTime > 3000 && stayTime < 1000 * 60 * 30) {
orderDuLog('ORDER_STAY_TIME', {
orderNo: orderNo.value,
stayTime,
type,
})
}
stayOrderDetailTime.currentTime = nowTime
}
/** d:如果最近的路由是訂單頁面,則重置時間*/
if (constant === current) {
stayOrderDetailTime.currentTime = getNowTime()
}
}
/** 監聽路由 */
watch(
() => {
return { name: route.name, id: route.params?.id }
},
throttle(
(currentRoute, lastRoute) => {
/** 從工單出發:只用考慮上一個路由是訂單頁面的情況 */
uplog(currentRoute?.name, lastRoute?.name, `${globalConfig.backstageCode}_orderdetail`, 'routeChange')
},
100,
{ leading: true, trailing: false }
),
{
deep: true,
immediate: true,
}
)
2.2、頁面跳出次數
頁面跳出次數就比較簡單了,只需要在跳出事件的handler里加上數據上報方法,比如查看商品詳情、退換貨詳情、用戶分期信息、工單詳情等等,最后計算跳出總量即可。
2.3、總結
老版頁面平均停留時間15.6s,新版頁面的平均停留時間8.5s,客服每次查詢信息的時長縮短7.1s,可根據詳情頁PV換算日均可降低客服同學工作時長。
圖片
同時,客服查詢特定訂單信息時需要跳到別的頁面查詢,這說明訂單信息是有缺失和或者客服是對信息準確性是有懷疑的,老版頁面跳出率11.4%(約8.7次訪問跳出1次),新版頁面跳出率7.92%(約12.6次訪問跳出1次)。客服查看訂單信息時跳出的率也下降3.48pp。
圖片
圖片
六、總結&規劃
1、總結
- 關于動態路由頁面的多平臺復用:跨技術棧使用單實例的iframe通信,配合雙向的postMessage事件監聽用戶行為觸發交互;微應用內使用Module Federation通信,在保障客服使用體驗的同時,節省了開發維護成本。
MF的首屏需要1279ms,非首屏只需428ms,渲染時間降低了6倍。
單個頁面的內存占用減少到了之前的1/10以內。
- 關于大數據量的頁面首屏優化:基于業務優化接口調用時機,保證同時不超過6個接口請求,避免出現Stalled耗時,對大接口RT進行優化,場景允許的話可改為GET協議類型,降低首屏響應時間,提升客服體驗。
首屏請求6個以上接口時不再出現Stalled耗時,大接口改為GET后總耗時是POST的2/3。
快詳情接口RT只需要平均190ms,從之前大接口的470ms下降了41%,首屏渲染時間從873ms降至376ms,下降了57%,95分位567ms,下降了62%。
關于信息編排、模塊拔插能力建設:根據業務分析字段特點,約定合適的Schema格式使得信息內容可靈活配置;對用戶的職能特點、設備情況進行分析,使得用戶所訪問頁面的布局、內容做到區分,做到給用戶看到合適的內容,降低坐席的查詢信息的費力度。
訂單需求開發成本能夠降低66.7%。
老版頁面平均停留時間15.6s,新版頁面的平均停留時間8.5s,客服每次查詢信息的時長縮短7.1s,根據訂單詳情頁的PV可換算出每日可降低客服同學的查詢時長。
老版頁面跳出率11.4%(約8.7次訪問跳出1次),新版頁面跳出率7.92%(約12.6次訪問跳出1次)。客服查看訂單信息時跳出率下降了3.48pp。
2、后續規劃
雖然客服訂單詳情頁的使用體驗已經得到提升,但是體驗升級之路仍在繼續:
- 模塊聯邦雖然很快,但是對公共依賴維護成本較大,也會導致應用構建速度下降。后續會基于無界對訂單子應用進行遷移,建設專門存放遠程組件的應用容器,提升子應用的秒開和快速切換體驗,同時也能提升工單子應用構建速率,解耦橫向訂單功能的發布。可期待后續內容。