降本增效,攜程市場DIY商品卡片系統的設計與實現
一、背景
攜程各個BU各個時期都有不同營銷頁面,數量眾多,其中很重要的一塊是產品模塊,運營需求的產品卡片樣式眾多,各個BU展示字段差別巨大,無法利用通用樣式,因此如需新增卡片或字段,傳統做法是運營提需求給設計,再提需求給開發,經過需求評審,正式開發,發布測試上線等等。
每次遇到大促活動或者接入新的業務方,都需要重新設計及開發商卡,而新的商卡大多只是新增一些換膚樣式,或個別字段,這卻需要開發人員多寫一套樣式代碼或新增字段樣式,同一個樣式應用于不同的業務方也需要重新進行開發,極大地浪費了開發和設計資源。
在DIY商品卡片系統開發前,由于開發成本的限制,營銷頁面上常用的商品卡片樣式基本固定為十幾種,這在用戶看來缺乏新意,吸引力不足,從而在一定程度上影響了營銷頁面商卡的點擊量。
綜上所述,為解決傳統商卡存在的以下問題:
- 即使樣式差別很小也需要重復開發,耗時耗力。
- 沒法多平臺統一卡片樣式,難于管理、復用性低。
- 樣式固定、對用戶吸引力低導致點擊率及訂單轉化率低。
我們從UI和邏輯解耦,UI模板在平臺配置生成的角度考慮解決問題,設計了DIY商品卡片系統。
二、系統介紹
商卡系統將UI和邏輯進行解耦,UI部分可在商卡配置后臺進行配置,邏輯部分由開發人員處理后引入原子UI組件進行最終渲染。
2.1 商卡配置后臺
UI在商卡配置后臺中由產品或者運營人員參照設計稿進行自由拖拽式配置,并且給每個模塊綁定字段,同一套配置可以同時應用于H5和React Native。
2.2 原子UI組件
原子UI組件是顆粒度更小的一種靜態組件(React, React Native),可以通過手動開發或配合“產品畫布”DIY這兩種方式為業務組件提供渲染模板,其他使用方也可以提供一些固定的數據(契約)直接渲染單卡片樣式。開發人員只需在組件內引入原子組件的npm包,對業務邏輯進行處理后把相應字段的值傳給原子組件即可最終渲染真實產品信息。通過原子組件的模式,可以讓所有組件都“引用”線上的公共UI組件,直接應用在各商卡的業務方,輕松實現商品卡片樣式的統一,避免了每個業務方重復開發同一套樣式。下圖為商卡搭建的部分樣式:
2.3 效果
- 商品卡片樣式現已擺脫對開發人員的依賴,產品運營團隊可以直接通過拖拽配置的方式,輕松定制全新的樣式。
- 當同一商卡模板需要更新樣式時,僅需在商卡配置系統中進行配置更新,便可實現一鍵跨端修改的效果,使所有使用該模板的業務方同步更新。
- 針對需要定制展示內容的業務方,我們提供了定制渲染能力,允許他們在整體樣式統一的基礎上,保留部分商卡內容的獨特風格。
- 目前,我們已擁有400多張商卡和40多種常用模板,廣泛應用于攜程營銷頁面、特賣、星球號以及攜程直播等多個場景。
- 商卡樣式的更新換代速度得到了極大提升,為用戶帶來更加新穎的體驗,從而增強了點擊欲望。經過AB實驗測試,我們發現商卡系統搭建的樣式相較于過去固定的樣式,在點擊率上有了顯著提升。
三、系統設計
DIY商品卡片系統主要由2部分組成:商卡渲染和配置平臺。
3.1 DIY商品卡片渲染
DIY商品卡片可以通過以下三種渲染方式渲染,可以根據需求和配置難度選擇合適的方式:
1)字段的寬高和位置都固定渲染
渲染組件按照在配置平臺上的布局進行商品卡片的渲染,字段之間沒有聯動設計,只能進行簡單卡片的渲染。
渲染稍復雜的卡片時面臨的問題主要有以下兩個:
- 同一行的兩個字段,不能根據前一個字段的寬度動態改變后一個字段的位置。如下圖,第一張圖中顯示正常,第二張圖中當第一個字段評分變化為4分時,3797點評的位置還在原處,就會留有不合理的間距。
- 卡片的高度也不能根據字段的長短和是否有缺失字段進行動態調整,如下圖,缺失兩個標簽后下方的字段并不會自動向上填補空白,卡片高度也不會相應減小,在實際應用場景中,很多商品卡片都不是固定高度,在展示商品時更傾向于緊湊的商品卡片布局,即卡片的高度要根據商品數據進行動態調整。
2) 字段的寬高和位置通過數據進行動態計算,依舊采用絕對布局的方式
- 卡片橫向的動態布局
增加了行容器組件,將同一行的字段放進同一個行容器中就可以讓字段位置不再依賴固定坐標。
以React版渲染組件為例,行容器是一個flex布局的<div></div>,去掉放入行容器里的字段的絕對布局屬性,通過設置justify-content即可控制同一行字段的排列方式,自動補齊位置。
- 卡片縱向的動態布局
獲取到商品數據和渲染數據后,先不對商品卡片進行渲染。通過動態算法和canvas渲染對字段是否存在和字段長度進行校驗,并根據校驗結果修改渲染數據。修改完成的渲染數據可以達到卡片高度和字段位置動態適應的效果。
動態算法分為以下幾個步驟:
a. 當字段所需高度增加或者減少時,檢查哪些字段的位置會受到當前字段高度變化的影響,將受影響字段和需要進行的調整收集起來。
b. 校驗所有字段后,對收集的字段對應的渲染數據進行調整。
c. 調整完成后開始根據渲染數據進行商品卡片渲染。
d. 商品卡片渲染完成后檢測每個字段是否正確渲染,對于計算有偏差的字段調整,根據真實渲染情況再次進行微調。
3)字段之間采用相對布局的方式進行排列
對于縱向的動態布局,依賴通過商品數據和渲染數據進行計算也會帶來一些問題,比如在渲染數據的調整上,調整后的商品卡片可能會和配置略有差異,有一些準確性的問題。
為了達到和手工開發一樣的效果,增加了垂直方向的容器,稱為垂直容器。放入垂直容器的字段,會被調整為相對布局的定位方式,字段依次向下排列,不依賴定位屬性。
考慮到商品卡片普遍情況下都是采用比較規則的布局方式,所以使用行容器加垂直容器層層嵌套的方式可以實現去絕對布局的效果,所有字段都自動根據前面的字段動態調整位置。
4)總體渲染流程
接入方將數據處理為契約中的格式,傳給原子UI組件進行渲染。商卡渲染時,通過場景號和版本從接口中讀取兩部分配置數據:
- 商卡總體配置JSON:包含有效期時間、卡片寬高、背景色、圓角等。
- 商卡內字段組件列表:包含每個字段組件的css和react native樣式以及一些聯動邏輯的配置。
當獲取到的字段組件列表長度大于0時,我們開始循環處理列表內的組件,不同的字段類型渲染成不同的組件,若遇到容器則進行遞歸,直到容器中的內容全都處理完。
3.2 配置平臺
考慮到解放開發人力,大部分布局不復雜的商卡可以由產品或者運營人員根據設計稿進行配置。為了易于非開發人員操作和理解、提高配置效率,我們在以下幾個模塊的設計上增加了許多考量。
1)字段組件設計
目前支持的字段組件有普通字段、圖片、普通容器、垂直容器、橫向布局容器以及內聯容器,這基本可以滿足設計稿的布局。普通容器主要存放需要浮動的元素。垂直容器和橫向容器其實就是flex布局的兩種形式。內聯容器是為了解決行內元素換行問題,正常用橫向布局容器是沒辦法實現這個效果的,內聯容器內的元素我們一律轉成inline元素。內聯容器主要應用在如下圖所示的情況:套餐名稱超出一行需要換行,星級以及金鉆標簽緊跟其后。
2)畫布交互設計
為簡化操作,我們將商卡系統設計成拖拽配置的形式,配置人員可以從左側可選字段組件中直接將組件拖進畫布。若畫布中已配置了容器,則可以直接拖進相應容器中,字段間的順序以及位置也是可以直接進行拖動調整,至于細微的位置調整,可以通過字段的樣式配置進行調整。
3)字段樣式配置
下圖所示的樣式配置基本上就是簡單的css屬性,在浮動類型這有個特殊處理:浮動類型為浮動即positinotallow="absolute"時,距離上、右、底,左邊實際上代表top、right、bottom,left;浮動類型為不浮動即positinotallow="relative"時,距離上、右、底,左邊實際上代表 margin-top、margin-right、margin-bottom,margin-left,這樣可以減少配置項。在行容器和垂直容器的配置中,額外還有對齊方式的配置,對應的是flex布局中justify-content和align-items,容器的子元素配置中也有壓縮和彈性比例系數的配置,即flex-shrink以及flex。目前的配置項幾乎可以達到還原設計稿的目的。
4)特殊問題處理
- 問題:接口重復調用
- 解決方案:發布訂閱
商品卡片的渲染schema獲取是在每一個商品卡片里進行的,渲染商品列表時就會有重復調用接口的問題。
通過一些方式將接口請求次數盡可能減少很重要,但因為各商品卡片之間相互獨立,并不能在某一個商品卡片中知道頁面中都有哪些商品卡片模版ID,我們減少請求次數的方案也就是對于同一個商品卡片模版,只請求一次。
商品卡片在獲取到卡片模版ID后,檢查當前ID是否已經有請求到的數據,有數據則直接從window上取對應的數據進行渲染,如果沒有則檢查該ID是否已經被標記為已經在請求數據,對于已經在請求數據的情況,監聽當前數據請求成功的消息,數據請求成功后觸發請求的商品卡片會通過消息推送當前模版ID的渲染schema,并將此數據存在window上。過程如圖中所示。
一些商品數據字段在攜程的應用場景中也是比較固定的,對于這樣的一些商品數據字段,我們通過提供默認樣式的方式減少配置時的工作量,配置人員只需要關心這樣的字段的位置、大小以及一些顏色方面的配置,不需要再關心如前綴圖標、復雜組合形式等問題。
- 問題:RN樣式差異
- 解決方案:手動進行兼容性處理
與h5不同的是,rn的樣式需要進行單獨處理,大部分樣式處理都在商卡配置后臺中用“css-to-react-native“包進行了轉換,某些特殊樣式由rn版原子組件單獨處理。例如:h5中的內聯容器在rn中要用<Text><Text/>當最外層容器,里面所有子容器也由<Text><Text/>包裹,這樣才能實現元素內換行。其他還有漸變背景色用LinearGradient組件處理、背景圖片轉換成<Image/>標簽、border拆分成borderWidth和borderColor來處理、下發的rn樣式適配屏幕寬高等等。
- 問題:業務關聯
- 解決方案:字段聯動
在一些場景下,商品卡片的數據之間也是有關聯關系的,我們提供了字段之間關聯的一些配置,如兩個字段互斥,永遠只出現其中一個字段;兩個字段也可以是必須同時出現的關系,當一個字段有值時另一個才出現,比如有酒店LOGO時才展示酒店名稱;還有的時候字段需要根據另一個字段進行樣式和展示形式的調整,比如在價格為0時商品卡片的搶購按鈕變為“免費”文案的純文字。
- 問題:商卡展示不夠靈活
- 解決方案:Render props
DIY商品卡片提供了自定義插槽,渲染時可以直接傳入一個JSX函數在配置位置渲染,例如:接入方傳入一個直播流播放器。這樣配置上更加靈活,DIY商品卡片組件也無需引入過多第三方組件。這樣的做法還帶來一個好處,留有一定的空間供開發人員介入,對于一些難以配置的數據展示形式,可以指定該數據的展示使用開發人員定制開發的數據展示模塊,開發和配置混合的形式極大地補充了DIY商品卡片的展示能力。
- 問題:多點擊事件
- 解決方案:自定義點擊事件函數
商卡有默認的點擊事件,一般用于商品的跳轉,若一張大卡內有多個點擊區域,可以用透明蒙層配置在點擊區域的上方,并綁定相應的字段。數據傳入時只需要傳入點擊事件對象數組,商卡會根據對象的name和綁定的字段名進行匹配,將對象的函數綁定在對應的字段上。通過name進行匹配就無需在新增點擊事件時在代碼內手動將點擊事件與字段進行綁定,這帶來的好處是可以減少商卡本身的開發改動,更加靈活,只需要配置字段和傳入字段一一對應即可。
- 問題:卡片重復配置
- 解決方案:父子商卡
對于一些重復度較高的商品卡片,將相同的部分保留作為公共部分,不同的部分抽取出來單獨配置是一種更合理的方式。
我們將這種模式的卡片配置稱為父子模塊,在配置時父模塊和子模塊沒有區別,兩種模塊都可以作為一個單獨的商品卡片進行渲染。
在卡片上配置一個空間,定義該空間的key,在渲染商品卡片時將key對應的子模塊ID和商品數據一起傳入父模塊,父模塊在配置在渲染時將子模塊渲染在預留空間里,即可實現兩個商品卡片的拼接。
四、商品卡片配置系統功能點
4.1 導入導出
導出數據:導出該頁面上的商卡樣式,導出格式為txt文件,方便進行備份和復用。
導入數據:為了使配置操作更為簡單,可導入已有的商卡模板或部分字段組件素材,導入后在此模板上進行增刪改操作,避免從0開始搭建。
4.2 版本切換
畫布可分為三個版本:線上版本、可編輯版本、歷史版本。線上版本為真實版本,歷史版本為上一次保存的線上版本。只有在可編輯版本內才可以編輯,編輯后可以保存或者上線,只有選擇上線才能替換線上版本。
可編輯版本的設計可以將線上與編輯隔離,以免未編輯完成的畫布影響線上,同時歷史版本在一定程度上也是一種備份。
4.3 預覽
畫布提供h5版本或app版預覽,預覽的版本為當前選中版本。預覽的商卡數據源分為兩種,一種是通用數據源,即樂高平臺內針對不同業務方預先配置好的數據字段;另一種為自定義數據,配置人員可以根據具體的需求以JSON的形式在樂高平臺內新增數據字段。通過預覽,配置人員可以在商卡上線前看到實時效果,同時通過自定義數據也可模擬一些極端情況,如某些字段缺失后,卡片布局是否會變形等
4.4 卡片配置
為簡化配置流程,重復利用素材,商卡配置后臺支持其他商卡導出的素材txt文件以及設計稿導出的元素素材txt文件導入進畫布。設計稿導出的為較小顆粒度的元素,如一個名稱字段、商品圖片,不包含容器類大顆粒度元素的導出。設計稿直出可以省略部分基礎配置,如:字號,字體顏色,圖片寬高,邊框及圓角等,配置人員只需專注于布局的配置即可,減少了基礎樣式的配置時間。其他商卡導出的素材可直接完美復制原效果,包括字段綁定的名稱等,這種方式導出的素材不止是小顆粒度的元素,也可以是整個容器甚至整張卡片。
除了直接導入,商卡系統有自帶的組件庫,可將其中的組件直接拖拽添加進畫布。目前支持的組件有字段、圖片、背景方塊、橫向布局容器、垂直布局容器、內聯容器和普通容器。通過選中畫布中的某個字段或容器可以配置其css屬性、調整容器內子元素的順序和對齊方式等。除了樣式配置,還有字段屬性配置,字段要與對應的字段名進行綁定才能正常顯示,若畫布中配置了某個字段組件,但數據中沒有此字段名,最終上線后也是不展示此字段組件的,唯有一一對應才能正常展示。
配置完成后可以先通過預覽查看實時效果,避免上線后出現問題。上線后接入方通過唯一的商卡場景號調用該商卡,按照契約內容傳入字段即可成功渲染。
五、接入樣例
DIY商品卡片組件主要通過npm包的形式引入,按照契約傳遞數據即可渲染。
傳遞給組件的數據分為三類:
a. 商品信息
渲染商品卡片所必需的數據,如酒店名、酒店星級、價格、主圖鏈接等商品信息。
b. 自定義處理邏輯
在我們的商品卡片中,可自定義的處理邏輯主要是在點擊事件中。組件支持對整個商品卡片的點擊方法進行覆蓋,也支持對單個字段如酒店標簽進行添加點擊事件處理邏輯。
c. 定義渲染信息
對于商品卡片的渲染,提供了0%~100%程度的自定義渲染方式。0%,就是接入方開發人員只需要傳遞商品數據,無需關心數據的渲染。100%,即是接入方開發人員可以將對應商品數據的整個渲染完全覆蓋掉,DIY商品卡片組件使用render props的方式渲染接入方傳遞進來的渲染方法。不到100%又高于0%程度的渲染,則是提供給接入方傳入指定商品數據的CSS以追加的形式修改原有CSS的能力。
六、總結
由于傳統商卡開發模式無法適應當前業務的快速發展,我們通過對開發中現有問題的分析,將UI和邏輯解耦設計了商卡系統,通過對CSS模塊的抽象,設計了商卡各個字段組件。商卡系統這使得我們能夠以低代碼平臺的方式實現通用的樣式跨端解決方案,從而達到降低成本和提高效率的目的。這個系統同時表明,前端工程師不僅需要關注樣式和解決渲染問題,還可以通過對CSS、渲染機制、數據結構和框架的深入理解來解決更廣泛的問題。