嗶哩嗶哩(B站)的前端之路
過去的開發(fā)模式中,我們采用了以后端為主的 MVC 架構(gòu)方式。具體來說,每次項(xiàng)目評(píng)審后,前后端會(huì)先一起約定好接口,之后分別進(jìn)行開發(fā),開發(fā)完,前端需要把頁(yè)面提供給后端,后端配置上數(shù)據(jù),然后返回出來。正式基于這樣的開發(fā)模式,導(dǎo)致了總工作量的增加,同時(shí)溝通和聯(lián)調(diào)成本的消耗也十分顯著。
前后端分離
為了擺脫這種前后端過分依賴的情況,(其實(shí)前端也不想每次修改或者發(fā)布都要后端這邊發(fā)布,后端也不想每次前端只改個(gè)標(biāo)題,都要發(fā)布一下,影響服務(wù)的穩(wěn)定性),那么先從前后端分離開始吧~
前后端分離,最基本的兩種模式,有中間層和沒有中間層。
第一種,沒有web中間層就很簡(jiǎn)單,提供一個(gè)html模板放到靜態(tài)資源機(jī)上面,html模板里面引用了所需的js和css,訪問頁(yè)面的時(shí)候 把這個(gè)靜態(tài)模板返回給用戶,然后執(zhí)行js 在瀏覽器端通過ajax請(qǐng)求api拿到數(shù)據(jù),渲染頁(yè)面。
(前后端分離)
第二種,有node中間層,隨著2009年,Node的橫空出世,把前端慢慢的推向了后端,有了node之后,JavaScript可以做更多的事情。
B站,一開始做前后端分離的時(shí)候,也確實(shí)按照第一種方式去做的,現(xiàn)在還有一些頁(yè)面仍然是這種模式,例如:https://www.bilibili.com/account/history (可查看網(wǎng)頁(yè)源代碼)。對(duì)于不需要seo的頁(yè)面來說,是一個(gè)不錯(cuò)的方式。前端開發(fā)完成之后,通過webpack打包出對(duì)應(yīng)的js和css 上傳到cdn上面,然后將webpack打包出來的 引用了對(duì)應(yīng)的資源的html文件 上傳到一臺(tái)專門的靜態(tài)機(jī)上面,然后運(yùn)維配置路由 將頁(yè)面流量導(dǎo)過去就好了。后端的同學(xué)只需要提供對(duì)應(yīng)的api接口就可以。前后端分開維護(hù),自己按照自己的節(jié)奏走,降低了頁(yè)面與服務(wù)的耦合度
這種方式確實(shí)是一種很快能夠進(jìn)行前后端分離的方法。我們花了一段時(shí)間,在pc端使用vue 進(jìn)行重構(gòu),移動(dòng)端H5端 用react進(jìn)行了重構(gòu)。 進(jìn)度很快,但是也慢慢展現(xiàn)出了弊端。
首屏的時(shí)候,因?yàn)樗却Y源加載完成,然后再進(jìn)行渲染,會(huì)導(dǎo)致了首屏有白屏,如果是單頁(yè)面還好,如果是spa應(yīng)用 那么 他的加載時(shí)間就會(huì)變得很長(zhǎng),白屏?xí)r間會(huì)很影響用戶體驗(yàn),再有就是由于國(guó)內(nèi)的搜索公司 對(duì)于spa 應(yīng)用沒有很好的兼容,導(dǎo)致了客戶端渲染會(huì)對(duì)seo非常的不友好,有seo 需求的頁(yè)面就很迫切的需要服務(wù)端渲染。
(B站的首頁(yè),右邊模塊做了服務(wù)端渲染,左邊模塊沒有做服務(wù)端渲染)
那么,依賴node 進(jìn)行服務(wù)端渲染就被提上了日程。
選型
首先進(jìn)行node 框架的選型,市面上主流框架有三種,hapi express koa ,還有一些是經(jīng)過一些封裝和定制的框架,例如:eggjs等。
一開始我就把eggjs 排除在外了,第一因?yàn)閑ggjs,的功能很強(qiáng)大,有很多功能,多到有些根本用不著,從而導(dǎo)致了他會(huì)重 不輕量級(jí),第二,eggjs對(duì)于我來說是個(gè)黑盒,如果有什么問題,我解決起來將會(huì)花費(fèi)很長(zhǎng)的時(shí)間。(但是有很多地方 我還是借鑒了eggjs的,畢竟 很強(qiáng)大)
然后剩下的三種框架,express的使用相對(duì)簡(jiǎn)單,文檔也比較多,比較全面,所以我就選擇了express(后來還是重構(gòu)掉了 = =!)
然后是前端框架的選型 因?yàn)榍岸丝蚣苤髁鞯挠泻芏?,ng r v 等等,我站在用的是react和vue, 他們有個(gè)優(yōu)勢(shì)就是可以進(jìn)行前后端同構(gòu),一樣的邏輯不用寫兩份,很棒。
(同構(gòu)邏輯大概如此吧)
由于之前前后端分離的時(shí)候,pc上面已經(jīng)再用vue 進(jìn)行了重構(gòu),所以自然,這次服務(wù)端渲染也建立在vue上面 用的是vue ssr (這也為我后面的一個(gè)想法埋下了伏筆)
首先 我們選擇一個(gè)簡(jiǎn)單的頁(yè)面來做打樣,就用tag頁(yè)吧(被神選中的孩子:https://www.bilibili.com/tag/3503159 )
開發(fā)
目錄結(jié)構(gòu)
- client 【客戶端代碼 同構(gòu)代碼】
- build 【構(gòu)建相關(guān)】
- PC 【pc 端 vue項(xiàng)目】
- package.json
- config
- config.local.js 【本地開發(fā)配置】
- dist 【構(gòu)建目錄 掛載資源目錄】
- server 【服務(wù)端代碼】
- controller 【控制器】
- PC
- route.js
- core [核心代碼庫(kù)]
- service [方法庫(kù)]
- view [視圖]
- PC [vue 構(gòu)建后文件]
- tag.html [構(gòu)建后的模板]
- tag.json [構(gòu)建后的bundle]
- manifest.json
- apps.js [啟動(dòng)項(xiàng)]
在一開始設(shè)計(jì)的時(shí)候,客戶端代碼和服務(wù)端代碼放在同一個(gè)git庫(kù)里面,client里面是vue的代碼和webpack的打包邏輯。Server里是服務(wù)端的代碼,用的是類mvc結(jié)構(gòu)。
Client里面的vue的開發(fā)代碼,參照的就是vue ssr 官方給的例子來做的,用的是 createBundleRender方法
const { createBundleRenderer } = require('vue-server-renderer')
const renderer = createBundleRenderer(serverBundle, {
... })
構(gòu)建配置也是用的推薦的配置(參考:https://ssr.vuejs.org/zh/build-config.html)
簡(jiǎn)單來說,就是提供兩個(gè)入口,一個(gè)entry-client.js,主要是客戶端的執(zhí)行入口, 打包出來的是客戶端的引用代碼集合(manifest),另外一個(gè)是entry-server.js 打包出來的是服務(wù)端運(yùn)行的邏輯,整合到了bundle.json里面。然后傳給上面的createBundleRender方法就可以了
對(duì)于server文件夾里面的邏輯就非常簡(jiǎn)單了,core里面是啟動(dòng)項(xiàng)目的一些express的核心代碼 路由注冊(cè)什么的邏輯,值得一說的是,這邊的路由,借鑒了eggjs的路由注冊(cè)方式,稍微做了一點(diǎn)修改,用的是配置化的方式
配置優(yōu)于代碼,將訪問地址和對(duì)應(yīng)的controller 做了關(guān)聯(lián)。
這邊還有一個(gè)filter 其實(shí)就是在執(zhí)行controller之前 注冊(cè)進(jìn)一個(gè)middlewares 優(yōu)先執(zhí)行(其實(shí)這邊有點(diǎn)局限性,后處理沒法做)。
這邊我忽略了壓力測(cè)試,壓力測(cè)試我后面再說吧。
上線部署
上線部署用的是docker來部署的,配置是1C 4G的配置,用了兩個(gè)實(shí)例來運(yùn)行,(之前的構(gòu)建鏡像邏輯什么的 就不具體介紹了)
上線之后 每天的訪問量大概在100W左右,服務(wù)表現(xiàn)挺穩(wěn)定,期間出現(xiàn)了一個(gè)bug,就是 這邊有一個(gè)狀態(tài)與用戶的登陸狀態(tài)有關(guān),所以在服務(wù)端請(qǐng)求接口的時(shí)候,需要帶上cookie去請(qǐng)求,當(dāng)時(shí)忘記加了 后來加上,發(fā)現(xiàn)這個(gè)有點(diǎn)弊端比較麻煩。
需要在調(diào)用vuesssr的時(shí)候帶在context 里面,然后asyncData方法里面都要一層一層的傳遞,最后在action 里面拿到,帶給api。
這時(shí)候 我們?cè)賮砜聪聇ag頁(yè)。
ssr html
(不錯(cuò) 把數(shù)據(jù)都帶上了)
重構(gòu)
其實(shí)也沒過多久,大概三個(gè)月吧,node的版本漲的很快,在7.6版本之后,node 就支持了async/await語(yǔ)法糖,不需要再用yield 和*函數(shù)了,那么 無疑 koa 是對(duì)于await/async 支持最好的,我們果斷放棄了express,選擇了koa2 進(jìn)行重構(gòu)。
其實(shí)不單單是koa2對(duì)于async的支持,另外一個(gè)原因在于,我們koa 是洋蔥式的執(zhí)行方式,這樣就解決了上面我說的,只有controller的前處理,沒有后處理,這樣子我就可以很方便的去執(zhí)行前后處理。Koa的執(zhí)行效率也要好于express.
上面我說過,選擇vue 對(duì)后面重構(gòu)埋下了一個(gè)伏筆就在這里
首先,我給項(xiàng)目接入了配置中心,配置中心是干嘛用的呢? 用來記錄腳本的版本號(hào),這樣子我就可以很輕松的通過配置中心來控制前端頁(yè)面使用什么版本的腳本。而不用因?yàn)楦牧藗€(gè)腳本的版本號(hào),就需要進(jìn)行一次服務(wù)的重啟更新。
然后,我對(duì)vue的打包組件進(jìn)行了魔改,將他打包出來的文件帶上了對(duì)應(yīng)的版本號(hào)(版本號(hào)為hash值)。
這樣子我就可以通過配置中心來控制,到底我需要使用什么版本的vue 構(gòu)建產(chǎn)物,vue 前端邏輯更新了,我也只需要通過配置中心去分發(fā)給服務(wù)端,而不需要重啟服務(wù)了。一舉兩得。
配置托管
圖中 conf 就是配置中心,我們的server 會(huì)與conf進(jìn)行一個(gè)長(zhǎng)連接,如果conf中的配置更新了,就會(huì)通知到服務(wù),然后服務(wù)去拉去新的bundle和manifest 來進(jìn)行渲染。Ok 很棒
全民SSR
重構(gòu)完,那么再接入一個(gè)項(xiàng)目試試吧
首頁(yè),好,就首頁(yè)吧
首頁(yè)跟tag 頁(yè)
其實(shí)也都差不多,沒有什么特別的地方,唯一不同的就是 量比較大,可能一天有1000W的訪問量左右。那么我們就在CDN上面加上一層緩存,然后在我們服務(wù)上面也加上一層緩存。破費(fèi)(perfect)!~
服務(wù)端的緩存是通過文件落地來的,就是在第一個(gè)請(qǐng)求進(jìn)來的時(shí)候 在渲染完成之后,寫一個(gè)文件到本地,然后下次訪問的時(shí)候就可以直接用這個(gè)丟這個(gè)本地文件出去,不用再次渲染了,然后通過過期時(shí)間去控制。
這里發(fā)現(xiàn)了一個(gè)問題,就是每次更新 我都會(huì)將tag 和index 都進(jìn)行打包,而我需要的是對(duì)項(xiàng)目進(jìn)行單獨(dú)的打包,單獨(dú)的更新,能不能通過參數(shù)來控制我打包哪個(gè)呢,可以啊,首先先把webpack.config.js 重寫,公用部分整合,然后私有的分開寫成多個(gè),通過package.json里面來多配置幾個(gè)script就好啦
這樣子每次更新項(xiàng)目的時(shí)候,我就只需要打包對(duì)應(yīng)的項(xiàng)目就可以了,不會(huì)因?yàn)轫?xiàng)目接入了很多之后,打包和開發(fā)時(shí)候的熱加載變得很慢很慢。
責(zé)任編輯:售電衡衡
-
權(quán)威發(fā)布 | 新能源汽車產(chǎn)業(yè)頂層設(shè)計(jì)落地:鼓勵(lì)“光儲(chǔ)充放”,有序推進(jìn)氫燃料供給體系建設(shè)
2020-11-03新能源,汽車,產(chǎn)業(yè),設(shè)計(jì) -
中國(guó)自主研制的“人造太陽(yáng)”重力支撐設(shè)備正式啟運(yùn)
2020-09-14核聚變,ITER,核電 -
探索 | 既耗能又可供能的數(shù)據(jù)中心 打造融合型綜合能源系統(tǒng)
2020-06-16綜合能源服務(wù),新能源消納,能源互聯(lián)網(wǎng)
-
新基建助推 數(shù)據(jù)中心建設(shè)將迎爆發(fā)期
2020-06-16數(shù)據(jù)中心,能源互聯(lián)網(wǎng),電力新基建 -
泛在電力物聯(lián)網(wǎng)建設(shè)下看電網(wǎng)企業(yè)數(shù)據(jù)變現(xiàn)之路
2019-11-12泛在電力物聯(lián)網(wǎng) -
泛在電力物聯(lián)網(wǎng)建設(shè)典型實(shí)踐案例
2019-10-15泛在電力物聯(lián)網(wǎng)案例
-
新基建之充電樁“火”了 想進(jìn)這個(gè)行業(yè)要“心里有底”
2020-06-16充電樁,充電基礎(chǔ)設(shè)施,電力新基建 -
燃料電池汽車駛?cè)雽こ0傩占疫€要多久?
-
備戰(zhàn)全面電動(dòng)化 多部委及央企“定調(diào)”充電樁配套節(jié)奏
-
權(quán)威發(fā)布 | 新能源汽車產(chǎn)業(yè)頂層設(shè)計(jì)落地:鼓勵(lì)“光儲(chǔ)充放”,有序推進(jìn)氫燃料供給體系建設(shè)
2020-11-03新能源,汽車,產(chǎn)業(yè),設(shè)計(jì) -
中國(guó)自主研制的“人造太陽(yáng)”重力支撐設(shè)備正式啟運(yùn)
2020-09-14核聚變,ITER,核電 -
能源革命和電改政策紅利將長(zhǎng)期助力儲(chǔ)能行業(yè)發(fā)展
-
探索 | 既耗能又可供能的數(shù)據(jù)中心 打造融合型綜合能源系統(tǒng)
2020-06-16綜合能源服務(wù),新能源消納,能源互聯(lián)網(wǎng) -
5G新基建助力智能電網(wǎng)發(fā)展
2020-06-125G,智能電網(wǎng),配電網(wǎng) -
從智能電網(wǎng)到智能城市
-
山西省首座電力與通信共享電力鐵塔試點(diǎn)成功
-
中國(guó)電建公司公共資源交易服務(wù)平臺(tái)摘得電力創(chuàng)新大獎(jiǎng)
-
電力系統(tǒng)對(duì)UPS的技術(shù)要求