一、背景
在電商領域內,商品是一個重要組成部分,與其對應的商品管理系統,則負責商品的新建、編輯、複製等功能。隨著商品管理系統的成熟穩定和業務上的擴充套件需求,催化出了商品中臺的誕生。它可以將現有商品功能最大效率的複用在很多業務上(公司內業務、公司外業務等)。而不是僅限於當前團隊的業務使用。
在設計商品中臺的前端系統時,我們使用了微前端和視覺化技術,其可以達到如下效果:
-
視覺化技術可以讓各個業務方的運營等相關人員,直觀的看到其配置的資料在頁面上的展示效果;
-
微前端可以幫助商品中臺更快更好的適配到各個業務方的專案中。
至此,本篇文章的背景介紹完畢,下面將會闡述如何在商品中臺前端系統中做微前端和視覺化。
二、視覺化技術
目前商品中臺的頁面如下圖所示:
圖中左側內容,就是商品視覺化,它的核心能力如下:
-
圖中右側所有的變動,都能在左側得到實時更新和展示,如主圖、 sku 組合、價格、圖文詳情、商品引數等功能。
-
圖中左側的視覺化區域是一個標準的 h5 頁面,可以把它看成一個子頁面,它與外層的父頁面在 ui 上是完全隔離的,同時在資料上又是共享的。
下面我將會對視覺化技術原理做完整闡述,請大家繼續往下看。
三、視覺化技術原理
視覺化整體技術原理圖如下:
從上圖可以獲得以下資訊:
-
子視窗用 iframe 展示;
-
子視窗用 vuex 做狀態管理;
-
子視窗和父視窗通過共享狀態 ( vue store )來完成資料通訊。
看到這,小夥伴可能會有以下疑問:
iframe 和父視窗的資料通訊是通過 postMessage 完成的,這裡為什麼不使用 postMessage 呢?
使用 vuex 完成 iframe 資料通訊是如何實現的呢?
回答第一個疑問:為什麼不使用 postMessage
實踐過的小夥伴應該深有體會,使用 postMessage 的話,資料通訊架構基本如下圖所示:
結合上圖可以分析出 postMessage 有以下缺點:
-
父視窗含有大量邏輯:父視窗需要將 vuex 的資料進行處理,然後通過 postMessage 進行傳輸;
-
資料通訊方式不純粹:vuex 和 postMessage 組合在一起,互相轉換,使資料通訊更加複雜和難以控制;
-
不支援 Vue.set , Vue.delete 等;
-
postMessage 只能同步字串,不能 fn。
綜合上面的缺點,在資料通訊方面,沒有使用 postMessage ,而是使用 vuex 替換掉 postMessage ,來完成 iframe 通訊。
回答第二個疑問:使用 vuex 完成 iframe 資料通訊是如何實現的呢?
這個問題的答案就是 uni-render 。通過它,可以做到讓子視窗通過 iframe 展示的同時,父子視窗共享 store 。那這個 uni-render 是什麼呢,可以繼續往下讀,將在下文給於第二個疑問的具體回答。
3.1 uni-render
uni-render 是一個讓父子視窗可以不用 postMessage 就能共享 vue store 的技術方案。它包含以下關鍵內容:
-
將 iframe 當成一個 dom 節點;
-
父視窗渲染子視窗( iframe )暴露的元件;
-
父子視窗共享 vue store;
uni-render 的技術原理圖如下:
這裡,我結合商品中臺配置視覺化區域做一個通俗解釋:
-
首先我們把 vue 專案設定為多頁應用,頁面分別是商品預覽頁、商品管理頁;
-
其次,調整 vue 入口,每個頁面對應一個入口;
-
編寫 iframe 元件和沙箱 vue;
-
在商品管理頁入口將沙箱 vue 和 store 掛載到 global 物件下;
-
在商品預覽入口將 global.parent 下的沙箱 vue 和 store 分別掛到 window 下和 global 下;
-
最後,其他的內容按照 vue 多頁寫法正常編寫即可。
通過上述 6 個步驟,就可以讓用 iframe 做展示容器的商品預覽頁和商品管理頁共享 store 啦。
這裡,小夥伴可能會有疑問,為什麼要使用沙箱 vue 呢?
這是因為 vue 的單例機制,子視窗(商品管理頁)由父視窗(商品管理頁) new Vue 渲染的, 因此在子視窗中使用 use 、 filter 、 mixin 、 全域性指令 、 全域性元件等, 會覆蓋父視窗 vue 物件。所以需要隔離出一個乾淨的和 vue 一樣的 vue ,然後用隔離出的沙箱 vue 來渲染子視窗(商品預覽頁)的內容。這樣就可以 達到父子視窗的 vue 互不影響。
下面將介紹一些具體實現,如 iframe 元件、沙箱 vue 、入口設計。iframe 元件的實現非常簡單,如下圖所示:
這個不再解釋了。
沙箱 vue 的實現非常巧妙,如下圖所示:
在 Function 上掛一個 $$clone 函式,這樣 vue 下就會有 $$clone 函式,通過執行 Vue.$$clone() ,將 vue 的各種屬性掛載到 SandboxVue 上。同時返回 SandboxVue 。即可得到一個乾淨的沙箱 vue 。
注意:這裡的 vue 指的是 vue2 ,目前 vue3 不是單例機制,在 vue3 中是不需要沙箱 vue 的。vue 多頁入口設計,如下圖所示:
對應上文通俗解釋 4、5步驟。
至此,uni-render 技術方案闡述完了。眾所周知,大多數 h5 、 pc 建站的資料通訊方案都繞不開 postMessage 。而我們通過 uni-render ,讓父視窗和 iframe 子視窗的資料通訊不再需要 postMessage ,同時只使用 vue 生態中的 vuex 做資料通訊。這帶來了非常多的好處,好處如下:
-
統一資料通訊方案;
-
對 store 資料的 watch 、 computed 、更加純正,資料通訊功能更加強大;
-
精簡程式碼,和 postMessage 永久告別;
-
支援同步函式,前方的路是星辰大海。
3.2 視覺化總結
綜上,商品中臺的視覺化介紹完了,我們通過 uni-render 技術方案讓商品預覽頁( iframe )和商品管理頁的資料通訊只通過 vuex 即可完成。讓視覺化的實時更新更加流暢,視覺化互動更加強大。
介紹完視覺化,下面我將繼續介紹商品中臺在微前端上的實踐,請大家繼續往下閱讀。
四、商品中臺微前端
這裡我們把商品中臺設計成了微前端架構,使其能夠完全適應複雜的外部業務。
這時,你可能會問,微前端是什麼?
4.1 微前端是什麼
概念如下:多個小型應用聚合為一個應用供使用者使用,每個小型應用可以獨立開發、獨立執行、獨立部署,與技術棧無關。大家可以把主應用想象成商場,那子應用就是商戶,這樣就好理解了。
注意:商品中臺不是主應用,它是一個嵌入外部業務的子應用,不具備外部業務嵌入它本身。
微前端和普通的前端有什麼區別呢?區別概括在下圖中:
結合微前端概念,再閱讀上圖,可以感受到微前端所帶來的優勢和價值。
4.2 為什麼要做微前端
整體概括下,主要有以下兩個目的:
-
將商品中臺更快、更好的嵌入到各個業務方專案中;
-
為後面主應用的設計做準備。
因此,我們把商品中臺專案設計成了微前端架構,它可以很好的解決前端中臺化所面臨的各種問題。知道了目的,那麼我們是如何去設計微前端的呢?
五、商品中臺微前端設計
目前微前端領域最主流的技術方案有以下兩種:
-
single-spa 技術方案;
-
iframe 技術方案;
基於這兩種技術方案,業界產出一些成熟的框架,如 qiankun 、 qingtian ( vivo 自研)等。設計架構如下簡圖所示:
下面將介紹商品中臺使用 qiankun 框架設計的微前端架構的技術實現。
5.1 qiankun 方案設計架構
為什麼使用 qiankun ,最核心的原因是:在國內,使用最多的微前端框架就是 qiankun 。整體效果也不錯,所以我們的中臺需要設計 qiankun 技術架構來適配那些基於 qiankun 的業務。
在說設計架構前,先給大家介紹下 qiankun 的技術原理,如下圖所示:
從圖中可以知道, qiankun 的核心在於建立微應用容器。瞭解技術核心後,下面開始介紹設計架構。設計架構如下圖所示:
從圖中可以知道,主要有 8 塊內容,下面將會依次介紹這 8 塊內容。
5.1.1 程式碼接入
主應用內註冊微應用
{
name: 'goods',
entry: initEntry('goods'),
container: '#root-view',
render,
// activeRule 作為區分不同微應用的關鍵字
activeRule: genActiveRule('/main/goods'),
props: msg
}
微應用入口
微應用打包
5.1.2 介面跨域
解決介面跨域,主要有以下兩種方式:
-
主應用轉發:介面的 host 與主應用一致,由主應用根據路徑關鍵字 cmmdy 進行轉發。
-
微應用配置:微應用服務端配置允許跨域
這裡我們選擇了第一種方式,也就是主應用轉發。
5.1.3 路由適配
這裡需要注意的是:微應用 router 需要新增 baseUrl ,並且要與主應用關鍵字 activeRule 保持一致。如下程式碼(簡寫)所示:
const KEY = 'product'
router = new VueRouter({
mode: 'history',
base: IN_CMS ? `/main/goods/${KEY}` : `/${KEY}`,
routes
})
這個 KEY 變數就是關鍵字。
5.1.4 多頁設定
目前多頁設定的程式碼如下圖所示:
每個頁面作為一個獨立的微應用引入, filename 設定與主應用 activeRule 值保持一致。
5.1.5 資料通訊
思考一個問題,主應用與微應用之間如何通訊?通訊這塊,主要有兩種方案:
-
initGlobalState:也是執行時通訊(官方方案);
-
window:掛載到 window 下。
initGlobalState 方案的優缺點如下:
優點:api 提供了資料的 change 事件,雙方均能監聽到資料變化。
缺點:微應用載入時,獲取初始資料的時機太晚 ,不適合用作微應用資料的初始化。
window 方案的優缺點如下:
優點:微應用程式碼全週期內均可以獲取資料,很好的避免官方方案中獲取資料太晚的問題。
缺點:需要自己處理對資料變化的監聽。
商品中臺採取的資料通訊方案是結合了上述兩種方案,互相取長補短,都有使用。
5.1.6 環境區分
主要有以下兩種場景:
-
區分 qiankun 與非 qiankun 技術棧:使用 window.__POWERED_BY_QIANKUN__ 即可判斷。
-
區分同樣使用 qiankun 的不同主應用:主應用與微應用之間約定引數,通過 window 物件或者生命週期函式中的 props 物件傳遞,來進行判斷。
5.1.7 本地聯調
思考一個問題,本地沒有主應用的服務,怎麼實現主應用與微應用間的快速聯調?解決方案如下:
主應用註冊微應用時,將 entry 設定為從 localstorage 中獲取,在 localstorage 中手動修改入口 entry 的值為微服務的本地地址,就可以實現本地的聯調。核心程式碼如下:
const timestamp = new Date().getTime()
const initEntry = (subSys) => {
const LS_KEY_ENTRY = `__entry__${subSys}`
const customEntry = localStorage.getItem(LS_KEY_ENTRY)
if (customEntry) {
return `${customEntry}`
}
if (subSys === 'goods') {
return `//vshop-commodity.vivo.com.cn/goods/?t=${timestamp}`
}
return `${location.origin}/${subSys}/?t=${timestamp}`
}
通過上述程式碼,即可在主應用中對入口地址進行動態適配,達到靈活聯調的目的。這塊還可以將其做成配置中心的形式,這樣就不用在 localstorage 中手動修改入口地址。
5.1.8 許可權管理
這部分是屬於業務強相關內容,做好頂層解耦即可,本文不再闡述。
5.1.9 qiankun 設計架構總結
到此,商品中臺的 qiankun 設計架構的核心內容闡述完了,使用 qiankun 的過程中,也遇到了一些問題,但使用這種流行框架的好處就是, 遇到問題時,可以去查閱相關文件和部落格,基本上都可以找到相關的解決思路。基於 qiankun 的設計架構,表現出了不錯的效果,也讓商品中臺能夠以 signle-spa 的 技術方案嵌入到其他業務專案中。
5.2 踩坑經驗分享
過程中遇到的坑較多,本文挑選幾個踩坑經驗分享出來。
5.2.1 uni-render 相遇 qiankun 跨域問題
現象:專案接入主應用,uni-render 控制的預覽頁面空白,控制檯報跨域錯誤。
原因:iframe 預覽頁面為商品中臺域名,而子應用接入主應用後為主應用域名,從而導致跨域。
解決方案:主應用、子應用 html 入口檔案頭部設定 document.domain ,使兩者 domain 保持一致。
5.2.3 uni-render 、qiankun 、 ueditor ”化學反應“
問題一:
現象:qiankun 子應用中富文字元件 ueditor 功能異常。
原因:qiankun 對 ueditor 劫持,導致 ueditor 某些變數無法獲取到。
解決方案:在主應用中,通過 excludeAssetFilter 讓 ueditor 的靜態資源不要被 qiankun 劫持處理。
問題二:
現象:子應用中 ueditor 的請求 url 報錯。
原因:ueditor 的請求 url 沒加主應用請求字首。
解決方案:子應用環境中,通過 ue.getActionUrl 給 ueditor 的請求 url 增加字首。
問題三:
現象:子應用中 ueditor 單圖上傳失敗。
原因:子應用設定了 domain , ueditor 的單圖上傳是通過 iframe 實現的,但是 iframe 沒有設定 domain ,導致上傳失敗。
解決方案:重寫 ueditor 的單圖上傳,將 iframe 改為 xhr 上傳。
至此,踩坑經驗也分享完了,還有一些踩坑,這裡就不再敘述了。在將幾個技術融合在一起的過程中,總是會有一些預想不到的問題,正所謂兵來將擋、水來土掩,我們用正確的心態去面對和解決這些問題即可。
六、總結
綜上,我們對商品中臺的視覺化和微前端做了整體的闡述,包含以下內容:
-
通過 uni-render 技術方案解決了視覺化頁面 iframe 和父頁面的資料通訊問題。
-
通過 qiankun 解決了商品中臺嵌入基於 single-spa 技術方案的外部業務。
-
uni-render 結合 qiankun 的踩坑經驗。
在解決一個場景或者問題時,技術的實現細節不是最重要的,最重要的是脫離技術的 Principles 。
最後用一句話結尾:Principles are higher than techniques. Principles produce techniques in an instant.
作者:vivo網際網路前端團隊-Yang Kun