每年的云音樂年度聽歌報告,就像一個靠譜的老朋友,總會在忙碌一年的時光盡頭里叩響記憶的大門。
首次訪問年報活動為例,好的用戶體驗主要包括以下幾個方面:
圖片
簡而言之,體驗優涉及以下幾個方面:
1、頁面導航:包括首屏秒開,頁面轉場等
2、資源管理:包括圖片、視頻、音頻和字體包等
3、頁面適配:包括文本適配、動效適配和機型適配等
首屏秒開是指用戶從點擊鏈接開始到展示頁面內容大約在1s左右完成。整個過程經歷如圖所示:
容器初始化 -> CDN -> TCP 建連 -> html/js/css 加載解析 -> DOM / CSSOM 解析 -> 渲染布局 -> 繪制
圖片
對于前端開發來說,優化難度是從右到左,越到左邊就越需要跨團隊合作來完成。可以簡單的分析總結:
為了有效管控年報中多個視圖的高效展示和切換,采用了 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、懶加載 lazy、緩存 cache
圖片資源占整個年報項目資源中的比例是最高的,大概 70% 左右。所以圖片展示速度是否足夠快和內容是否完整都會直接影響用戶體驗。
優先將圖片資源使用 tinypng 進行手動壓縮,在不失真的情況下,保證圖片大小足夠小。其次正確選擇圖片格式能有效減少圖片大小。其中圖片格式很多,主流的有:
不同圖片格式在不同場景上使用。格式沒選準確,會導致資源浪的費。如在「年度總覽」一頁中,海浪
本文鏈接:http://www.www897cc.com/showinfo-26-99645-0.html云音樂2023年報前端大揭秘
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com