自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Islands Architecture(孤島架構(gòu))在攜程新版首頁(yè)的實(shí)踐

開(kāi)發(fā) 新聞
本文通過(guò)攜程新版首頁(yè)項(xiàng)目系統(tǒng)的介紹了其整體架構(gòu)設(shè)計(jì),組件開(kāi)發(fā),數(shù)據(jù)配置的整個(gè)流程及實(shí)現(xiàn)原理,是對(duì)島嶼式架構(gòu)的一次實(shí)踐。

作者簡(jiǎn)介

攜程前端框架團(tuán)隊(duì),為攜程集團(tuán)各業(yè)務(wù)線在PC、H5、小程序等各階段提供優(yōu)秀的Web解決方案。當(dāng)前主要專(zhuān)注方向包括:新一代研發(fā)模式探索,Rust構(gòu)建工具鏈路升級(jí)、Serverless應(yīng)用框架開(kāi)發(fā)、在線文檔系統(tǒng)開(kāi)發(fā)、低代碼平臺(tái)搭建、適老化與無(wú)障礙探索等。

一、項(xiàng)目背景

2022,攜程PC版首頁(yè)終于迎來(lái)了首次改版,完成了用戶體驗(yàn)與技術(shù)棧的全面升級(jí)。

作為與用戶連接的重要入口,舊版PC首頁(yè)已經(jīng)陪伴攜程走過(guò)了22年,承擔(dān)著重要使命的同時(shí),也遇到了很多問(wèn)題:

維護(hù)/更新困難

祖?zhèn)鞔a黑盒邏輯過(guò)多,產(chǎn)品也難以推動(dòng)新需求的上線,舊版首頁(yè)已經(jīng)不能滿足高速發(fā)展的業(yè)務(wù)需求。

技術(shù)棧陳舊且不統(tǒng)一

互聯(lián)網(wǎng)技術(shù)日新月異,舊版首頁(yè)的整體架構(gòu)設(shè)計(jì)和技術(shù)棧都相對(duì)落后,且大首頁(yè)中各個(gè)組件的研發(fā)涉及多事業(yè)部合作,存在技術(shù)選型差異的問(wèn)題,增加了維護(hù)成本。

用戶體驗(yàn)有待改善

舊版攜程首頁(yè)的設(shè)計(jì)風(fēng)格沿用至今,在視覺(jué)和交互層面上,都已經(jīng)難以滿足用戶不斷提升的互聯(lián)網(wǎng)體驗(yàn)和審美需求。

綜合上述情況,為了給用戶提供更好的服務(wù),攜程首頁(yè)的整體改造迫在眉睫。

二、需求分析

攜程首頁(yè)改造需要考慮的核心問(wèn)題包括以下幾個(gè)方面:

技術(shù)選型

為了優(yōu)化首屏性能,提升用戶體驗(yàn),攜程新版首頁(yè)采用服務(wù)端渲染模式。在技術(shù)選型上,考慮到我們希望應(yīng)用層是輕量的,只做頁(yè)面HTML拼接和響應(yīng)兩件事情,最終決定基于Node.js構(gòu)建應(yīng)用載體,客戶端則統(tǒng)一使用公司主流的React技術(shù)棧。

跨團(tuán)隊(duì)合作

首頁(yè)作為攜程的重要門(mén)戶,涉及多業(yè)務(wù)線的流量入口。如圖1所示,我們可以將整個(gè)頁(yè)面進(jìn)行切割,按業(yè)務(wù)線劃分成多個(gè)組件模塊。

圖片

圖1 攜程首頁(yè)業(yè)務(wù)模塊切分圖

可以看到,整個(gè)頁(yè)面的研發(fā)是需要框架部門(mén)和各個(gè)事業(yè)部業(yè)務(wù)團(tuán)隊(duì)緊密合作才能完成的,這就需要一整套完善的跨團(tuán)隊(duì)合作模式。其中,我們希望業(yè)務(wù)團(tuán)隊(duì)只需要關(guān)注業(yè)務(wù)邏輯的實(shí)現(xiàn),完成組件模塊的開(kāi)發(fā)??蚣軋F(tuán)隊(duì)則負(fù)責(zé)提供:

  • 組件模塊服務(wù)端/客戶端構(gòu)建方案
  • 組件模塊服務(wù)端渲染方案
  • 應(yīng)用層面,實(shí)現(xiàn)頁(yè)面組裝及響應(yīng)
  • 組件模塊開(kāi)發(fā)環(huán)境

監(jiān)控及維護(hù)

上線后,我們需要時(shí)刻關(guān)注應(yīng)用狀態(tài),及時(shí)響應(yīng)異常情況。因此,需要對(duì)應(yīng)用及組件進(jìn)行埋點(diǎn)監(jiān)控。除此之外,由于需要跨團(tuán)隊(duì)合作,對(duì)于業(yè)務(wù)組件,我們希望各個(gè)業(yè)務(wù)團(tuán)隊(duì)不僅可以實(shí)現(xiàn)開(kāi)發(fā)/構(gòu)建自由,彼此獨(dú)立互不影響,在監(jiān)控及版本管理上也能實(shí)現(xiàn)自控。因此,我們將各個(gè)業(yè)務(wù)組件包裝成Node.js應(yīng)用,開(kāi)發(fā)人員可以直接在發(fā)布系統(tǒng)查看組件版本,完成發(fā)布/回退,也可以通過(guò)應(yīng)用ID在埋點(diǎn)管理平臺(tái)查看組件的相關(guān)埋點(diǎn)。

三、整體架構(gòu)設(shè)計(jì)

圖片

圖2 攜程首頁(yè)架構(gòu)設(shè)計(jì)圖

基于上述需求分析,攜程新版首頁(yè)的整體架構(gòu)設(shè)計(jì)如圖2所示,可以分為四個(gè)部分:

業(yè)務(wù)模塊開(kāi)發(fā)

我們將攜程首頁(yè)拆分為多個(gè)業(yè)務(wù)模塊,由各業(yè)務(wù)團(tuán)隊(duì)負(fù)責(zé)完成相應(yīng)組件的開(kāi)發(fā)。與常規(guī)React組件開(kāi)發(fā)不同的是,首先,開(kāi)發(fā)人員需要在配置文件中設(shè)置好模塊相關(guān)配置,如組件唯一ID;其次,組件開(kāi)發(fā)需遵循一些規(guī)則,如為防止出現(xiàn)樣式污染,我們強(qiáng)制使用CSS Modules;最后,我們支持服務(wù)端渲染組件,可以在服務(wù)端生命周期中拉取數(shù)據(jù),然后在服務(wù)端/客戶端使用。為了更好的輔助業(yè)務(wù)團(tuán)隊(duì)完成組件開(kāi)發(fā),框架團(tuán)隊(duì)會(huì)提供腳手架幫助創(chuàng)建組件模版,搭建開(kāi)發(fā)環(huán)境,模擬完整首頁(yè)場(chǎng)景。

業(yè)務(wù)模塊構(gòu)建

業(yè)務(wù)模塊開(kāi)發(fā)完成后,就需要構(gòu)建/發(fā)布至生產(chǎn)環(huán)境。整個(gè)構(gòu)建過(guò)程會(huì)在Pipeline中完成,開(kāi)發(fā)人員git push代碼后會(huì)自動(dòng)觸發(fā)。基于不同的entry及配置,我們會(huì)使用webpack分別完成客戶端及服務(wù)端代碼的生產(chǎn)態(tài)構(gòu)建,并將客戶端構(gòu)建產(chǎn)物(js+css)上傳至靜態(tài)資源管理系統(tǒng)。

之后,我們會(huì)將服務(wù)端構(gòu)建產(chǎn)物(js)連同組件及靜態(tài)資源版本相關(guān)信息包裝成一個(gè)Job應(yīng)用,該應(yīng)用中會(huì)有一個(gè)定時(shí)任務(wù)負(fù)責(zé)推送當(dāng)前版本信息,觸發(fā)組件完成服務(wù)端渲染,這里我們是使用定時(shí)器來(lái)實(shí)現(xiàn)定時(shí)任務(wù)的管理。最后,需要由開(kāi)發(fā)人員在發(fā)布系統(tǒng)中將構(gòu)建好的應(yīng)用鏡像部署到生產(chǎn)環(huán)境,完成組件的發(fā)布。

業(yè)務(wù)模塊服務(wù)端渲染

業(yè)務(wù)模塊的服務(wù)端渲染主要包括兩部分:

  • 在沙盒中完成服務(wù)端渲染
  • 將組件相關(guān)信息及渲染生成的html存到Redis中

我們將相關(guān)功能實(shí)現(xiàn)封裝成云函數(shù),作為服務(wù)提供出去。由于部分組件對(duì)服務(wù)端渲染具有數(shù)據(jù)更新的需求。因此,上文我們提到過(guò),Job應(yīng)用中會(huì)有一個(gè)定時(shí)任務(wù),負(fù)責(zé)觸發(fā)組件進(jìn)行服務(wù)端渲染,這里也就是會(huì)觸發(fā)云函數(shù)的調(diào)用。

應(yīng)用頁(yè)面組裝

最后,我們就需要在應(yīng)用中將所有的業(yè)務(wù)模塊拼裝起來(lái),定時(shí)從Redis中獲取組件相關(guān)信息,組裝成首頁(yè)html返回到客戶端。

四、整體架構(gòu)的核心功能實(shí)現(xiàn)

對(duì)應(yīng)上述首頁(yè)架構(gòu)設(shè)計(jì),我們簡(jiǎn)要介紹下部分核心功能的實(shí)現(xiàn):

4.1 搭建組件開(kāi)發(fā)環(huán)境,模擬首頁(yè)場(chǎng)景

我們會(huì)在開(kāi)發(fā)階段提供腳手架輔助業(yè)務(wù)團(tuán)隊(duì)開(kāi)發(fā)組件,其中一項(xiàng)重要功能就是搭建組件開(kāi)發(fā)環(huán)境。常規(guī)的webpack搭建React開(kāi)發(fā)環(huán)境我們這里就不贅述了,為了實(shí)現(xiàn)開(kāi)發(fā)環(huán)境的統(tǒng)一標(biāo)準(zhǔn)化,我們還做了以下事情:

  • 將webpack,babel的相關(guān)配置封裝到cli中,有選擇的提供可配置項(xiàng),規(guī)范化組件開(kāi)發(fā)環(huán)境
  • 對(duì)entry進(jìn)行收口。這里需要注意的是,服務(wù)端和客戶端的entry是不同的,對(duì)于客戶端entry,需要獲取服務(wù)端傳過(guò)來(lái)的數(shù)據(jù),并通過(guò)調(diào)用ReactDOM.render()完成渲染:
import React from 'react'
import ReactDOM from 'react-dom'
import Comp from 'COMP_PATH'
const render = async() => {
let data
// 獲取服務(wù)端傳遞到客戶端數(shù)據(jù)
const container = document.getElementById('__MFE___MODULE___DATA__')
if (container && container.textContent) {
try {
data = JSON.parse(container.textContent)
} catch(e) {
console.log(e)
}
}

const root = document.getElementById('__MODULE__')

// 客戶端渲染組件
if (module.hot) {
ReactDOM.render(<Comp serverData={data} />, root)
} else {
ReactDOM.hydrate(<ErrorBoundary><Comp serverData={data} /></ErrorBoundary>, root)
}
}
render()

對(duì)于服務(wù)端entry,則需要調(diào)用服務(wù)端生命周期拉取數(shù)據(jù),并調(diào)用renderToString()完成渲染:

import React from 'react'
import { renderToString } from 'react-dom/server'
import Comp from 'COMP_PATH'
const render = async() => {
let data
// 執(zhí)行服務(wù)端生命周期
if (Comp.getInitialProps) {
data = await Comp.getInitialProps(_ctx)
}
// 沙盒中傳入setMfeData方法,見(jiàn)下文中服務(wù)端渲染組件實(shí)現(xiàn)
setMfeData(data)

// 服務(wù)端渲染組件,返回html
return renderToString(<Comp serverData={data} />)
}
export default render()

搭建首頁(yè)場(chǎng)景。我們希望開(kāi)發(fā)人員在組件開(kāi)發(fā)時(shí),就可以看到其嵌入在整個(gè)首頁(yè)中的效果,而不是只能看到自己的組件。因此,我們?cè)诜?wù)端處理頁(yè)面請(qǐng)求時(shí),通過(guò)以下方式搭建了首頁(yè)場(chǎng)景:

  • 讀取首頁(yè)html文件(首次從線上拉?。?/li>
  • 解析/處理首頁(yè)html,移除當(dāng)前組件相關(guān)的線上script/link標(biāo)簽,添加開(kāi)發(fā)態(tài)構(gòu)建產(chǎn)物
  • 在沙盒中服務(wù)端渲染組件,替換首頁(yè)html中的組件html

4.2 SSR-Service 服務(wù)端渲染組件

我們會(huì)在沙盒中運(yùn)行服務(wù)端構(gòu)建生成的代碼(可結(jié)合上文中服務(wù)端entry看),完成組件渲染,得到服務(wù)端生命周期中返回的數(shù)據(jù)及組件html。

const vm = require('vm')
const render = async ({content, request}) => {
// content即為服務(wù)端構(gòu)建生成的代碼
const script = new vm.Script(content)
let moduleObj = {
exports: {}
}
let mfeEnv = 'prod'
let mfeData

// 基于云函數(shù)中的request模擬req
const _req = {
url: request.rawPath,
query: request.queryStringParameters,
headers: request.headers
}

let sandBox = {
...global,
process,
require,
module: moduleObj,
console,
_ctx: {
req: _req,
env: mfeEnv,
},
setMfeData: (data) => {
mfeData = data
}
}

// 沙盒中運(yùn)行,執(zhí)行服務(wù)端渲染
const ctx = vm.createContext(sandBox)
script.runInContext(ctx)
const comp = await sandBox.module.exports.default

return {
comp,
mfeData
}
}

4.3 整體頁(yè)面組裝

在首頁(yè)應(yīng)用中,我們會(huì)定時(shí)從redis中獲取組件相關(guān)信息,拼裝首頁(yè)html,在有客戶端請(qǐng)求進(jìn)入時(shí),直接返回緩存中的最新html。

let indexCache = ''
const renderPage = async (content) => {
// 加載首頁(yè)html
const $ = cheerio.load(content)
// 更新組件
for (let module of modules) {
try {
// moduleData為從redis取到的數(shù)據(jù)
let data = moduleData[module] || ''
if (!data) {
continue
}

data = typeof data == 'string' ? JSON.parse(data) : data
const {comp, version, mfeData, style} = data

// 更新組件相關(guān)的html,link,script標(biāo)簽
parse(module, comp, $, version, mfeData, style)
} catch(e) {
console.log(e)
}
}

// 生成html
const payload = $.html()
if (!payload) {
throw Error('renderPage error - html is null')
}

// 更新緩存
indexCache = payload
}

五、公共組件的渲染原理及技術(shù)細(xì)節(jié)

前面說(shuō)的是島嶼式架構(gòu)之首頁(yè)的整體架構(gòu)和獨(dú)立組件渲染的核心實(shí)現(xiàn),其中有些獨(dú)立組件(左側(cè)菜單欄,頭部等)除了在大首頁(yè)中使用,還會(huì)在其他的頁(yè)面中使用,這里就稱為公共組件。

5.1 公共組件需求點(diǎn)和痛點(diǎn)分析

在開(kāi)始開(kāi)發(fā)公共組件前,需要整理一下目前各個(gè)事業(yè)部的接入需求、成本及痛點(diǎn)。所以總結(jié)了以下問(wèn)題點(diǎn):

各個(gè)事業(yè)部的站點(diǎn)技術(shù)架構(gòu)不同

由于各個(gè)事業(yè)部的站點(diǎn)技術(shù)架構(gòu)不同。有的事業(yè)部可能是服務(wù)端渲染,有的可能為客戶端渲染 。在服務(wù)端渲染中,技術(shù)棧又可能出現(xiàn) JAVA 和NODE 。而在客戶端渲染中,各個(gè)事業(yè)部技術(shù)棧也不統(tǒng)一,有React、JQuery或者Vue等等前端框架。這里的問(wèn)題是各個(gè)事業(yè)部的技術(shù)棧的錯(cuò)綜復(fù)雜,如果分開(kāi)維護(hù)會(huì)帶來(lái)不同的版本及很高的維護(hù)成本。

所有頁(yè)面中的公共組件有變更時(shí)能否統(tǒng)一熱更新

當(dāng)公共組件的改動(dòng)或有問(wèn)題需要修復(fù)時(shí),不能讓所有的頁(yè)面都去變更公共組件,而是應(yīng)該我們變更后,所有頁(yè)面上的公共組件會(huì)靜默生效,各個(gè)事業(yè)部無(wú)需關(guān)心公共組件的變更。

公共組件的樣式如何不對(duì)頁(yè)面造成巨大影響

由于各個(gè)業(yè)務(wù)方的樣式風(fēng)格不同并且還存在一些全局的公共樣式,如何才能保證每個(gè)接入方為下圖的頁(yè)面布局方式,其頁(yè)面組成的方式為陰影部分是事業(yè)部所維護(hù)的組件,其他是公共組件。

圖片

由于歷史原因,舊版的公共組件已經(jīng)使用了很多年了,新版頭尾和舊版的頭尾布局構(gòu)造不同,要如何設(shè)計(jì),才能使其改動(dòng)最小,而不是去做很大的改動(dòng)去適配公共組件。新舊版大首頁(yè)頁(yè)面布局變化如下圖:

圖片

公共組件的渲染性能問(wèn)題

在背景中提到的不同形態(tài)的公共組件(比如有些不需要左側(cè)菜單或者頭部樣式的不同),如何在客戶端能第一時(shí)間展示給用戶相應(yīng)組件形態(tài)并且支持搜索引擎優(yōu)化(SEO)。當(dāng)多個(gè)公共組件在頁(yè)面中如何能快速進(jìn)行加載及渲染。

5.2 解決公共組件問(wèn)題和痛點(diǎn)

問(wèn)題一:各個(gè)事業(yè)部的站點(diǎn)技術(shù)架構(gòu)不同

前面提到了各個(gè)業(yè)務(wù)支線的技術(shù)棧不統(tǒng)一的問(wèn)題,并且還存在服務(wù)端和客戶端渲染的情況,如果為了多個(gè)技術(shù)棧去維護(hù)多個(gè)公共組件維護(hù)成本極高,且沒(méi)有辦法做到一套代碼多端使用。這里就從服務(wù)端和客戶端渲染分析,提供的相應(yīng)解決的方案

CSR(客戶端渲染)

在CSR中,技術(shù)棧也不同。由于有React、Vue、jQuery,所以我們需要提供的應(yīng)該是一個(gè)原生JS的公共組件,這樣能保證維護(hù)成本。但是大首頁(yè)的首屏技術(shù)棧已經(jīng)為React,再去開(kāi)發(fā)及維護(hù)一套原生JS組件顯得冗余。所以需要一個(gè)方案來(lái)支持多技術(shù)棧運(yùn)行,并且能夠兼容我們大首頁(yè)首屏的技術(shù)棧。

最終的方案是使用Preact,它很輕量,重點(diǎn)是它可以幫助我們解決多技術(shù)棧運(yùn)行并且能夠兼容React??扇f(wàn)一有頁(yè)面同樣在使用 Preact 和我們沖突怎么辦? 這里將Preact單獨(dú)打包出來(lái)common包并且重名了全局的變量。這樣即使頁(yè)面使用了Preact也不會(huì)和我們有沖突,在webpack的 externals 的選項(xiàng)中可以配置組件需要的包名。

{
//...
externals: {
preact: 'xxxxxx'
}
// ...
}

SSR(服務(wù)端渲染)

在SSR中,在技術(shù)棧上選擇了Preact,Preact 它同樣支持 SSR,可以構(gòu)建一個(gè)服務(wù)端的JS來(lái)支持SSR。因此我們的問(wèn)題就迎刃而解了,我們?cè)诮M件構(gòu)建的時(shí)候多生成一份 Preact 的 SSR 的 JS,用沙盒執(zhí)行服務(wù)端渲染輸出HTML并存儲(chǔ)。我們調(diào)研了以前的老的公共組件,全部攜程的業(yè)務(wù)線存在的技術(shù)棧只有兩種:JAVA、NODE,這樣就提供兩個(gè)接入方式的外殼即可——兩套不同語(yǔ)言的SDK及接入方式,其內(nèi)部都是獲取統(tǒng)一的公共組件HTML字符串供頁(yè)面使用。

{
// ...
resolve: {
extensions: ['.ts', '.tsx', '.js'],
alias: isPreact ? {
"react": "preact/compat",
"react-dom": "preact/compat", // Must be below test-utils
"react/jsx-runtime": "preact/jsx-runtime"
} : {},
}
// ...
}

?(React輕松轉(zhuǎn)換Preact)

問(wèn)題二:所有頁(yè)面中的公共組件有變更時(shí)能否統(tǒng)一熱更新

當(dāng)公共組件更新或者修復(fù)緊急的某些問(wèn)題,不應(yīng)該影響業(yè)務(wù)方頁(yè)面,應(yīng)該是自動(dòng)進(jìn)行更新,當(dāng)用戶訪問(wèn)頁(yè)面時(shí),看到就是最新的公共組件,因此我們沒(méi)有做類(lèi)似npm包多版本的方式進(jìn)行管理。

基于問(wèn)題一的基礎(chǔ)上:

SSR(服務(wù)端渲染)的情況

SSR的服務(wù)端的HTML從哪里來(lái)?HTML怎么樣才能是最新的?

我們需要構(gòu)建出來(lái)一份服務(wù)端的JS在沙盒中輸出HTML,存儲(chǔ)在了 Redis 中,將多個(gè)公共組件統(tǒng)一構(gòu)建出了多個(gè)HTML,分別存放在 Redis 里。業(yè)務(wù)方接入JAVA、NODE的SDK其實(shí)要做的只有一件事:守護(hù)進(jìn)程定時(shí)的去 Redis 里拿到最新的 HTML 結(jié)果。

圖片

CSR(客戶端渲染)的情況

CSR如何保持為最新公共組件的?

需要一臺(tái)機(jī)器同多語(yǔ)言技術(shù)棧SDK一樣,定時(shí)從 Redis 里讀取數(shù)據(jù),對(duì)外暴露一個(gè)接口,供客戶端的JS調(diào)用。這樣,每次用戶訪問(wèn)頁(yè)面的時(shí)候,客戶端JS會(huì)發(fā)起請(qǐng)求,保證用戶所看到的的內(nèi)容永遠(yuǎn)是最新的。

圖片

問(wèn)題三:樣式問(wèn)題

目前新版的相比之前舊版的公共組件在樣式和交互上更加復(fù)雜。由于左側(cè)菜單的存在,使得布局構(gòu)造不同,而且各個(gè)事業(yè)部的頁(yè)面樣式可能五花八門(mén),很難保證不會(huì)影響自身樣式和事件等問(wèn)題。

比如:如果使用flex的布局,需要在最外層套用一個(gè)div,如果不套用的話則需要在body元素上添加flex樣式,但是不能保證其他的事業(yè)部的頁(yè)面的 body 是否有其他的樣式,甚至body 內(nèi)是否存在其他的div元素等。還有很多事業(yè)部的頁(yè)面的類(lèi)似滾動(dòng)等事件監(jiān)聽(tīng)都是在body上進(jìn)行監(jiān)聽(tīng)的,所以如果外層套取div,這種形式會(huì)讓原來(lái)頁(yè)面的事件監(jiān)聽(tīng)滾動(dòng)非常麻煩,各個(gè)事業(yè)部原來(lái)監(jiān)聽(tīng)body的事件,需要一一進(jìn)行改動(dòng)。

圖片

觀察老項(xiàng)目發(fā)現(xiàn),之前的公共組件骨架有個(gè)最外層的div元素,并且有一個(gè)名為"container"的id,我們要做的就是將左側(cè)的菜單 fixed 在左側(cè)就好了.關(guān)于css的fixed的兼容性:

圖片

(樣式屬性兼容情況)

但是此時(shí)有個(gè)問(wèn)題是,我們的左菜單是可以展開(kāi)或收起的。所以在展開(kāi)和收起的時(shí)候需要一個(gè)全局的通信機(jī)制,當(dāng)左側(cè)的組件變化時(shí),在組件的內(nèi)部應(yīng)該觸發(fā)全局的通信鉤子,通知 id 為container 的div元素跟隨左菜單變化,達(dá)到 flex 布局的效果。

圖片

(左側(cè)菜單展開(kāi))

圖片

(左側(cè)菜單收起)

問(wèn)題四:性能問(wèn)題

基于問(wèn)題 1/2/3 大概已經(jīng)擬定了技術(shù)的方向,并且已經(jīng)能在各個(gè)事業(yè)部行的通了,證明思路是沒(méi)有問(wèn)題的,但是還有些個(gè)瑣碎的問(wèn)題需要考慮:

因?yàn)槭嵌〞r(shí)從服務(wù)端里進(jìn)行拉取,那么第一次沒(méi)有拉取時(shí)或者在客戶端渲染的情況下請(qǐng)求server是需要時(shí)間的,這樣請(qǐng)求回來(lái)HTML再進(jìn)行異步渲染,是否時(shí)間過(guò)長(zhǎng)?

為了解決上面的問(wèn)題,我們考慮了先準(zhǔn)備一個(gè)預(yù)渲染的HTML占位,類(lèi)似骨架屏的意思,此時(shí)就可以先進(jìn)行骨架屏的渲染,之后再異步拉取渲染,來(lái)解決異步渲染白屏等待時(shí)間的問(wèn)題。

圖片

多個(gè)公共組件的客戶端 JS 資源是否能夠合并,將Preact公共包也一起合并打包。

為了解決這個(gè)問(wèn)題,我們的那臺(tái)跑沙盒JOB機(jī)器就可以繼續(xù)做這件事情。因?yàn)槊總€(gè)組件構(gòu)建后有資源的版本,我們需要將版本存儲(chǔ)一份,一旦新的組件構(gòu)建后,拉取其他公共組件的資源版本,將多個(gè)JS組裝在一起。同時(shí)因?yàn)槲覀冇昧?Preact ,抽取了 Preact 為一個(gè)共同依賴,將它放在最前面,保證它的最先執(zhí)行。

圖片

六、公共組件的數(shù)據(jù)動(dòng)態(tài)配置系統(tǒng)

介紹完攜程新版大首頁(yè)的公共組件的渲染原理及技術(shù)細(xì)節(jié),接下來(lái)就是公共組件中的數(shù)據(jù)如何支持動(dòng)態(tài)配置。

6.1 為什么需要組件數(shù)據(jù)動(dòng)態(tài)配置系統(tǒng)

攜程PC版首頁(yè)進(jìn)行改版的過(guò)程中,按業(yè)務(wù)線將整個(gè)頁(yè)面劃分成了多個(gè)組件模塊,每個(gè)組件模塊內(nèi)都有需要展示的業(yè)務(wù)數(shù)據(jù)。而頁(yè)面上線之后,隨著產(chǎn)品需求的變更,業(yè)務(wù)數(shù)據(jù)會(huì)被頻繁的更新,如果每次更新數(shù)據(jù)都發(fā)布一次模塊代碼的話,這個(gè)成本和風(fēng)險(xiǎn)很大。

因此,將代碼和數(shù)據(jù)分開(kāi)發(fā)布是很有必要的,當(dāng)組件數(shù)據(jù)有改動(dòng)時(shí)無(wú)需發(fā)布組件,搭建一個(gè)專(zhuān)門(mén)用于發(fā)布大首頁(yè)數(shù)據(jù)配置的管理系統(tǒng)勢(shì)在必行。

6.2 組件數(shù)據(jù)動(dòng)態(tài)配置系統(tǒng)的需求分析

攜程大首頁(yè)數(shù)據(jù)配置管理系統(tǒng)的核心功能是完成數(shù)據(jù)配置的發(fā)布,并保證發(fā)布的可靠性和安全性,為了實(shí)現(xiàn)這個(gè)目標(biāo),此管理系統(tǒng)應(yīng)制定一套完整的數(shù)據(jù)檢驗(yàn)規(guī)范和發(fā)布流程,其中主要功能包括:

  • 規(guī)范數(shù)據(jù)配置上傳格式,本地配置數(shù)據(jù)與線上配置數(shù)據(jù)差異對(duì)比
  • 制定不同組件模塊的數(shù)據(jù)校驗(yàn)規(guī)則,并以此校驗(yàn)數(shù)據(jù)合法性
  • 數(shù)據(jù)配置發(fā)布前效果預(yù)覽,確保與線上其他組件模塊之間相互不影響
  • 更新線上頁(yè)面

6.3 組件數(shù)據(jù)動(dòng)態(tài)配置系統(tǒng)架構(gòu)設(shè)計(jì)

圖片

圖1 大首頁(yè)數(shù)據(jù)配置管理系統(tǒng)架構(gòu)設(shè)計(jì)

數(shù)據(jù)配置管理系統(tǒng)的架構(gòu)設(shè)計(jì) (如圖1 所示),為了實(shí)現(xiàn)需求分析中的四塊主要功能,整個(gè)管理系統(tǒng)主要搭建了兩個(gè)應(yīng)用:

前端應(yīng)用:以可視化界面的形式提供本地上傳配置文件,預(yù)覽數(shù)據(jù)效果以及更新頁(yè)面等功能,同時(shí)完成了數(shù)據(jù)校驗(yàn)和預(yù)覽檢測(cè)。

Node服務(wù):主要負(fù)責(zé)數(shù)據(jù)配置的處理及發(fā)布,將前端應(yīng)用上傳的數(shù)據(jù)配置保存到QConfig系統(tǒng)中。

其中,前端應(yīng)用提供的預(yù)覽功能的架構(gòu)設(shè)計(jì)如下圖2 所示:

圖片

圖2 預(yù)覽功能架構(gòu)設(shè)計(jì)

預(yù)覽功能的實(shí)現(xiàn)主要依賴三部分 (如圖2所示):

前端應(yīng)用:負(fù)責(zé)提供數(shù)據(jù)配置和展示頁(yè)面效果。

服務(wù)端渲染應(yīng)用:調(diào)用組件渲染函數(shù),根據(jù)數(shù)據(jù)配置渲染出當(dāng)前組件HTML,并從Redis拉取其他組件的HTML,而后組裝成一個(gè)完整頁(yè)面的HTML吐給前端應(yīng)用。

Redis:存儲(chǔ)所有組件模塊的HTML。

6.4 數(shù)據(jù)配置管理系統(tǒng)的核心功能實(shí)現(xiàn)

前面部分介紹了數(shù)據(jù)配置管理系統(tǒng)的架構(gòu)設(shè)計(jì),這里就架構(gòu)中核心功能部分的實(shí)現(xiàn)進(jìn)行詳細(xì)介紹,主要包括:

  • 數(shù)據(jù)配置規(guī)范及校驗(yàn)
  • 組件及頁(yè)面預(yù)覽

數(shù)據(jù)配置規(guī)范及數(shù)據(jù)校驗(yàn)

本地上傳的數(shù)據(jù)配置最終要傳給組件渲染出來(lái),而數(shù)據(jù)配置的上傳者不一定是組件的開(kāi)發(fā)者,上傳者并不一定清楚組件所需數(shù)據(jù)的類(lèi)型和結(jié)構(gòu),那么如何保證上傳的數(shù)據(jù)與組件要求的數(shù)據(jù)結(jié)構(gòu)保持一致呢?

這就需要管理系統(tǒng)制定一套數(shù)據(jù)配置規(guī)范來(lái)約束上傳的數(shù)據(jù),然而不同的組件,其數(shù)據(jù)結(jié)構(gòu)是不同的,那么每個(gè)組件都應(yīng)有一套自己的規(guī)范。管理系統(tǒng)提供了兩種制定數(shù)據(jù)規(guī)范的方式:

  • 錄入組件的基本信息,其中包括詳細(xì)的數(shù)據(jù)結(jié)構(gòu):數(shù)據(jù)名稱,數(shù)據(jù)類(lèi)型,必傳或可選等。
  • 組件使用TypeScript(推薦的組件開(kāi)發(fā)語(yǔ)言)語(yǔ)言開(kāi)發(fā)時(shí),可以上傳.d.ts聲明文件,系統(tǒng)會(huì)根據(jù)此文件解析出具體的組件信息及數(shù)據(jù)結(jié)構(gòu)。

規(guī)范制定完成之后管理系統(tǒng)會(huì)將其存儲(chǔ)起來(lái),每次有上傳者上傳某一組件的數(shù)據(jù)配置后(為方便上傳者修改數(shù)據(jù),管理系統(tǒng)規(guī)定數(shù)據(jù)配置以JSON文件的形式提供),系統(tǒng)會(huì)根據(jù)組件的數(shù)據(jù)規(guī)范校驗(yàn)上傳的數(shù)據(jù)配置,如果校驗(yàn)通過(guò)則會(huì)展示上傳數(shù)據(jù)與線上數(shù)據(jù)的差別,上傳者可進(jìn)行預(yù)覽操作;如果校驗(yàn)未通過(guò),則提示未通過(guò)原因及具體的不規(guī)范數(shù)據(jù),上傳者不可進(jìn)行后續(xù)的預(yù)覽操作,需重新上傳數(shù)據(jù)配置,直到校驗(yàn)通過(guò)。

組件及頁(yè)面預(yù)覽

此部分功能的核心實(shí)現(xiàn)在SSR Service 服務(wù)端渲染組件中(上文中有詳細(xì)介紹,這里不贅述),主要分為以下幾個(gè)步驟完成:

  • 應(yīng)用的組件渲染函數(shù)在接收到符合組件數(shù)據(jù)規(guī)范的配置數(shù)據(jù)后,將數(shù)據(jù)通過(guò)Props傳給組件,進(jìn)而render出當(dāng)前組件的HTML。
  • 從Redis中取出其他模塊的HTML 與當(dāng)前組件HTML拼接在一起,為了保證預(yù)覽的可靠性(減少其他模塊出錯(cuò)時(shí)對(duì)當(dāng)前組件的影響),其他模塊均使用生產(chǎn)態(tài)的HTML進(jìn)行拼接。為什么一定要將其他模塊的HTML拼接在一起預(yù)覽呢?為了測(cè)試配置數(shù)據(jù)發(fā)布之后對(duì)其他組件模塊的影響,若有影響則不能發(fā)布,從而保證線上頁(yè)面的安全性。

七、總結(jié)

本文通過(guò)攜程新版首頁(yè)項(xiàng)目系統(tǒng)的介紹了其整體架構(gòu)設(shè)計(jì),組件開(kāi)發(fā),數(shù)據(jù)配置的整個(gè)流程及實(shí)現(xiàn)原理,是對(duì)島嶼式架構(gòu)的一次實(shí)踐。希望能對(duì)大家今后跨團(tuán)隊(duì)組件式開(kāi)發(fā)的項(xiàng)目有所收獲。

責(zé)任編輯:張燕妮 來(lái)源: 攜程技術(shù)
相關(guān)推薦

2023-07-07 12:26:39

攜程開(kāi)發(fā)

2022-05-19 17:50:31

bookie集群延遲消息存儲(chǔ)服務(wù)

2023-06-06 16:01:00

Web優(yōu)化

2022-05-13 09:27:55

Widget機(jī)票業(yè)務(wù)App

2022-08-06 08:23:47

云計(jì)算公有云廠商成本

2022-03-30 18:39:51

TiDBHTAPCDP

2022-07-15 12:58:02

鴻蒙攜程華為

2022-08-20 07:46:03

Dynamo攜程數(shù)據(jù)庫(kù)

2017-10-09 09:12:35

攜程運(yùn)維架構(gòu)

2022-07-15 09:20:17

性能優(yōu)化方案

2022-07-08 09:38:27

攜程酒店Flutter技術(shù)跨平臺(tái)整合

2022-08-12 08:34:32

攜程數(shù)據(jù)庫(kù)上云

2023-02-08 16:34:05

數(shù)據(jù)庫(kù)工具

2016-12-15 21:41:15

大數(shù)據(jù)

2024-07-05 15:05:00

2022-08-11 22:03:59

Astro孤島架構(gòu)

2024-04-26 09:38:36

2022-06-17 10:44:49

實(shí)體鏈接系統(tǒng)旅游AI知識(shí)圖譜攜程

2022-05-13 07:22:39

攜程微服務(wù)SOA

2022-06-03 09:21:47

Svelte前端攜程
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)