本文主要以 vivo 商城的前端開發經驗,結合實際案例,探讨如何在業務版本高速疊代的情況下,進行大幅度的架構改造,讓前端開發從蠻荒的 Java Web 時代躍進到高速的資訊時代。
本文首發于 vivo網際網路技術 微信公衆号
連結: https://mp.weixin.qq.com/s/vD9yvYNaxTQBLABik6aqNg
作者:官網商城前端團隊
【背景】
一年前 vivo 商城還是以 Java 為技術核心,前背景一起,Java 既要負責服務、資料庫,也要負責頁面的渲染。在早期這種開發模式也能夠很好的運作。然而随着業務疊代的加快,前端技術的發展,這種開發模式的弊端越來越明顯。主要突出的有以下兩個方面:
- 前端技術棧架構繁雜且陳舊,導緻疊代速度很難提升
到2018年12月,整個商城前端系統随着不同需求疊加積累的原因,造成了不同頁面使用不同的技術,比較典型的有jQuery,Vue,FreeMarker,artTemplate,這些不同的技術棧從開發來看,相同的内容,在不同的頁面可能使用不同的技術棧,導緻需要開發很多遍,對開發、測試來說工作量都是成倍的增長。
- 無法适用多端開發的需求
自2017年微信上線小程式功能後,各種小程式如雨後春筍般出現,vivo 商城一開始也推出了自己的微信小程式,然而由于業務發展,需要适配的端越來越多,原先使用原生開發的小程式方式,無法做到一套代碼編到多個平台。
為了提升開發效率,滿足高速發展的業務需求,在過去的一年裡,我們通過對商城内外部系統的全面分析,按照分層的邏輯整理出前端架構的更新指導說明。
【分層架構】
在《前端架構-從入門到微前端》一書中提到,前端架構自上而下可以設計為四個層次,分别為系統級、應用級、子產品級、代碼級,我們通過這四個層次來分析vivo商城前端架構更新過程中的種種思考和實踐,最終形成了一套以Vue + Node.js為核心的全端架構方案。
技術演進過程:
分層架構實施圖:
一、系統級
即應用在整個系統内的關系,比如如何和背景通訊,如何與其他應用內建。針對這一級别,我們進行了前後端分離、多端統一、BFF、SSR等方面的探索和實踐。
1、前後端分離
架構更新,第一步面臨的問題便是前後端分離,vivo 商城仍然處于業務高速發展時期,不能因為技術重構而停下業務版本的疊代, 是以業務版本疊代必須要和前後端分離同時進行,那怎麼才能做到雙線并行,平滑更新?
這裡舉個小例子:當我們分離完成訂單子產品後,就會通過 Nginx 将關于訂單子產品的所有請求轉發到新的靜态資源服務上,如下圖:
通過前後端分離,我們徹底解放前端,讓前端開發效率提升了至少2個檔次。
更多技術細節比如:新老頁面如何同步資訊,如何容災容錯等等,請關注我們的系列第二篇《vivo商城前端架構更新(二):前後端分離篇》
2、多端統一
從 PC 浏覽器,到移動端浏覽器、到 App 内嵌,再到各個小程式,再到服務端、用戶端。繁榮的生态,猶如百家争鳴,百花齊放。然而,繁榮的背後,對前端工程師的挑戰,則與日俱增。
我們承接的端越來越多,新的端不斷的出現,如小程式、快應用等。前端工程師陷入了如下套娃陷阱:
- 現有代碼、新代碼要适配新的端開發場景
- 已經适配新的端開發場景的代碼成為了現有代碼
- 循環...
我們希望解決這種問題,希望做到一套技術架構方案【代碼】,可以覆寫現在的端和未來的端。
通俗點說,我們希望做到如下圖所示的能力:
在這種前端開發背景下,多端統一出現了。它是前端的一個未來趨勢,它也是解決上面套娃陷阱的一劑良藥。
更多細節内容,請關注我們的系列第三篇《vivo商城前端架構更新(三):多端實踐篇》
3、BFF
- 業務現狀
随着端的增多,新的接口數量呈現爆發式增長,老的接口為了适配各端,也會增加了各種不同的字段,導緻前後端适配多端的工作量越來越大。但是抽象來看,大部分端的變動都是UI層級的變動,很少有底層服務的改變,是以帶來了一個問題接口究竟是面向UI,還是面向通用服務?
為了解決這個問題,Sam Newman 發表了一篇文章,講述了這種體驗者專用 API 的方式,并将其稱為BFF(Backends for Frontends)模式。
在BFF理念中,最重要的一點是:服務自治,誰使用誰開發。服務自治減少了溝通成本,帶來了靈活和高效。
- 關鍵技術
商城前端積極适應前端技術的發展,為了提供一流的使用者體驗,積極推動BFF層在商城業務中的實作。
- 直連Dubbo:
Apache Dubbo 是一款高性能、輕量級的開源Java RPC架構,它提供了三大核心能力:面向接口的遠端方法調用,智能容錯和負載均衡,以及服務自動注冊和發現。
我們使用了社群提供的 Dubbo2.js 進行 Dubbo 服務的調用,并且做了一些封裝來優化發開體驗。
- 內建GraphQL網關:
GraphQL 是一個開源的 API 資料查詢和操作語言及實作為了實作上述操作的相應運作環境。相較于REST以及其他 web service架構提供了一種高效、強大和靈活的開發 web APIs的方式。
(1)請求你所要的資料 不多不少
(2)擷取多個資源 隻用一個請求
(3)強大的API調試工具
4、SSR
自從前後端分離後,前端采用了SPA技術,走的都是CSR(用戶端渲染)的模式。使用CSR的優勢在于節省後端資源、局部重新整理等,但随着應用的日益複雜,首屏渲染時間不斷變長, 并且存在嚴重的 SEO 問題。是以為了解決SPA應用遇到的這些問題, 我們必須考慮 SSR。
SSR 即服務端渲染,是指由伺服器端完成頁面的HTML 結構拼接,并且直接将拼接好的HTML發送到浏覽器,然後為其綁定狀态與事件,成為完全可互動頁面的處理技術。
主要優勢在于:
- 更好的 SEO,由于搜尋引擎爬蟲抓取工具可以直接檢視完全渲染的頁面。
- 更快的内容到達時間 (time-to-content),特别是對于緩慢的網絡情況或運作緩慢的裝置。
為了避免重複造輪子,我們使用了社群非常優秀的SSR架構nuxt,通過一系列的優化,比如:頁面緩存、元件緩存、API緩存、最小化渲染等方式,最終讓我們頁面在500ms内就能全部展示,這是使用者體驗上的極大提升。
二、應用級
即應用外部的整體架構,如多個應用之間如何共享元件、如何通信、如何開發通用腳手架等。在應用級别的架構上面,我們主要沉澱了适用于商城的UI庫,為其他商城衍生項目提供基礎元件支援。
1、元件庫
移動端的設計千變萬化,市場上非常流行的移動端的元件庫比如antd-mobile , vant,他們對于開發通用型的 App,非常高效且美觀,然而大部分自主研發的 App都有自己的一套設計風格和理論。如果使用流行的元件庫,就會出現頻繁需要修改源碼,以适應UI風格的變化。這樣的工作量日積月累,就會變得越來越大。是以我們還是建議如果是做自己特色的App,還是要自建UI庫。如果感覺自建UI庫難度較大,可以先fork一份流行的元件庫,學習其中的各種實作,慢慢形成自己的UI庫。
商城也實作了自己的UI庫,目前已經在商城、秒殺、vivo 内購、v客聯盟等應用中廣泛使用,極大的提高了開發效率。如下圖:
三、子產品級
應用内部的子產品架構,如代碼的子產品化、資料和狀态的管理等,在項目中比較典型的是我們設計了針對 Vue 的極緻子產品化方案,頂層 page 設計,資料自治等方面的工作。
1、極緻子產品化
我們的方案擯棄了官方推薦的按檔案類型組織子產品,而采用按照功能組織子產品,這種"可插拔式"元件設計,讓代碼按照功能聚合。
一個檔案包裡面包含該功能的所有實作,包括圖檔、樣式、腳本、資料流、元件等等,這樣另一個項目想要使用,直接遷移即可,極大地減少了遷移的成本,并且還有一個優勢是,如果這個功能以後下架,直接删除即可,不必各個檔案夾下找檔案,極大地提升了代碼的簡潔性和可維護性。
➜ confirm git:(dev_abtest_gray) tree
.
├── api.js // 接口資源
├── components // 内部元件
│ ├── component1
│ │ ├── images
│ │ ├── index.scss
│ │ └── index.vue
│ ├── component2
├── images // 圖檔資源
├── index.scss // 樣式資源
├── index.vue // 邏輯實作
├── module.js // 資料流
└── trackData.js // 埋點
2、頂層 page 設計
所有的頁面都會有很多通用的功能,比如加載前的骨架圖、出錯後的處理邏輯、資料采集邏輯、上拉重新整理、下滑分頁加載,全局 iOS 适配等等,這些功能在邏輯上是需要抽象的,避免各個頁面多次實作,導緻浪費開發資源和降低程式的可維護性。
針對此我們實作了一個頂級 page 元件,所有的頁面都繼承于這個 page 。做到通用性的功能全局管理。
頂級 page 的部分 API 使用如下:
<template>
<v-page
// 頁面是否完成
:pageInit="true"
// 頁面是否出錯
:pageError="false"
// 頁面監控開啟,包括pv,uv,渲染時間
:pageMonitor="true"
// 上拉重新整理
@pullDownRefresh="pullDownRefresh"
// 下拉監聽
@reachBottom="reachBottom"
// 滾動監聽
@pageScroll="pageScroll"
>
<template slot="header">
<!-- 自定義頭部,如果沒有則使用預設頂部導航菜單 -->
<Header />
</template>
<template slot="skeleton">
<!-- 自己實作的骨架圖,如果沒有則使用預設骨架圖-->
<Skeleton />
</template>
<template slot="footer">
<!-- 自己實作的底部,吸底顯示,如果沒有則不顯示-->
<Footer />
</template>
</v-page>
</template>
3、資料自治
本着誰使用,誰負責的原則,對于頁面中的資料流也是一樣的,我們開發了針對page的全局mixin,負責自動注冊和解除安裝頁面資料,并将各個頁面之間的資料進行隔離。
import { mapState, mapGetters, mapActions, mapMutations } from 'vuex'
export default (name, module) => ({
computed: {
...mapState(name, Object.keys(module.state)),
...mapGetters(name, Object.keys(module.getters))
},
created () {
// todo 要加判斷是否已經注冊,動态注冊子產品
if (!(name in this.$store._modules.root._children)) {
this.$store.registerModule(name, module)
}
},
methods: {
...mapActions(name, Object.keys(module.actions)),
...mapMutations(name, Object.keys(module.mutations))
}
})
四、代碼級
當我們開始編寫代碼的時候,就要考慮代碼的規範和品質。規範的目的是為了提升維護性,而品質則是開發的門面,一個好的項目離不開這這兩個内容的限制。
1、規範
一個好的規範應該做到簡單、好記、易于執行。為了實作這個目标,我們制定了一系列的規範,最主要的是開發規範、送出規範。
每個項目組的規範可能都不一樣,需要根據自己的項目特色,可以參考優秀的項目實踐,整理出自己的項目規範,小組内部讨論優化,達成一緻意見,最後釋出執行。每一位新進項目組的成員,首先要做的就是學習這些規範,用規範引導開發。
(1)開發規範
包含但不局限于以下内容:命名規範、HTML 規範、css規範、js規範。
(2)送出規範:
為了規範送出代碼,進而友善開發者追蹤項目的開發資訊和功能特性,我們封裝了**@vivo/commit**,對我們的送出進行強制校驗。比如:
每一條commit由四個部分組成,如下圖:
- 修改類型
style: 樣式修改
fix: bug修複
feat: 功能開發
refactor: 代碼重構
test: 測試類修改
doc: 文檔更新
conf: 配置修改
merge: 代碼合并
- 影響子產品
每一條commit,應明确指出其影響範圍是哪個子產品,如果是通用子產品,注釋上(全局)字樣,友善code reviewer對方案進行評估
- 跟蹤單号
每一條commit,必須要有單号,每個公司都有自己的缺陷跟蹤系統,單号的目的是為了讓每一條送出有據可循,友善後續對問題的回溯。
- 問題描述
問題描述應該簡潔明了,讓其他人一看就知道這條commit修改了什麼,禁用一些通用描述,比如:'修改了一個bug','添加了一個功能'
2、品質
關于品質我們從兩個方面進行提升,代碼檢視 和 代碼覆寫率。
(1)代碼檢視:
為了提高代碼檢視的效率,調研了市場上面衆多的代碼檢視工具,好用都需要收費,并且功能比較複雜,比如:upsource。于是開發了一個基礎vscode的code review插件,支援GitLab,實時消息通知。
添加評論
(2)代碼覆寫率:
商城的業務疊代速度非常快,使得開發單元測試開發的成本非常大,然而我們有時又想看看測試場景的覆寫情況,為了實作這些目标,我們研發了內建測試代碼覆寫率平台。通過這個平台可以清晰的看到每一行代碼被測試執行的情況。保證了開發的品質,并能給測試提供精确的指導建議。
- 服務端架構
- 前台架構
- 自動內建 GitLab
- 結果展示
【小結】
本篇文章介紹了 vivo 商城架構更新的背景,并從系統級、應用級、子產品級、代碼級四個層次,總結了 vivo 商城前端架構更新過程中的種種實踐和探索,希望能給有類似需求的團隊帶來幫助。
我們在前端技術方面的探索并未結束,作為前端架構更新的第一篇,後面會圍繞架構更新帶來一系列的文章,為大家更詳細的講解其中的難點和經驗,敬請期待。
更多内容敬請關注vivo 網際網路技術微信公衆号
注:轉載文章請先與微信号:Labs2020聯系
分享 vivo 網際網路技術幹貨與沙龍活動,推薦最新行業動态與熱門會議。