當前位置:
首頁 > 科技 > 輕芒聯合創始人、CTO 范懷宇親述《使用小程序做交互的技巧》

輕芒聯合創始人、CTO 范懷宇親述《使用小程序做交互的技巧》

小程序上線幾百天了,期間誕生了很多優秀小程序。而輕芒雜誌的小程序,一定算得上是一個優秀案例。就連筆者朋友的公司想要開發讀書小程序,首要參考的就是輕芒雜誌小程序。那麼,輕芒是如何把小程序做起來的呢?來聽輕芒聯合創始人范懷宇,親自為你講講吧!

作者 | 范懷宇,輕芒聯合創始人、CTO

責編 | 胡巍巍

在這裡跟大家分享一些輕芒做小程序的具體實踐,結合我們在做輕芒雜誌過程中的技術選擇,重點和大家聊一些輕芒雜誌交互實現的經驗。

開始之前,先自我介紹一下,我大概 2009 年開始入行,2011 年去了豌豆莢,2016 年底我和王俊煜、崔瑾一起出來做了輕芒。

在過去十年的職業生涯中,我經歷過很多「端」,我們最早在 Windows 上提供豌豆莢的各種服務,然後還有塞班系統、Android、iOS 以及 Web,現在小程序也是非常重要的一個「端」。

看整個客戶端的技術發展會發現,從「端」的技術來講是越來越薄的。原來做 Windows 開發,你可能面臨很多的光碟,非常厚的 MSDN 的文檔,還有海量的 Windows32 API。

小程序很薄,需要了解的細節也更少

而到小程序,已經越來越薄,需要了解的細節也更少。這是因為,平台上的框架會變得越來越厚,我們開發者入手一個平台會變得越來越簡單。

今天「端」的發展趨勢是,用戶在哪裡,「端」就在哪裡,我們更多的是來適應「端」,而且無論在哪個「端」都需要提供最好的產品體驗。

輕芒在小程序發布的第一天就上線了和小程序相關的產品,包括為用戶推薦高品質內容的輕芒雜誌,我們也把產品經驗開放給內容創作者使用,通過輕芒小程序+,他們無需代碼就可以搭建自己的小程序。

什麼是交互

回到今天的主題:交互。首先要搞清楚什麼是交互,在傳統客戶端的分層邏輯里我們會常聽到 MVC、數據、控制、界面,這是一個可能用了二十幾年的分層模型。

交互毫無疑問包含內容呈現,把我們需要給用戶的數據呈現出來。還有一部分很重要的,是你要和用戶互動,交互界面通常不是靜態頁面,用戶會在界面上產生一些行為,這些行為通過控制層會反饋到數據層,數據層進行一些變化和更迭,再通過控制層送回給界面層重新進行數據的渲染和呈現,這是整個 MVC 流轉的方式。

除了內容的呈現和渲染,還有很重要的一點是要處理好所有和用戶的互動,這塊其實佔了前端很大的開發量。因此,會有的人可能前端開發無聊,有的人則會覺得非常有魅力。

做交互其實沒有什麼銀彈。在軟體開發里我們一直會需要一個「銀彈」,就是「當我知道那個秘籍之後,我就可以做得非常輕鬆,什麼事情都可以變得非常瀟洒」,其實並不存在。

大量的實踐混雜在細節里,包括我今天和大家分享的很多東西,聽上去沒有那麼神奇,甚至說有的一點都不工程,但這是真正的實踐。你真正要把一些東西做好,很多都是在細節裡面的。

首先,我先介紹一下小程序的平台特點,因為所有的前端開發,都是在一個平台上進行,你得對這個平台非常熟悉,就像在一個舞台上做演出,你需要知道舞台在哪裡、光在哪裡、聲音在哪裡等等。你需要了解這個平台的特性,才能在後面的實踐中更加自如。

小程序交互層的設計特點

跟我經歷過的其他平台相比,先我覺得小程序是個非常純粹的數據驅動的前端平台。比如,現在去用 Web 或者 Android 開發,如果你需要在界面點一個按鈕來隱藏一個地方,或者點一個按鈕來展開某個地方,你會怎麼做?按剛剛 MVC 模型,你可能不會穿過 Controller 去到 Model,然後改了 Model 再回來,不會走這個路徑。

你肯定會在前端用一些簡單的腳本或技巧,讓這個東西隱藏起來、再展開,這樣不會影響核心數據模型,而且很便利。

但在整個小程序里其實沒有這一塊東西,你不能操作任何 DOM 節點,這意味著不管你做什麼事情,包括改變界面的任何一點點狀態,都需要通過控制層傳到邏輯層,用 JS 經過一些計算,再調用 setData 函數,觸發對 Data 變化的比對,重新對界面進行一次渲染,這是小程序整個的內核驅動模型。

這個模型坦白講有很多部分非常重,會限制你做一些特別靈活的實現,但對我們開發者來講,只有理解這個模型,才會知道如果去做開發,哪些環節需要特別注意。

還有一個大家非常熟悉的點,小程序是傳統的 Web 組件混著一些原生組件,就是用本地語言,比如說 iOS 上的 Objective C 或者是 Android 上 Java 來實現的一個本地組件,這和 Web 上面實現的組件是不一樣的。

小程序採取的技術路線,是所謂的分層渲染,底下是 Web 層,上面是原生層,當我滑動 Web 層時,原生層會接到一些事件,它會嘗試跟 Web 層聯動,但它們其實不是一起動的,中間有一個延遲。

這個延遲會帶來很多麻煩事情,尤其是由於這個分層是原生層永遠活在 Web 層上面,這也會給交互設計、交互實踐帶來一些麻煩。

還有一個特色是單窗口,當然在移動時代其他平台也如此,在一個交互頁面,我們只會跟一個窗口打交道。小程序特別的一點是它徹底取消了窗口這個概念,可能我們都感覺不到窗口的存在。

如果大家做過 Android,就知道 Android 可以做懸浮窗,由於小程序平台機制的限制,懸浮窗這種交互形態是無法實現的。

以上是我總結的小程序平台交互設計的顯著特點,這也是我們後面圍繞小程序平台做交互設計、交互實踐的一個很重要的起點。

輕芒的交互實踐案例一:列表的實現

下面跟大家分享一些輕芒的案例,首先講一個大家用得最多的:列表。列表為什麼重要?理清楚列表的邏輯對於整個產品後續的實現、對於工程師更好的去理解業務和設計,是非常重要的一個環節。

在傳統的一般界面組件里,列表是最複雜的一個模塊,它會有很多數據的聯動,它的交互、數據傳遞、事件分發都會涉及到很多問題,這也是我們為什麼要妥善思考列表怎麼做。而且在很多場景里,長列表的性能是很多性能瓶頸的來源,這也是需要額外注意的地方。

什麼叫列表?比如下圖左邊是非常顯然的列表,可以無限載入、不斷滾動的內容流,我們認為每一個卡片都是一個列表項。右圖看上去是一個內容詳情頁,在實踐中我們也把它做成了列表,我們會把它每個段落抽象出來,按照列表來渲染。

小程序裡面最大的特色是,它並沒有一個原生的列表組件。我們剛剛說 MVC,而 MVC 中最核心的是模型(Model)的設計,就是整個產品中的數據模型到底是什麼樣的,這是真正影響開發時間的。

如果模型設計得好,就像種了一棵樹,樹上長著枝椏,如果你把所有的數據模型抽象的非常漂亮,那樹上的枝椏,也就是 View 和 Controller,實現起來會非常輕鬆。

如果你的模型完全不做抽象,隨便收拾一下就開始做交互開發,那即便你在界面層選了很多漂亮的轉型、用很多 Fancy 的框架,你也很難把代碼複雜度降下來,整個產品的復用度也不會高,這也是我為什麼會說如果拿一個項目最好從列表入手,因為列表的數據模型通常是最複雜的,列表清楚了,整個產品也就清楚了。

在輕芒里,比如剛剛那個瀑布流,我們會抽象成一個 event 對象,當然,我們不是在所有的小程序開發里都這麼做,因為在其他小程序中業務不同,抽象模型也可能不一樣,我們配套的列表實現也會不一樣。

在輕芒雜誌,因為有非常複雜的卡片類型或者不同類型的交互樣式,我們會把不同類型的數據統一抽象成一個 event 對象,每個 event 會有一個類型,然後會圍繞 event 來設計列表控制項。

這裡面最重要的其實是對 event 對象的抽象,也就是產品中最核心的數據模型,如果這個搞清楚了,你會發現後面東西會變得很簡單。

在這個代碼中,每一個卡片的渲染,可能有兩個不同的實現方式,有一些卡片是用模板實現的,比如說 single-card-*,在實現中,我們會直接用 single-card 加上具體的 type 來實現列表項,比如說 single-card-article、single-card-image、single-card-video 等等。

在微信有了自定義組件之後,我們把一些後來實現的新卡片放成了自定義組件,比如這裡的 Universe Card ,我們把新的卡片用了新的方式來做。

早期因為我們用 Template 對界面元素做封裝,這種封裝包含了任何互動的實現。因為微信早期沒有提供任何可封裝機制,現在你也許可以通過不同的開源框架搞定。

早期唯一的辦法就是把一些和界面互動相關的函數抽象成一些 Mixin 模塊,然後在頁面配置里直接整合進去,增強復用度。這個方案我們現在依然在用,這也是在小程序里最常用的實踐方案之一。

另一方面,封裝列表也是為了統一相關聯的界面組件,比如統一的 loading 樣式,統一的空白頁樣式,翻頁的邏輯和多級嵌套等。

舉例來說,所有數據的載入,在列表中定義了翻頁方式,就定義了產品的客戶端和服務端的交互方式。在輕芒,所有的服務端翻頁都會使用 Next Url 模式,服務端會告訴客戶端有沒有下一頁,有的話客戶端滾到底就會載入下一頁。

這告訴我們,如果服務端具有統一的 API 模式,客戶端開發會變得很輕鬆,抽象和復用可以做得更充分。

當然,這反向也約束了服務端,需要服務端提供統一的或者整合的 API,這樣落地到公司全部團隊,可以提升整體的效能。對前端來講,也只有這樣的方案才是能夠極大地簡化開發複雜度。

列表實現中,還有一個和小程序特性特別相關的點,就是如何在列表中嵌入一個原生組件,比如我想在列表裡播視頻,我想在列表裡嵌個地圖、嵌個輸入框,該怎麼辦?

如果用微信原生組件的渲染機制,如果嵌一個視頻在內容流里,它可能會遮住很多你想彈出來的對話框;有可能你想實現頁面快速滾動,視頻會有非常慢的拖影,這都很影響產品體驗。

在輕芒雜誌中,我們用過非常多的交互方案去嘗試這個東西,最後是團隊一起來解決的。這個解決方案的核心理念是:不要在列表中直接使用原生組件播放視頻,而是用設計的方案去規避,基於分層的方案來實現整個交互。

原生組件出現的時候,整個交互停止,比如看左邊這個視頻,在播放一個嵌入的視頻時,這個視頻好像是「長」在那個卡片上的,其實它是個虛擬的定位,只是大概在那個位置。

用戶一旦滾動頁面,視頻立刻收起來不再播放。可能這聽上去很不「技術」,但實際上這是你對平台的理解,你知道這是一個系統設計上的硬坑,如果平台不搞定這點,無論誰來做都會碰到同樣的問題。與其這樣,不如我們理解平台之後,和團隊商量,換種方式實現,把事情變得更簡單、更輕鬆。

列表實現中還有一些問題,就是如何處理數據和狀態。每個列表項裡面會包含很多元素,比如列表項里會有一篇文章的基本信息,同時右下角有個交互按鈕,用戶點擊之後按鈕狀態發生變化,對輕芒來講這就是「馬克」,而這些都不屬於原生數據,會隨著用戶的交互而變化,因此被稱為狀態。

在早期小程序開發中,大家可能不太會區分狀態和數據,會把數據和狀態放到同一個數據模型上,因為實現邏輯非常複雜,在純數據驅動的模式下會碰到非常多問題。

而輕芒的實踐經驗,就是要讓數據和狀態分離。比如拿到數據後,我們會把數據分成兩部分,一部分是原生數據,它們不會隨著用戶交互而發生變化,用列表來進行存儲;而另一部分是狀態,它會隨著用戶交互發生變化,這裡我們會用字典進行存儲。

圖示的代碼中,UI Switch 和 subscribed 的對象都存儲了狀態信息,當用戶對特定元素進行操作後,只需要在控制層改變 Switch 對應 ID 的某一個值,就可以對界面進行快速的更新。

這看上去是一個非常小的優化,但如果你在所有界面交互中都能把控好這一點,帶來的收益是非常大的。它還有一個特點,是中心化。

比如剛剛看到的視頻播放,我們同時只允許一個視頻播放,用戶點了某個視頻後其他視頻會停止播放,這時候你只需要在控制層把整個狀態清空,對交互對象的狀態重新設定即可,這都是一些聽上去比較細節,但從實踐來講非常重要的事情。

最後一個問題,是長列表性能優化,與其說是經驗分享,不如說教訓分享。在輕芒雜誌中,我們會用到大量的長列表,無限滾動的瀑布流、非常長的文章等等。這些都會導致列表變長,帶來卡頓。為什麼?

從小程序的渲染機制來看,從設定數據到界面渲染呈現,也就是調用 setData 函數到 setData 函數返回,這裡面有兩件可能比較耗時的事情,一個是數據比對:小程序需要找到有哪些數據更新了,這些數據關聯了哪些界面元素。

還有一件就是刷新界面:把更新的數據呈現出來,先在虛擬節點上做渲染,重新放到前端來呈現。而小程序最大的問題,在於這兩個環節太「黑盒」了,它告訴開發者的信息非常少,因此優化起來就比較困難。我們知道輕芒雜誌的渲染都花在這裡了,所以會有卡頓,但到底是哪裡耗了時間,其實我們不能知道。

於是我們做的性能優化方案就是試,不停地做二分,少做一部分邏輯,看看性能有沒有好轉。我們最早把性能點放在 setData 不能追加上,雖然 setData 可以修改一個列表中的元素,但它一旦要增刪元素,就要全部替換,這是現在微信整個數據驅動模型的一個核心問題。

在這裡,我們嘗試使用固定的列表項個數,先虛擬一百個列表項,可能只有十個有數據,如果新的元素需要追加,就只用修改而不需要全部替換。最後的結論是,有效果,但沒太大的效果,而開發成本卻增加了不少。

後來我們把性能問題,更多地放在了渲染上,發現一個有效的方案是,讓界面設計變簡單一些,需要渲染的元素變少,性能就好了不少。

所以在長列表中,很大的瓶頸還是在 DOM 渲染上,小程序並沒有為列表提供一個元素回收機制,這導致它會完整渲染整個列表,它並不關心這個列表項需不需要在界面上呈現、需不需要給用戶看,這就會造成很多性能問題。

在這裡我們也沒有太完整的經驗,更多的是教訓。那對於大家來說,如果你能預見到你的產品會有很長列表,你可以提前考慮是不是要降低列表項的設計複雜度,控制每個列表項 DOM 節點的數量,減少一些 DOM 節點上綁定的數據和事件等等,這些可能會對產品的性能有所優所。

當然整體來看小程序的渲染還是個黑盒,我們能用的優化手段也比較少。總結一下,其實最想和大家聊的是交互的開發和業務是密不可分的,需要去和設計、後端、產品,一起去改進,在技術設計中更多的去理解需求。

輕芒的交互實踐案例二:全局窗口的實現

我們知道微信小程序是一個單窗口的交互平台,那什麼是全局窗口呢?比如,上面左邊這個分享卡片,它需要在任何輕芒雜誌的頁面都能被呼出,從而對頁面進行分享。

但因為在小程序中沒有全局窗口機制,對我們而言唯一的辦法就是通過把組件放進每一個頁面,隨時可以呼出使用,模擬全局窗口,我們把這個稱為全局組件。在這個部分,我會重點來聊組件的封裝,在小程序中最好的組件方案是怎樣的。

剛剛大家聽了很多第三方的框架,在輕芒,我們主要還都是基於原生的機制來實現的。常見原生方式有兩種:基於模板(Template) 和基於自定義組件的,這兩種封裝方式我們都很常用。

像這個代碼展示的,我們有的卡片使用了自定義組件來進行封裝,有一些載入的進度條可能是模板來封裝的。但如果我把它做成全局組件,讓每個頁面都包含,這兩種方式都需要我在各個頁面拷貝大量的重複代碼,可能有上百行,一旦需要變更,就會需要同時修改整個小程序里幾十個頁面。

這種方案我們早期使用過,但發現效果不好,維護起來太麻煩了。於是我們重新設計了一下,發現在小程序中還有一些更簡單的封裝方案更適合,就是大家今天可能很少用的 include 方案,就相當於直接引入一個別的代碼塊。

這是一個非常傳統的一個方案,我們現在往往會強調組件,強調封裝,要封裝得漂亮,要讓組件邊界變得乾淨,但實際上在這樣的場景里,我們打破一些簡單的組件的限制,反而會讓事情變得簡單。

我們會寫一個全局窗口的文件叫 Global WXML,把所有小程序中會用到的全局組件的數據和實現都放在這裡面,圖上的代碼是疊過的,其實展開還是比較長的,因為在 include 中不能再使用其他 Template 進行封裝了。

對應的,還配套一些 js 代碼,我們封裝了 action.js 的文件,裡面會包括各種全局組件的控制,比如模態對話框、輸入對話框、toast、分享卡片等等。在最後使用裡面,每一個頁面只要加兩行,一個是 include global,一個 Mixin 這個 action.js,這解決了問題。

這個例子非常小,背後想聊的主要還是組件封裝的思路。做技術設計,始終要考慮怎麼封裝組件、怎麼復用,因為這個對所有前端開發、服務端開發都是重要的,如果不復用、不去嘗試做一些抽象,那麼長此以往代碼會變得非常散,難以維護、難以閱讀。

但怎麼做封裝,怎麼做實踐?除了把組件抽象得非常漂亮外,也可以嘗試用一些邊界更模糊的封裝方案,可以顯著的讓代碼變短變簡單,更易於變更和維護。

比如,在前面例子中,我要變更某個全局組件的樣式,只要修改 Global XML,而調用該文件的地方是不用改的,用開閉原則來看,它是個非常滿足開閉原則的實現策略。

輕芒的交互實踐案例三:馬克

最後再跟大家分享一個案例,這是一個非常「輕芒」的交互實現策略。圖上的這個交互叫「馬克」。

這個交互是輕芒雜誌的一個核心功能,我們之前在 Android、iOS 上都做過類似的實現,最麻煩的確實是在小程序上。要實現這個,首先需要去理解小程序的事件機制。

小程序在早期只有綁定,在後期開放了事件捕獲流程,那就成了一個非常傳統的事件傳播機制,先是事件從父到子捕獲,然後是從子到父的事件冒泡。所有你想提供的交互,不管是左右滑動、翻頁、點擊翻頁等等,都依賴這個事件傳播機制。

要實現這樣的馬克交互,首先不是處理事件,而是對數據模型進行抽象,由於小程序無法精確的知道元素的排版信息,我們最早是做不出來這個效果的,最後做了一個模型抽象,把一個文章的段落切分成了句子,每個句子用一個 View 來進行渲染。

當你進行馬克,需要選中一部分文字的時候,我們其實是直接讓你選擇一個句子,這樣可以避免去考慮句子內的排版,從而非常好的實現了馬克的效果,這也是和設計師圍繞技術可行性反覆討論出來的,小程序沒有辦法控制 DOM 節點,更沒有辦法拿到排版裡面的細節,從數據上解決這個問題,是唯一的思路,你可以去改變一些數據模型,為你後來的交互做一些準備。

在我們掉過很多坑,當用戶移動手指去修改選中區域的時候,如果我們用了微信的條件判斷 wx:if 去改變底色之類的,很可能會讓整個控制項樹的結構發生變化,整個交互界面會陷入一個非常可怕的狀態,完全失去響應,我們推測這是觸發了微信底層的 Bug。

所以在這樣的交互中,最好的方式是只改變 CSS 的,儘可能不去改變界面元素的結構。

這裡有個細節,在觸發了調整馬克選中區域之後,我們再拖動手指的時候,整個界面是不滾動的,直到我們放開手指之後,整個界面才可以重新開始滾動。

這時候就用到了我剛剛說過的事件傳播機制的流程處理,在小程序中,只要你設定了捕獲函數就必須捕獲事件,不像其他平台,可以通過捕獲函數的返回值來控制是否需要捕獲,而如果不進行捕獲,後續又沒法中斷整個頁面的滾動。

那怎麼辦?我們後來用了一些比較 Hack 的方式,我們去動態的修改綁定的捕獲函數名。

當需要捕獲時,就把需要的函數設定上,而停止捕獲時,就把捕獲函數設置為空白字元串,這樣可以繞過它的捕獲機制。

這時候,就可以控制界面在什麼時候,可以滾動頁面,什麼時候只可以調整選中區域了。

這個馬克交互我們改版過很多次,早期由於沒有 query 任何 DOM 元素的機制,非常難以實現。

整個馬克的實現過程中,工程師和設計師有非常多的討論,設計師根據技術限制重新想一些交互方案,工程上不斷地嘗試採取一些類似 Hack 的手段去落地這些方案。

如果你希望把交互做得更往前,毫無疑問也會需要這樣進行設計和實現,也會踩到一些平台的坑,這些經驗也可以供大家參考。

上面分享的三個案例,不全部是單純的技術,有不少技術外的事情。交互,看上去是一個純前端實現的問題,其實它的實現是整個團隊的事情,包括產品、設計、以及後端的 APIS 設計等等。也是平台能力和產品設計的交互融合。

另外,在小程序這個平台上尤其需要重視數據模型的設計,一定要讓整個數據模型理解好業務,把整個數據模型設計得清晰,什麼是數據、什麼是狀態、哪些應該隔離、哪些應該放在一起,把這些事情區分好,可以讓整個前端開發變得很輕鬆。

此外,我們在處理原生組件時也要特別小心,需要規避掉一些設計方案,否則會帶來一系列 Bug。

這是輕芒的實踐,我相信和大家的具體的業務需求、目標會不完全一致。但也期望今天的分享可以給大家一些啟發,如果大家能運用到其中的一些,對我們來講就足夠了。

本文系作者投稿,版權歸作者所有。


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 CSDN 的精彩文章:

Google 誓要幹掉 URL!
劉強東性侵案女主曝細節;馬雲中秋赴聯合國打卡|極客頭條

TAG:CSDN |