Lath(純前端容器)打造頁面間的無縫平滑連線體驗

語言: CN / TW / HK

前端效能現狀

提到前端你最先想到什麼?前端工程?web?JavaScript、CSS、HTML?

如果站在使用者的視角那是他們距離資訊最近的地方,無論前端在生產技術上如何演變,最終服務於資訊表達的根本不會變。 在網際網路誕生的初期就已經存在前端技術了,起初它就和傳真沒有什麼區別,只能表達一些簡單的基礎資訊,但隨著瀏覽器不斷的技術演變,如今的前端領域已是擁有超級複雜的資訊表達系統了。

如今的前端除了可輕鬆構建起豐富的資訊表達形式,更具有可共享、易更新、多平臺執行等等的優點,隨著生態體量的不斷壯大,前端技術也逐漸慢慢滲透到了其它終端領域。比如在移動應用中使用 Hybrid 混合技術,通過結合前端技術與原生技術來同時兼顧與獲得兩者的優勢,以及在另外的領域通過削減複雜度和實現支援部分 API 來使前端達到更高效能的方案,比如 RN、miniApp 等。

資料統計約 70% 的移動 App 中都內建了 webview/混合的技術方案,主要進行能力供給與 Web 頁面的管理。恰如其名、web 頁面以單位“頁面”進行度量,可見其在構建大型應用時的侷限性,就如紙張一般,可以肆意的書畫其內容,但是脫離紙張之外就無能無力了,因而才需要一個“導航器”將紙張進行串聯成冊以進行管理,瀏覽器的基礎功能也恰是如此了。

與原生技術的效能差異是什麼?

使用混合或轉換技術來替代純前端技術產品是有違於網際網路開放與共享本質的,而究其原因,為什麼而不得不採用混合技術?原因主要有兩個:1 是可提供系統級的能力,比如修改系統資訊等,這在 Web 上是完全做不到的(但這也並非 Web 領域內所專注的事情)。2 是對頁面間連線的管理,包括轉場效果、載入效能、記憶體回收等等。問題 1 是 Native Apps 領域裡本身就需要解決的問題,而“問題 2”正是當前 Web 能力不足的關鍵之處。

當前我們大部分真正需要以“資訊形式”共享到外部的頁面基本上都是符合“問題 2”的狀況,對於這一部分頁面存在兩個態:一個是在混合技術內呈現的態,一個是在混合技術外呈現的態。由於技術的差異性,必然造成後者的整體使用體驗比前者要差距甚大。

現在為了彌補這種體驗上的差距,我們需要一個純前端的技術來抹平因這種技術差異所造成的體驗缺陷。

我們可以進行具體一點的思考,這些所謂的體驗差距到底是什麼呢?

我覺得可以從心理感受上總結為“快”和“穩”,快首先體現在載入速度,首次見面可不能慢了,另外則是在動畫效果上的幀率要足夠順滑,不可出現卡、閃的現象,再一個就是在手指互動的一些行為上能做到及時的響應,不能出現延遲響應和不響應的狀況。

如果說快是物理現象,那麼在穩的層面則更傾向於“心理感受”,這也是人們自動區分事物屬性所具有的本能,比如 web 頁面更容易出現結構異變,比如在載入的初期呈現不完整的結構資訊,以及更容易出現載入時白屏,甚至是出現錯誤導致不得不強制重新整理,這都令 Web 感覺更加的“脆弱”,(比如很多人會遇到表單填寫失敗重新整理後被清空也是脆弱感的體現)並且它實際上還很“單薄”,因為 Web 應用往往功能和結構都相對簡單,在 Web 中頁面關係是串聯的,要麼向前要麼向後是一個二維的線形的結構,而原生 App 則可以是網狀的,能夠產生跳躍的, 因此這些印象都深刻的為 Web 帶來了偏見。

除此以外 Web 還有一些天生的缺陷或是說從來就不是很普遍被解決的問題,例如離線訪問、與其他應用的共享和互動、推送通知、鍵盤與文字編輯、後臺更新、檔案系統等等,但樂觀的是這些在一些最新的瀏覽器中已被得到了支援。

效能問題該從哪個角度來解呢?

我們再來回顧來看一下 SPA + PWA 的組合為什麼沒能普遍流行就很好得到解釋了。

雖然 SPA 增加了應用的“厚度感”,PWA 也提供了上面提到的諸如離線快取,訊息推送等能力,但這都沒能讓 Web 看起來能像一個原生應用,它僅僅是一個更好的 Web 而已,在體驗問題上並沒有得到巨大的進步。

當然互動的體驗問題,也是多年來前端容易忽視的問題,因為人們常常認為前端的“所謂效能”主要指的就是“首屏載入”的時間,而非真實的使用感受,因為真實世界裡我們常常歸咎於使用者手機效能的好壞,在這一點上所有人對 Web 的包容性明顯比原生應用的包容性高的多,只要頁面不出錯就是可被接受的,對於 Web 效能本就如此早就產生了一些不必說的共識。

以“首屏載入”作為“效能”的主要指標的另一個重要原因是該指標能夠被進行可直觀的可量化的衡量,不僅可作為技術緯度的衡量,更重要的是在業務資料方面也是一個重要的衡量指標。

相比一些硬指標,體驗效能則就較為抽象了,就拿 Android 和 IOS 相比,我們都知道兩者在互動體驗上存在著明顯的差異,但在功能性方面對比兩者則可能是仲伯之間,很難在一個單一維度中對比某一體驗設計對整體的影響, 因為體驗設計本身是一個整體性的東西,因此體驗感的缺失也是整體性的,站在一個單點來看細節體驗在一些功能性面前簡直不值一提,但站在一個系統來看,一個整體的體驗感卻是勝過不痛不癢的功能點的。

因而當功能性與同類產品相佐時就更需要提升整體的體驗感來獲得忠實的使用者。

在一件事情達到瓶頸時人們往往就會尋求新的發展方向,比如發展替代產物或提升工程效率或甚至於覆蓋到其他領域,都是從實際效能比出發去驅動工作內容的,就如晶片領域是一樣,這都是很容易理解的事物發展規律。

讓我們迴歸到網際網路的本質,用第一性原理來撥析 Web 發展的方向,若我們從一個資訊共享的未來俯瞰觀測,那麼開啟與開放一定會是趨勢而一定不會是走向一個封閉領域,所以一切的“變種”一定都是短暫的洪流。重回正題,我們要面向未來地解決前端體驗問題,就必須用前端來解決前端問題。注意我說的是體驗問題,而沒有歸納為效能問題,因為 Web 的普遍性體驗並非都是因為受限於效能,而絕大部分是開發思維的問題。

純前端容器 Lath 是什麼?

既然我們明確了必須由前端解決前端體驗問題,那麼我們到底要解決哪一些具體的問題呢?嗯,很棒的是我們已經這樣做了,並且誕生了很棒的產品,就是我們今天要講的主角“Lath”,抱歉主題來的有點晚,因為我必須交代以上背景。

Lath 是一個純前端的容器,它能夠把任何一個普通的 Web 頁面變為可絲滑連線與其關聯頁面的集合,也就是可以將任何頁面變為一個 SPA 單頁 Web 應用。 Lath 所帶來了一些互動效果,但要注意的是它並非是一個庫或框架,準確來說 Lath 並不參與到你頁面內部的邏輯,比如你元件自己的動畫效果和邏輯,它主要關注頁面之外的邏輯與效果,總結一下它的核心功能就是建立頁面間的絲滑連線,也就是 App 中的檢視視窗的管理能力。

Lath 通過哪些細節提升使用者體驗?

Lath 的所有核心能力都是圍繞如何建立絲滑的頁面連線所展開的,接下來我們就來看一下它其中的部分亮點。

注意這並非是一篇技術細節和原理的解讀文章,其中大部分是對概念與功能的介紹,我認為技術細節可以有很多種方式去實現,而去做這樣一個有概念的事情比講解技術實現更有意義。

關於互動前置

第一個亮點就是從點選的開始就增加互動效果,我們普遍的 web 頁面互動的起點都是從“click”事件開始的,這是從 PC 時代遺傳下來的習慣,而實際上在任何 Button、Item、Card 等效果上都應具有 touch 的三種狀態效果,尤其是需要被響應的互動。 我們可以點選一下螢幕上的 App Icon,或是微信的聯絡人列表,再或是任何一個原生 App 中的 Item 導航條,你都會發現,從你手指和螢幕接觸的一刻即產生了互動效果,只要你不鬆手效果將會一直存在,直到產生新的事件分支,比如長按、拖拽、點選等。而從開始觸控到螢幕到手指離開螢幕,確定為點選事件的整個過程大概需要花費 300ms 左右,也意味著在 1/3 秒內整個螢幕上的互動部分都是靜止的,這就會令人產生一個錯覺,就是互動被延遲了,尤其當你後續操作需要更多耗時時,直觀的感受就是卡頓的。

這是一個很小的體驗 Tips,但也是很普遍很通用性的能力,也是 Lath 提供的一個基礎小能力,會對頁面中符合條件的各類元素提供點選事件前的增強效果。(對元件質量不對齊又比較懶的開發者非常貼心)

及時可預期的互動

除了互動前置,使用及時且可預期的互動也是加強體驗的重要手段,什麼是可預期互動?我們一般認為是符合常識、物理規律、現實經驗的可被理解的互動反饋,比如 Loading、以及產生彈性/慣性的手勢、頁面間轉場動畫等都屬於可預期互動。

在 Lath 中已經預置好了強大的視窗管理系統,從最初的點選到頁面的載入以及轉場動畫等都覆蓋了整套的體驗方案。

接下來我們來簡單說一下預載入,例如我當前停留在 A 頁面,A 頁面將可能會訪問 B、C 頁面,那麼此時使用者在決定進入某個頁面前 Lath 已經在任務空閒期偷偷幫你載入好了必要資原始檔,當你點選進入時就無需進行前期的載入,可直接執行頁面程式碼邏輯。 對比傳統的 Web 跳轉方式能平均節省 50ms ~ 1s 的前期載入時間,當然這都是在網路良好的基礎上。

當然在實際過程中還有著各種各樣的週期性問題,Lath 都通過配置檔案提供了多樣的管理方案。

平滑的下拉重新整理

說完預載入,另一個和載入有點關係的就是重新整理載入,但是這兩者在體驗上有著很大的區別,首次載入需要的是速度,而重新整理載入則更強調“穩定”,主要體現在不能閃,不能白。 傳統的 Web 應用,在下拉重新整理時往往都會經歷上圖的三個主要階段,而在載入的中間過程則會出現非常具有 Web 特色的白屏和渲染過程,資訊在閃爍過程中忽有忽無,造成了不安全與脆弱感。

Lath 為每個頁面提供了配置化的平滑下拉重新整理,注意它並不是一個下拉重新整理元件,因而開發者不需要引入一些程式碼,也不需要為其書寫呼叫邏輯,作為容器能力它僅需一個簡單的配置即可自動完成這一切。我們可以看到在下拉重新整理的過程中沒有載入過程,也沒有替換和修改的過程,而是直接呈現一個完整的新內容,這是非常貼近原生 App 的體驗的。

內容如何進行平滑的更新是下拉重新整理最核心的能力,基於這個核心能力我們還可以做到平滑的主題切換和語言切換。

系統級的視窗管理

提到視窗管理,前端往往沒有關心這個領域,因為我們所做的工作都是基於視窗內來完成的,對於視窗本身的管理往往沒人關心,那應該都是交由系統和瀏覽器去決定的吧。

如果有一個課題,實現一個系統級的視窗管理,如果僅僅是用前端來實現,那麼應該如何實現呢?

首先系統級的視窗管理就是非常複雜的一件事,包含全屏視窗、嵌合視窗、Split 視窗、Modality 視窗、Sheets 視窗、 Tab 視窗、Slide 視窗等等,每一種視窗又回形成串聯、並聯的歷史關係,同時還要根據歷史關係進行前進與後退的各種型別間的視窗動畫效果切換,以及視窗間的手勢操作和動畫效果、視窗的拖拽、視窗映象,除此之外視窗還存在縱向的以及相互巢狀的視窗等問題。

對於這視窗問題我個人還是對此挺執著的,每當看到 IOS 上的炫酷互動,都讓我有種衝動想去用前端技術去實現一個,曾經在 15 年的時候我就開始了這個計劃,從一個系統桌面開始,桌面中又包含了各種應用,應用中又包含了各種視窗效果。 接下來我們列舉一些比較常用的視窗效果

Multilayer 疊加視窗(範型)

疊加視窗是一種可透視視窗,視窗是非全屏屬性的,比如一個主視窗只負責 Tab 卡的切換,而在主視窗正下方還疊加了內容視窗,此時根據兩者的透視關係會疊加為一個“全屏視窗”,而實際上這是由多個視窗合併而來的視窗。 我們最常見的使用就是創造一個 Tab View,而實際上 Lath 並沒有像傳統框架那樣提供一個 Tab 元件,而是通過疊加視窗 + 無縫連線 的特性由開發者自行創造的,每一個 Tab 按鈕都可以是一個 A 連線,當被點選時之要將其跳轉頁面的動畫設定為無動畫即可做到 Tab 切換的效果,當然主內容檢視設定成 Slide 視窗效果時也可做到可划動的 Tab 切換效果。

全屏視窗(範型)

全屏視窗是最尋常的視窗型別,有著較高的覆蓋層級,比如當全屏視窗和 Multilayer 疊加視窗產生互動時,全屏視窗將始終在 Multilayer 疊加視窗的前景出現。 全屏視窗和疊加視窗都是一種範型別,而其他功能型視窗都屬於這兩種範型視窗。

可互動視窗

多數的視窗型別都具有可自定義的視窗動畫效果,而我們一般所說的 Web 轉場動畫是不具備可互動性的,可互動性動畫不是一個固定的動畫方式,而是包含了更多的輸入資訊,比如手指觸發的方向/位置,從哪個來源觸發的,比如由邊緣回退、瀏覽器按鈕、手勢操作等觸發源所觸發的動畫。 可互動動畫相比固定動畫更具靈動性,也更符合事物發展的預期。另外可互動動畫一般還會具有可中斷、可干預的特性,比如通過手勢划動來另程式返回到上一個視窗,過程是連續且可中斷的。下面介紹的幾種視窗型別則就是符合可互動視窗的特性。

Slide 切卡視窗

切卡型別也是一種常見的多頁面互動方式,比如早期的 IOS 任務管理列表,橫向滑動即可快速的在多頁面間進行切換,切卡型別視窗準確來說是一個“窗中窗”的視窗型別,本身是依託於某個全屏視窗或 Multilayer 疊加視窗內的。 作為可互動型視窗最重要的則是其滑動的效能部分,大家都知道 JS 到 Dom 的渲染執行緒是單一執行緒,除了預先執行的 CSS 動畫和一些原生預設行為不受阻塞外,其餘之外的任何節點操作都受制於 JS 與 Dom 渲染引擎的雙向效能限制,如果想做到絕對媲美原生效能的互動體驗就不得不用到兩個重要的原則,核心要點已經透露了,看破不說破,細心的小夥伴能從中發現,而另一個要點也將會在後面進行提及。

切卡型視窗還可以創造一種常見的 App 互動形式,就是近幾年在國內的很流行的“二樓”效果。

Sheets 彈出層視窗

彈出層是一種更加扁平的視窗效果,其從形態上講看起來像是一種疊加視窗,單實質上它卻是屬於全屏視窗的,只不過它具有部分半透明特性的全屏視窗。 彈層視窗會讓資訊變得更飽滿,很適合需要延伸閱讀的場景,在資訊閱讀完後能夠快速回歸到主資訊流中。

Sheets 彈出層視窗也是一個重要的可互動視窗,其體驗的核心依舊體現在互動的效能上,是否跟手是否吊幀等。

另外還有一些細節體驗,比如支援橫向和縱向的兩個方向進行關閉視窗等。 除了一段位的彈出層,Lath 還支援兩段位的彈出層 能更扁平的展示延展資訊,使整體互動體驗更上一層樓。

視窗手勢回退

在 IOS 中我們最常用的功能是什麼?無意就是返回操作了,由於 IOS 沒有實體返回按鈕,因此使用從邊緣划動的方式成為了主流的回退方式。

在 Lath 中這種方式變得更激進了,可以無需從邊緣划動,而是頁面的任何位置滑動都可使頁面進入回退狀態,當然邊緣划動依舊是強制返回的手勢,這是針對可滾動區域相互巢狀帶來的經驗互動。 當然整體互動的流暢感和跟手性依然是該效果最核心的賣點。

視窗導航管理

視窗導航是基於視窗管理所拓展出的一種快速連結能力,類似瀏覽器歷史列表的能力,但在 Lath 中預設提供了一種可互動的視窗導航模式,互動模式類似於 Safari 的標籤導航器,它最大的好處是能夠扁平化的呈現使用者的瀏覽路徑,以快捷的方式訪問歷史頁面資訊。

視窗導航器中使用的能力都是由 Lath 本身所提供的對外能來進行構建的,當然你也可以構建一個適合自己的導航器。

記憶體管理

Lath 中的每一個 Applet(我們所指的頁面)都有一個完整的上下文,當視窗不處於活動狀態時或隨著瀏覽深度的增加導致視窗數量的增多時,記憶體管理就非常重要的,在處理記憶體管理上有很多的細節,最核心的思想還是以棧的方式來存放視窗,當視窗數量超過設定值時則進行主動的銷燬回收。 根據 Applet 的型別和配置,有些可能被徹底銷燬,有些則可能始終不被銷燬,有些是銷燬例項但保留了其映象檔案等等。

任務管理

雖然 Lath 對視窗數量是有控制機制的,但是當這些視窗同時存在一些多工時,則需要有力的管控機制。比如 A 頁面存在一個播放中的影片,當從 A 頁面轉場進入 B 頁面時,若 A 頁面未被銷燬回收,那麼 A 頁面的影片則應該立即被暫停。當然這應該是開發者自己就應需要考慮的事情,比如通過 Segue 的轉場事件自行管理播放狀態。但對於容器層我們還額外的提供了一些自動化的管理手段,以幫助一些懶惰的開發者來提升開發體驗與使用者體驗。 在 Lath 中這是一個自動媒體管控的配置項,能夠自動的根據一些情況進行符合預期的管控,比如還能夠對頻繁有 DOM 操作的不可見頁面進行回收等。

除了媒體管控,在 Web 中還有兩個常見的背景 Trigger 觸發事件,就是 setTimeout 和 setInterval,對於在不同頁面中的呼叫我們也將其做了自動管理,當頁面 Segue 到不可見時管理器則會暫停相關 Trigger 的觸發行為,直至下一次 Segue 到可見時再繼續執行任務。

Lath 的技術特點

前面介紹了一些功能上的特色,而在技術上 Lath 也有著獨特風格。Lath 被定義為一個純前端的 Container,宗旨是建立頁面間的無縫平滑連線。那麼勢必他將會在產生諸多的互動元素,而這些元素如何能夠不干擾開發者本身呢?

不受限框架

我之前專門講過 WebComponents 的技術優點,沒錯,Lath 本身就是以一個 WebComponents 的技術方式進行提供服務的。

因而它並不會侷限於任何的框架體系,在常見的理性框架中都能夠便捷的去使用它。

漸進式增強

另外 Lath 本身也是有原子化能力組成的,它的每個核心部分都能進行按需載入,在首屏的最初期它和一個 div 標籤沒有太大區別,隨著漸進式的能力載入才逐漸起到更多的作用。 容器中的很多功能都並非是立刻被需要的,而是根據上下文資訊及實踐順序一次被按需載入的,因而在可拓展性方面它顯得更加的靈活小巧。

開發友好

我們在 Web 中能看到 Lath 的部分就是它標誌性的兩個標籤了,就像這樣,只要將頁面內容外包裹上該標籤就能獲得神奇的能力。 這與框架最顯著的區別在於你無需將各種互動元件作為你程式碼邏輯的一部分,而所有期望效果都是在原有頁面上進行的互動體驗增強而已,這是一個非破壞性的開發體驗。

當然在我們開啟控制檯排查的時候也依然不會看到多餘的互動元素和結構,簡直就是和你的程式碼結構保持完全一致,明朗整潔。

最後

展望:Lath 旨解決頁面到頁面間的體驗問題,而對於頁面內的基礎元件的效能問題,未來我們也給提上了計劃日程,在元件問題上我們並不關心常規的邏輯,更不是想重複造輪子,而是將會帶著一種新的關於互動效能的思想來解決元件中的基礎問題,尤其是互動中取樣率差和掉幀嚴重的互動元件,從而能更好的完整的打造高效能的 Web App。

最後希望大家能夠喜歡它,有任何問題都可以加我進行交流。

---------------------------- 附件------------------------------- 附上產品技術文件以及新增到主屏幕後的離線狀態下 Demo 演示影片 技術文件: https://lath.dev [1] (注:文件服務來源 ghp,國內首次訪問可能較慢,另原始碼倉庫目前尚未開放,最新訊息可從官網獲取) Demo 影片:

參考資料

[1]

https://lath.dev: https://lath.dev/