云音樂2023年報前端大揭秘
前言
每年的云音樂年度聽歌報告,就像一個靠譜的老朋友,總會在忙碌一年的時光盡頭里叩響記憶的大門。
- 如果你曾錯過了彼時的年報,不妨現在就拿起手機掃描上圖的二維碼,與往期的精彩視聽來一場不期而遇的邂逅;
- 如果你為年報中精巧而溫馨的動畫而深深著迷,請直接移步至本文的姊妹篇:「云音樂2023年報動效大揭秘」一探究竟;
- 如果你恰好想了解年報中前端開發承擔了什么樣的角色、積累了哪些最佳實踐 ——那么巧了,本文將從性能體驗、質量管理、工程效率和一些筆者底層思考,幫助你逐步揭開年報的神秘面紗
性能體驗
首次訪問年報活動為例,好的用戶體驗主要包括以下幾個方面:
- 頁面能秒級打開,頁面到達率高、流失少;
- 頁面間轉場展示流暢,不會出現卡頓;
- 文本和圖片內容在任何設備上都能完整展示,不出現缺失或者加載閃爍情況;
- 音頻或視頻啟播速度快,播放過程中不發生錯亂或者卡頓情況;
- 頁面內的動效展示流暢,不會出現卡頓;
圖片
簡而言之,體驗優涉及以下幾個方面:
1、頁面導航:包括首屏秒開,頁面轉場等
2、資源管理:包括圖片、視頻、音頻和字體包等
3、頁面適配:包括文本適配、動效適配和機型適配等
首屏秒開
首屏秒開是指用戶從點擊鏈接開始到展示頁面內容大約在1s左右完成。整個過程經歷如圖所示:
容器初始化 -> CDN -> TCP 建連 -> html/js/css 加載解析 -> DOM / CSSOM 解析 -> 渲染布局 -> 繪制
圖片
對于前端開發來說,優化難度是從右到左,越到左邊就越需要跨團隊合作來完成。可以簡單的分析總結:
- 在渲染/繪制階段,可以隔離狀態變化頻繁的組件,減少無效狀態引起的繪制。盡量選擇由 GPU 渲染的 CSS3 來實現動效。邏輯實現的動效建議使用GSAP。
- 在 DOM / CSSDOM 解析階段,可以減少 DOM 的嵌套深度,減少使用 JavaScript 直接修改元素樣式,減少不必要的 CSS,減少使用 CSS 選擇器等。
- 在 HTML / JS / CSS 加載解析階段,可以通過構件工具對文件進行壓縮。利用離線包能力,將這些資源提前下載到本地。
- 在 CDN、TCP 建連階段,更多依賴 App 網絡庫底層的優化,如使用 HTPP/2 減少 TCP 連接數。
- 在容器初始化階段,為了達到極致的打開速度,可以將 H5 容器進行預初始化。也可以建立容器復用池,減少容器創建的耗時。
頁面管理
頁面路由
為了有效管控年報中多個視圖的高效展示和切換,采用了 SPA[1] 的形式對H5進行組織,同時為頁面路由提供了路由表的配置。
路由表簡單理解是各個子頁面路由對象的集合,這個集合可以是全局數組對象,本地文件,或者服務端下發的配置。集合內的順序決定了用戶看到報告頁順序。這樣也可以根據產品的述求,靈活調整集合內的順序,這樣就能動態調整頁面順序了。
各個子頁面路由對象的屬性具體如下。建議不用傳統的 path,因為子頁面數量多,且名稱很難記憶,可以使用 routerIndex,這是子頁面在交互稿中的位置代號,方便開發和調試。其中 ignoreSwipe 是使用在下節手勢處理中「子容器接管父容器手勢」的場景。
export interface PageProps {
model: unknown;
position?: number; // 頁面埋點使用
}
export interface PageRouteProps {
c: ComponentType<PageProps>;
cId: string; // 當前頁面唯一id
routerIndex: number | string; // 路由索引
ignoreSwipe?: boolean; // 是否忽略滑動,默認false
}
手勢處理
年報項目中用戶可以通過點擊、左右滑動、上下滑動來切換頁面。為了防止手勢沖突,全局只有一個父容器,各個報告頁面是子容器;一些手勢頻控、頁面狀態變化等通用邏輯統一在父容器中實現。只在父容器中使用 hammerjs 做手勢監聽,子容器不再負責頁面切換的手勢監聽,關鍵代碼如下。
hammer = new Hammer(reportRef.current);
hammer.get('swipe').set({ direction: Hammer.DIRECTION_ALL });
hammer.on('swipeleft', onNextPageWrap);
hammer.on('swiperight', onPrePageWrap);
hammer.on('swipeup', onNextPage);
hammer.on('swipedown', onPrePage);
對于子容器,可能會有以下幾種特殊情況:
情況一:如果子容器需要感知用戶手勢事件,可以監聽父容器發出的自定義事件。
// 【翻頁】動效:下一頁
export const ON_PAGE_NEXT = 'ON_PAGE_NEXT';
// 【翻頁】動效:上一頁
export const ON_PAGE_PREV = 'ON_PAGE_PREV';
情況二:如果子容器需要完全接管父容器的手勢監聽事件,例如年度歌手相關的所有頁面。首先需要在路由表中將 ignoreSwipe=ture 設置忽略手勢,然后在監聽父容器發出的通知,進行自定義的事件處理。最后當子容器接管結束后,需要根據需要再次觸發父容器的切換事件。關鍵代碼如下:
const onNextPage = useCallback(
(nextAction) => {
if (currentPage >= len - 1) {
nextAction(); // 結束接管,再次觸發父容器的切換事件
return;
}
setCurrentPage(currentPage + 1);
},
[currentPage, len]
);
useEffect(() => {
bus.on(ON_PAGE_NEXT, onNextPage);
return () => {
bus.off(ON_PAGE_NEXT, onNextPage);
};
}, [onNextPage, onPrePage]);
情況三:如果子容器存在特定區域需要響應特點手勢,例如歌手來信頁面左右切換是查看歌手來信。這時候需要子容器調用stopPropagation主動阻止手勢向父容器傳遞。
轉場實現
在路由表和手勢處理準備就緒后,最后來看看頁面之間轉場的實現。年報頁面的轉場效果使用React官方實現的 react-transition-group 組件。由于篇幅限制,這里不詳細介紹react-transition-group 組件的底層原理。結合路由表順序和當前頁面位置,通過 z-index 和 match 來控制子頁面的層級和顯示隱藏。使用 CSSTransition 實現頁面間的進場和退場CSS動畫。關鍵代碼如下:
(pages || []).map((item, index) => {
// zIndex 和 match是關鍵代碼
const zIndex = (pages.length - index) * 100;
const match = index === currentIdx;
return (
<div
key={item.cId}
style={{
zIndex,
pointerEvents: match ? 'auto' : 'none',
overflow: 'hidden',
}}>
<CSSTransition
in={match}
timeout={100}
...
appear
unmountOnExit>
<item.c
model={item.model}
positinotallow={index} />
</CSSTransition>
</div>
);
})
但是以上的頁面轉場存在一個問題,即頁面之間只能存在一種轉場方式。如何自定義頁面之間的轉場效果?基本思路是各自頁面維護自己的轉場效果;大部分頁面只需將轉場信息配置到路由表中;小部分頁面可以通過頁面上下文獲得上一個或者下一個頁面的信息,動態決定如何進場或者退場。基于該思路,改造路由表對象,新增一個 TransitionParams 協議,并且提供漸隱漸顯的默認轉場實現。關鍵代碼如下:
export type TransitionParams = {
timeout: number | { appear?: number | undefined; enter?: number | undefined; exit?: number | undefined };
classNames: CSSTransitionClassNames;
};
export interface PageRouteProps {
c: ComponentType<PageProps>;
cId: string; // 當前頁面唯一id
routerIndex: number; // 路由索引
transition: TransitionParams; //默認是漸隱漸顯的轉場效果
ignoreSwipe?: boolean; // 是否忽略滑動,默認false
}
CSSTransition 配合改造后的關鍵代碼如下:
<CSSTransition
in={match}
timeout={item.transition.timeout}
// 關鍵代碼
classNames={item.transition.classNames}
appear
unmountOnExit>
<item.c model={item.model} positinotallow={index} />
</CSSTransition>
資源管理
資源管理主要任務是將網絡資源下載到本地,最終將本地資源加載到內存,以便程序可以使用這些資源。優化資源管理可以有效提高年報用戶體驗。通常開發者會通過壓縮資源的大小,并通過內容分發網絡(CDN)加快資源的下載速度。但是除了這些還有其他通用的方法呢?
在介紹具體優化手段前,先來看以下關鍵字,這些是性能優化的通用方法。
- 提前 preload:提前準備必要的資源,提升加載速度
- 同步 sync:串行執行當前任務,確保執行任務的優先級
- 異步 async:工作線程異步執行,處理比較耗時的操作,不阻塞主線程
- 懶加載 lazy:又稱按需加載,不浪費請求
- 緩存 cache:將資源緩存到內存或者磁盤本地,減少不必要的網絡請求
- 延遲 defer:不立即執行任務,延遲執行
總結如下圖:
圖片
接下來,將從圖片、視頻、字體包等各個資源,進一步解析如何結合上述關鍵詞進行優化。
圖片
關鍵字:提前 preload、懶加載 lazy、緩存 cache
圖片資源占整個年報項目資源中的比例是最高的,大概 70% 左右。所以圖片展示速度是否足夠快和內容是否完整都會直接影響用戶體驗。
優先將圖片資源使用 tinypng 進行手動壓縮,在不失真的情況下,保證圖片大小足夠小。其次正確選擇圖片格式能有效減少圖片大小。其中圖片格式很多,主流的有:
- SVG 是基于XML的矢量圖片格式,不失真無限放大。支持動畫。
- JPEG 是有損壓縮,不支持透明度或者動畫。
- PNG 是無損壓縮,支持透明度。APNG 是 PNG 的擴展,支持動胡奧。
- WebP 是無損和有損壓縮,支持動畫或者透明度。
- GIF 是位圖圖片格式,支持動畫。
不同圖片格式在不同場景上使用。格式沒選準確,會導致資源浪的費。如在「年度總覽」一頁中,海浪??背景是一張PNG格式,大小為 1.7MB。但是該場景不需要透明,選擇JPEG后大小為 96kb。總結如下是選擇圖片格式的流程圖。
圖片
小圖標或 logo 可以使用 SVG。其它能用 WebP 盡量使用 WebP。對于 WebP 的兼容性問題,可以通過業務封裝的圖片組件進行處理,不能使用 WebP 則兜底變成 PNG ,因為 PNG 兼容性最好。GIF 盡量不適用。對于超過特定大小的動圖,建議使用CSS動效或者視頻替代。
在已經壓縮圖片和選擇正確的圖片格式的前提下,會在當前報告頁面提前 preload 預加載下一頁面的圖片資源,并將圖片緩存cache在本地。
預下載的方式有多種,可以自動全量下載,也可以手動按需下載。
自動全量下載,可以基于上文 CSSTransition 的 in 參數。不只是匹配當前頁面進行渲染,也提前渲染下一頁。自動下載的方案存在缺點比較明顯,如:不能按需下載;頁面的生命周期和用戶感知不一致,導致一些邏輯提前執行如頁面曝光埋點。
let matchIndex = -1;
(pages || []).map((item, index) => {
const zIndex = (pages.length - index) * 100;
const match = index === currentIdx;
if (match) {
matchIndex = index;
}
return (
<div
key={item.cId}
style={{
zIndex,
pointerEvents: match ? 'auto' : 'none',
overflow: 'hidden',
}}>
<CSSTransition
// in 這里是關鍵代碼
in={match || (index === matchIndex + 1)}
timeout={item.transition.timeout}
classNames={item.transition.classNames}
appear
unmountOnExit>
<item.c model={item.model} positinotallow={index} />
</CSSTransition>
</div>
);
})
相較自動下載,手動按需下載更加可控。手動按需下載那些圖片可以選擇更接近用戶體感的 LCP(Largest Contentful Paint)原則。因為在業務頁面加載階段,命中 LCP 的元素可能會發生變化,所以這里主觀選擇可能命中 LCP 的圖片元素進行預下載。如下圖,最終選擇A、B、C、D這個四張圖片進行預加載。
圖片
手動下載的方法有多種。可以基于三方庫 pxloader 進行再一次封裝。如果將 React 升級到19后,系統默認提供了 preload API,更多API詳情見此鏈接[2]。
import { preload } from 'react-dom'
// 下載字體包
preload('https://.../path/to/font.woff', { as: 'font' })
// 下載樣式表
preload('https://.../path/to/stylesheet.css', { as: 'style' })
// 下載不知道的文件類型
prefetchDNS('https://...')
手動下載的時機可以監聽頁面切換,根據當前頁面信息在路由表中獲取到下一個需要預加載的頁面信息。最終手動下載的偽代碼如下:
const preload = new PreLoader();
const usePreLoader = ({ pages, currentIdx }: PreLoadSourceProps): void => {
useEffect(() => {
const preLoadIndex = currentIdx + 1; // 提前預加載
const item = pages[preLoadIndex];
if (item.cId === YearOverviewIdentifier) {
preload.add([A, B]);
preload.start();
}
...
}, [currentIdx, pages, reportInfo]);
};
視頻
關鍵字:提前preload、同步sync
和圖片同樣的思路,先明確視頻的尺寸,不同尺寸大小的視頻資源大小也不一樣。在視頻尺寸正確的前提下,在做視頻體驗優化才能事半功倍。在項目初期準備 6 種視頻,相同視頻內容,網絡環境下,用不同系統的機型進行壓測。這 6 種視頻,分別是:
- 寬1242、高2688、FPS50
- 寬1242、高2688、FPS25
- 寬1080、高2388、FPS50
- 寬1080、高2388、FPS25
- 寬720、高1625、FPS50
- 寬720、高1625、FPS25
壓測得出的實驗結論是:
- 安卓 10-13,分辨率高于 2400 的 6 種視頻大小都能體驗,但寬1242 & 高2688 尺寸的視頻基本上會有卡頓;
- 安卓 10-13,分辨率低于 2400,寬1242 & 高2688 該尺寸的視頻無法進行體驗;
- 安卓 10 以下的,只能體驗寬720 & 高1624 該尺寸下的視頻。
- iOS 系統 13-16,體驗都正常,iOS12 系統,寬1242 & 高2688 & FPS50 和寬1080 & 高2338 & FPS50無法進行體驗。
最后根據實驗結論,結合視頻效果和資源大小的考慮,最終采用三種尺寸的視頻,分別是:
- 寬1080、高2388、FPS50,命名為 w1080FPS50
- 寬720、高1625、FPS50,命名為 w720FPS50
- 寬720、高1625、FPS25,命名為 w720FPS25
總結如下是選擇視頻尺寸大小的流程圖。w1080FPS50 體驗最優,w720FPS25 兼容性最好。開發可以根據不同的機型、系統選擇合適的視頻。
圖片
為了讓用戶在點擊封面頁面的開始按鈕后能流暢觀看視頻,選擇在封面頁面渲染完成后,同步 sync 添加視頻頁面。通過 zIndex 將其隱藏在封面頁后面,利用用戶游覽封面頁的間隙,提前 preload 創建 video 組件。同時,設置<video>的 preload='auto',讓游覽器結合網絡等自身條件自動決策是否預加載視頻。
即使選擇好合適的視頻資源后,也需要兜底處理一些播放的異常情況,防止播放失敗影響體驗。可以監聽onWaiting回調,如果在規定時間內如果沒有再次觸發onPlay回調,會直接手動執行onEnd回調的事件。也可以在onCanPlay回調中啟動視頻超時定時器,在規定范圍內系統沒有自動觸發到onEnd回調,那也會直接手動執行onEnd回調的事件。
音頻、字體包
關鍵字:延遲defer,懶加載lazy
音頻在工程中通常有兩種播放能力:WebAudio H5 原生播放能力和通過RPC方式調用客戶端原生播放能力。
在站內場景中,通常推薦使用客戶端 RPC 提供的播放能力,因為它可以復用端上播放的基礎能力,如播放音質、啟播時長等都有優化。如果站內想要繞過某些播放權限,會選擇 H5 原生播放能力。而在站外,只能使用H5原生播放能力。為了簡化開發接入,封裝 audioManager 業務組件,提供統一的API接口,抹平站內、站外的兼容性問題。
對于字體包,通常會懶加載 lazy 選擇視覺設計中使用到的字體包。針對固定文案的情況下,使用工具裁剪掉多余的字體,從而達到最小字體包。也會在 header 中延遲 defer 下載字體包的資源,以避免阻塞封面頁的渲染。在封面頁面不使用特殊字體包的前提下,可以在封面頁面設置不可見的<p>標簽,靜默下載字體包,從而讓后續報告頁面里的特殊字體不出現跳變。
小結
可以組合使用不同的優化手段來提高性能,但切記物極必反。例如在年報項目開發過程中,為了實現歌手子頁面轉場的效果,會將 6 個子頁面提前加載。在開發早期,各個頁面渲染復雜度低,不會出現問題。但在后續為了提高視覺效果,加入了各種噪點、混合、粒子效果后,在低端機設備上,該模塊在切換時容易出現白屏現象。最后定位是子頁面提前預加載過多,內存占用大,使得端上收到 OOM 警告,回收 H5 容器,從而導致了白屏。最后,采用懶加載 lazy 加載,同一時間最多顯示 3 個頁面,這樣既保證子頁面轉場的流暢,也保證了功能的正常使用。
頁面適配
用戶手機機型千變萬化,頁面適配是必須要面對的問題。開發之前先明確兩個關鍵的點:站內是否支持全屏;站外是否需要支持。明確后結合工程現狀,將需要適配的重點機型進行枚舉,并歸納總結,以 iPhone 機型為例如下:
圖片
如果只適配站內全屏/非全屏,建議將適配的機型寬高比控制在 2 個以內。舉例年報只支持站內全屏,如下圖第二列所示,可以將站內適配劃分成 2 檔,通過媒體查詢,所以只需支持寬高比大于等于 375 / 667,適配范圍支持站內寬屏或者小屏的機型。針對特殊頁面,還可以配 合max-height 再做微調。
@media screen and (min-aspect-ratio: 375 / 667) {
// 大于等于 0.5622
// 適配范圍:站內寬屏、特殊小屏的機型
}
@media screen and (max-height: 750px) {
}
如果需要適配站外,建議將適配的機型寬高比控制在 4 個以內。如下圖第三列所示,分析歷史站外流量分布,重點支持微信、微博等 App。可以將站內、外適配劃分成四檔。第一檔需支持寬高比大于等于 400 / 815 且小于 375 / 667,適配范圍支持15pro、12promax、15promax、vivo s9 等站外+大屏。第二檔需支持寬高比大于等于 375 / 667 且小于 375 / 603,適配范圍支持 8p、SE 等站內+寬屏、小屏。第三檔需支持寬高比大于等于 375 / 603,適配范圍支持8p、SE 等站外、寬屏+小屏。
@media screen and (min-aspect-ratio: 400 / 815) and (max-aspect-ratio: 374 / 667) {
// 大于等于 0.4907,小于等于 0.5607
// 適配范圍:15pro、12promax、15promax、vivo s9 等 站外+大屏
}
@media screen and (min-aspect-ratio: 375 / 667) and (max-aspect-ratio: 374 / 603) {
// 大于等于 0.5622,小于0.6218
// 適配范圍:8p、SE 等 站內+寬屏、小屏
}
@media screen and (min-aspect-ratio: 375 / 603) {
// 大于等于 0.6218
// 適配范圍:8p、SE 等 站外、寬屏+小屏
}
質量監控
面向年報這種大型活動,針對質量問題的感知不能僅被動通過用戶反饋問題,更重要的是需要一套完善的監控系統自動發現問題。通過這套系統也能夠有效評估出線上運行的狀態,最后用數據來驗證“質量優”這個技術指標。
對于開發者而言,質量優的背后實質是高可用。需要對各種異常結果進行兜底,來保證用戶完整體驗完年報活動。簡而言之,質量優涉及以下幾個方面:
1、異常監控:包括 crash 平臺、實時數據監控平臺,離線日志回撈平臺等
2、功能降級:包括 AB 配置中心,上線 SOP 白皮書,活動研發平臺等
3、數據準確:包括異常數據兜底,敏感數據過濾,埋點數據等
異常監控
除了必備的異常 crash 監控平臺,還需要根據年報活動的特性來構建一個更全面的監控體系。主要包括分享率監控、視頻卡頓監控、音頻啟播失敗監控、接口異常監控等。由于監控的多樣性,需要根據不同的場景采用不同的監控策略。例如,如果想觀察分享點擊率,需要實時上報監控數據;如果需要排查頁面白屏問題,需要收集用戶本地的離線數據。由于年報日活高,樣本數據量大,需要考慮是否需要采樣上報。建議實時數據采用一定的采樣率,而離線數據則采全量上報。
監控的思路是:現狀分析,指標建設,監控埋點,指標分析,方案優化,AB 實驗,驗證收益。PACD 的方式持續改進,直到問題被解決。
以分享率監控為例。活動分享率要達到xx%是年報關鍵指標。分析工程中現狀后,制定如下圖的監控指標。
圖片
結合線上埋點數據,能夠清晰發現圖片生成耗時和圖片生成成功率對圖片分享成功率有正向影響。因此,后續開發的重點就是如何降低圖片耗時生成和提高圖片生成的成功率。
功能降級
監控的本質是及時發現問題并快速止損。在面對突發情況時,通過線上配置快速關閉某些非核心功能,以確保核心功能的正常進行。在開發中過程中,可以將降級開關集中配置在一個能進行可視化的活動研發平臺內統一管理,如下圖。在上線前,需要將這些開關納入上線前的標準操作流程(SOP)中。
圖片
下圖是年報的監控大盤。對可能嚴重影響用戶體驗的功能,如白屏、視頻和音頻進行了重點監控,同時也對關鍵業務模塊,如報告頁面和歌手來信進行了監控。這些監控在年報活動也發揮了重要的作用。例如,通過對報告頁面接口的監控,發現由于 cookie 丟失導致用戶進入不了年報的問題;通過對音頻播放的監控,明確了站內需要支持音視頻的自動播放。
數據準確
最后數據準確性也是質量優的重要組成部分。其中需要關注兩個方面。首先,需要確保用戶界面的數據準確無誤,這包括對服務端可能出現的異常情況進行兜底處理,例如服務端下發數據失敗或者下發敏感數據等情況需。其次,要確保上報后臺的埋點數據不會漏報、重復報或者缺失關鍵信息。這些埋點數據的準確性直接影響策劃和開發的實時決策。
工程效率
除了體驗好、質量優,效率高也是開發一直追求的命題。
對于策劃或者視覺來說,需求的頻繁變更是項目最大的風險。這要求開發團隊能夠預見并總結可能出現變更的情況。例如,頁面順序會根據內測用戶的反饋進行調整,頁面數量可能會根據線上頁面的流失率進行刪除。在例如,視覺要求頁面之間有統一的轉場效果,但是針對某些特殊頁面,要求工程應該能夠支持自定義的轉場效果。
對于 QA 或者客服來說,當用戶反饋問題時,他們應能夠通過快捷方式查看該用戶的年報。同時,由于年報通常包含 30+ 頁面,他們希望能有一種快速定位到特定頁面的方法。通過這種方式,他們可以自助定位并解決部分線上客訴。在排除了用戶自身、數倉或者服務端的問題后,再將問題提到前端。
對于開發者來說,期望有一套開箱即用的基礎組件,如圖片、視頻、音頻等。或者能夠在項目創建初期,就能有一個包含基礎交互、數據模型和常用基礎組件的年報模板。更進一步是,希望在開發過程使用使用D2C的插件,通過視覺稿直接生成代碼。更終極的狀態是,策劃和運營通過搭建平臺能自助地構建出年報項目。
圖片
簡而言之,效率高涉及以下幾個方面:
1、頁面導航:包括頁面自動曝光、支持動態路由等
2、功能調試:包括年報快捷查看,頁面快速定位等
3、工程基建:包括通用組件/能力,年報模板,D2C 插件,搭建平臺等
為了實現各個活動之間的能力復用,可以將活動成果都會統一沉淀到活動研發平臺。
這個平臺覆蓋了活動的完整生命周期,包括開發前期需要參考的技術方案,UI組件,通用能力,活動模板,開發中期需要的功能開關、分享內容等配置,以及開發后期需要用到保障SOP和監控告警等。
圖片
后續在年報項目的初期階段,計劃通過活動研發平臺的年報模板工程,快速搭建出年報的項目。基于這個初始化工程,數據開發團隊能盡早進行的自測稽查。
深度思考
最終年報項目按照預期順利拿到業務結果。再次回顧立項初期,開發并不是簡單被動接收目標,簡單的將需求實現。可以從業務和技術關系分析出發,定性、定量分析項目目標是否能夠達成。
關系分析
業務和技術的關系可以劃分為三大類別:業務驅動技術、技術保障業務、技術引領業務。
業務驅動技術的一個典型例子是2020年報[3]中的DIY音樂人物形象。這是開發第一次在大型項目中使用 three.js。項目的初期,需要做大量的技術調研,并需要快速解決一些從未碰過的難題。項目的過程中,由于技術局限性,策劃不得不妥協,對需求進行調整。在業務驅動技術的情況下,大部分情況是未知的,這會使得開發處于高度緊張的狀態,也可能導致技術動作的變型。
技術引領業務的一個典型例子是性格POP[4]。在這個項目中,開發主導了整過程,他們基于矩形樹圖算法實現一個無縫擠壓的動效。策劃團隊根據這個特定的動效,定制業務需求。在技術引領業務的情況下,由于整個過程因為都是已知的,開發都是胸有成竹的狀態,這通常會使業務的收益超出預期。
大部分的需求都是屬于技術保障業務的情況。這要求開發對當前的工程能力有清晰的認知,并協助策劃做出最合理的決策。開發需要科學分析業務目標是否能達成,可以有效地管理策劃的心理預期。
圖片
需要多積累技術知識,減少業務驅動技術的被動情況出現。同時也需要提升對業務的敏銳度,讓技術引領業務的發展。最后將知識和經驗沉淀,從而更好保障業務的需求。
定量分析
2023年報就是典型的技術保障業務的場景。在業務立項初期,產品目標是日活xx千萬,活動分享率 xx%。
圖片
項目初期項目負責人不應埋頭搬磚,需要先通過現有數據,理性分析業務目標是否能達成。
圖片
分析顯示,目前 Android 和 iOS 雙端的日活累計是X千萬。這個數字實際設定了年報日活躍用戶的上限。如果這個上限都不能滿足業務目標,那開發需要尋找更多的推廣渠道。除了現有的移動端設備,可以考慮其他有多終端設備,如PC、iPad和TV,還可以考慮支持例如微信等站外渠道。這樣開發階段,需要重點考慮如何適配這些不同的用戶界面。
在確定投放渠道之后,可以進一步分析每個渠道內的投放位置。例如,在移動端通常會選擇在首頁首幀 banner、首頁彈窗、以及其他高日活的頁面進行投放。細化分析每個位置的用戶點擊率,最終能初步預估活動的日活。在投放位置有限情況,如果這個預估值無法達到業務目標,那么需要額外開發其它點擊率較高的位置,如啟動開屏位,供策劃選擇。
在不同角色中,對分享率這個指標的定義可能會有所不同。因此,在項目啟動的前期,開發需要和策劃達成一致,明確如何統計活動的分享率。在明確統計口徑后,分析現有的分享行為,包括圖片分享、鏈接分享、口令分享和截屏分享。需要注意,截屏分享是最容易忽略統計的一種分享行為。接下來,分析不同分享行的流程。如下圖所示列出了不同分享行為所需要經過的路徑。
圖片
在這些路徑下進行數據埋點,以便能夠觀察和分析影響分享率的漏斗。通過結合對漏斗的專項治理和過往活動的數據,最終也能準確預估年報的數據。
圖片
定性分析
日活和分享率很大程度取決于年報的產品創意是否足夠新穎。產品創意需要將用戶的聽歌數據以字體、圖片、動效、音頻、視頻等多種方式準確地展示給用戶,從而引發用戶的共鳴。而作為開發者,需要以流暢、準確、且高效的方式將這些多元的媒體內容傳達給到用戶。
案例一:得益于今年年報對體驗和質量的高要求,上線后 5 日總客訴量比往年減少了 41%,收獲了大批用戶好評。
圖片
案例二:首頁banner投放,從用戶點擊到 H5 頁面打開,頁面達到率 Android 端只有93%左右。假設將頁面到達率從 93% 優化到 97% 以上,對年報帶來收益大概是是新增上百萬的日活用戶。
因此,好的用戶體驗和高的產品質量能夠有效地促進年報的分享,從而進一步提升日活躍用戶數量。
總結
一款深入人心的作品,不僅要有優質的產品內容支撐,有效的技術優化和保障也不可或缺,具體總結如下圖:
圖片
最后本文從2023年度報告中的性能體驗、質量監控和工程效率等方面出發,介紹了筆者在年報項目經歷中的一些探索和經驗。
參考資料
[1]SPA: https://developer.mozilla.org/en-US/docs/Glossary/SPA
[2]更多API詳情見此鏈接: https://react.dev/reference/react-dom#resource-preloading-apis
[3]2020年報: https://st.music.163.com/c/year2020
[4]性格POP: https://sg.music.163.com/jia/begin/show