(轉(zhuǎn))大公司里怎樣開發(fā)和部署前端代碼轴猎?

作者:張?jiān)讫?br>

鏈接:https://www.zhihu.com/question/20790576/answer/32602154

來(lái)源:知乎

在我的印象中啥刻,facebook是這個(gè)領(lǐng)域的鼻祖刺下,有興趣共郭、有梯子的同學(xué)可以去看看facebook的頁(yè)面源代碼树埠,體會(huì)一下什么叫工程化糠馆。

接下來(lái),我想從原理展開講述怎憋,多圖又碌,較長(zhǎng),希望能有耐心看完绊袋。

---------------------------- 我是一條分割線 ----------------------------


讓我們返璞歸真毕匀,從原始的前端開發(fā)講起。上圖是一個(gè)“可愛”的index.html頁(yè)面和它的樣式文件a.css癌别,用文本編輯器寫代碼皂岔,無(wú)需編譯,本地預(yù)覽展姐,確認(rèn)OK躁垛,丟到服務(wù)器,等待用戶訪問(wèn)圾笨。前端就是這么簡(jiǎn)單教馆,好好玩啊,門檻好低啊擂达,分分鐘學(xué)會(huì)有木有活玲!


然后我們?cè)L問(wèn)頁(yè)面,看到效果谍婉,再查看一下網(wǎng)絡(luò)請(qǐng)求舒憾,200!不錯(cuò)穗熬,太?完美了镀迂!那么,研發(fā)完成唤蔗。探遵。。妓柜。了么箱季?

等等,這還沒完呢棍掐!對(duì)于大公司來(lái)說(shuō)藏雏,那些變態(tài)的訪問(wèn)量和性能指標(biāo),將會(huì)讓前端一點(diǎn)也不“好玩”作煌。

看看那個(gè)a.css的請(qǐng)求吧掘殴,如果每次用戶訪問(wèn)頁(yè)面都要加載赚瘦,是不是很影響性能,很浪費(fèi)帶寬啊奏寨,我們希望最好這樣:


利用304起意,讓瀏覽器使用本地緩存。但病瞳,這樣也就夠了嗎揽咕?不成!304叫協(xié)商緩存套菜,這玩意還是要和服務(wù)器通信一次心褐,我們的優(yōu)化級(jí)別是變態(tài)級(jí),所以必須徹底滅掉這個(gè)請(qǐng)求笼踩,變成這樣:


強(qiáng)制瀏覽器使用本地緩存(cache-control/expires)逗爹,不要和服務(wù)器通信。好了嚎于,請(qǐng)求方面的優(yōu)化已經(jīng)達(dá)到變態(tài)級(jí)別掘而,那問(wèn)題來(lái)了:你都不讓瀏覽器發(fā)資源請(qǐng)求了,這緩存咋更新于购?

很好袍睡,相信有人想到了辦法:通過(guò)更新頁(yè)面中引用的資源路徑,讓瀏覽器主動(dòng)放棄緩存肋僧,加載新資源斑胜。好像這樣:


下次上線,把鏈接地址改成新的版本嫌吠,就更新資源了不是止潘。OK,問(wèn)題解決了么辫诅?凭戴!當(dāng)然沒有!大公司的變態(tài)又來(lái)了炕矮,思考這種情況:


頁(yè)面引用了3個(gè)css么夫,而某次上線只改了其中的a.css,如果所有鏈接都更新版本肤视,就會(huì)導(dǎo)致b.css档痪,c.css的緩存也失效,那豈不是又有浪費(fèi)了邢滑?腐螟!

重新開啟變態(tài)模式,我們不難發(fā)現(xiàn),要解決這種問(wèn)題遭垛,必須讓url的修改與文件內(nèi)容關(guān)聯(lián)尼桶,也就是說(shuō)操灿,只有文件內(nèi)容變化锯仪,才會(huì)導(dǎo)致相應(yīng)url的變更,從而實(shí)現(xiàn)文件級(jí)別的精確緩存控制趾盐。

什么東西與文件內(nèi)容相關(guān)呢庶喜?我們會(huì)很自然的聯(lián)想到利用數(shù)據(jù)摘要要算法對(duì)文件求摘要信息,摘要信息與文件內(nèi)容一一對(duì)應(yīng)救鲤,就有了一種可以精確到單個(gè)文件粒度的緩存控制依據(jù)了久窟。好了,我們把url改成帶摘要信息的:


這回再有文件修改本缠,就只更新那個(gè)文件對(duì)應(yīng)的url了斥扛,想到這里貌似很完美了。你覺得這就夠了么丹锹?大公司告訴你:圖樣圖森破稀颁!

唉~~~~,讓我喘口氣

現(xiàn)代互聯(lián)網(wǎng)企業(yè)楣黍,為了進(jìn)一步提升網(wǎng)站性能匾灶,會(huì)把靜態(tài)資源和動(dòng)態(tài)網(wǎng)頁(yè)分集群部署,靜態(tài)資源會(huì)被部署到CDN節(jié)點(diǎn)上租漂,網(wǎng)頁(yè)中引用的資源也會(huì)變成對(duì)應(yīng)的部署路徑:


好了阶女,當(dāng)我要更新靜態(tài)資源的時(shí)候,同時(shí)也會(huì)更新html中的引用吧哩治,就好像這樣:


這次發(fā)布秃踩,同時(shí)改了頁(yè)面結(jié)構(gòu)和樣式,也更新了靜態(tài)資源對(duì)應(yīng)的url地址业筏,現(xiàn)在要發(fā)布代碼上線吞瞪,親愛的前端研發(fā)同學(xué),你來(lái)告訴我驾孔,咱們是先上線頁(yè)面芍秆,還是先上線靜態(tài)資源?

先部署頁(yè)面翠勉,再部署資源:在二者部署的時(shí)間間隔內(nèi)妖啥,如果有用戶訪問(wèn)頁(yè)面,就會(huì)在新的頁(yè)面結(jié)構(gòu)中加載舊的資源对碌,并且把這個(gè)舊版本的資源當(dāng)做新版本緩存起來(lái)荆虱,其結(jié)果就是:用戶訪問(wèn)到了一個(gè)樣式錯(cuò)亂的頁(yè)面,除非手動(dòng)刷新,否則在資源緩存過(guò)期之前怀读,頁(yè)面會(huì)一直執(zhí)行錯(cuò)誤诉位。

先部署資源,再部署頁(yè)面:在部署時(shí)間間隔之內(nèi)菜枷,有舊版本資源本地緩存的用戶訪問(wèn)網(wǎng)站苍糠,由于請(qǐng)求的頁(yè)面是舊版本的,資源引用沒有改變啤誊,瀏覽器將直接使用本地緩存岳瞭,這種情況下頁(yè)面展現(xiàn)正常;但沒有本地緩存或者緩存過(guò)期的用戶訪問(wèn)網(wǎng)站蚊锹,就會(huì)出現(xiàn)舊版本頁(yè)面加載新版本資源的情況瞳筏,導(dǎo)致頁(yè)面執(zhí)行錯(cuò)誤,但當(dāng)頁(yè)面完成部署牡昆,這部分用戶再次訪問(wèn)頁(yè)面又會(huì)恢復(fù)正常了姚炕。

好的,上面一坨分析想說(shuō)的就是:先部署誰(shuí)都不成丢烘!都會(huì)導(dǎo)致部署過(guò)程中發(fā)生頁(yè)面錯(cuò)亂的問(wèn)題柱宦。所以,訪問(wèn)量不大的項(xiàng)目铅协,可以讓研發(fā)同學(xué)苦逼一把捷沸,等到半夜偷偷上線,先上靜態(tài)資源狐史,再部署頁(yè)面痒给,看起來(lái)問(wèn)題少一些。

但是骏全,大公司超變態(tài)苍柏,沒有這樣的“絕對(duì)低峰期”,只有“相對(duì)低峰期”姜贡。So试吁,為了穩(wěn)定的服務(wù),還得繼續(xù)追求極致奥タ取熄捍!

這個(gè)奇葩問(wèn)題,起源于資源的覆蓋式發(fā)布母怜,用 待發(fā)布資源 覆蓋 已發(fā)布資源余耽,就有這種問(wèn)題。解決它也好辦苹熏,就是實(shí)現(xiàn)非覆蓋式發(fā)布碟贾。


看上圖币喧,用文件的摘要信息來(lái)對(duì)資源文件進(jìn)行重命名,把摘要信息放到資源文件發(fā)布路徑中袱耽,這樣杀餐,內(nèi)容有修改的資源就變成了一個(gè)新的文件發(fā)布到線上,不會(huì)覆蓋已有的資源文件朱巨。上線過(guò)程中史翘,先全量部署靜態(tài)資源,再灰度部署頁(yè)面蔬崩,整個(gè)問(wèn)題就比較完美的解決了恶座。

所以搀暑,大公司的靜態(tài)資源優(yōu)化方案沥阳,基本上要實(shí)現(xiàn)這么幾個(gè)東西:

配置超長(zhǎng)時(shí)間的本地緩存? ? ? ? ? ? ? ? —— 節(jié)省帶寬,提高性能

采用內(nèi)容摘要作為緩存更新依據(jù)? ? ? —— 精確的緩存控制

靜態(tài)資源CDN部署? ? ? ? ? ? ? ? ? ? ? ? ? —— 優(yōu)化網(wǎng)絡(luò)請(qǐng)求

更資源發(fā)布路徑實(shí)現(xiàn)非覆蓋式發(fā)布? —— 平滑升級(jí)

全套做下來(lái)自点,就是相對(duì)比較完整的靜態(tài)資源緩存控制方案了桐罕,而且,還要注意的是桂敛,靜態(tài)資源的緩存控制要求在前端所有靜態(tài)資源加載的位置都要做這樣的處理功炮。是的,所有术唬!什么js薪伏、css自不必說(shuō),還要包括js粗仓、css文件中引用的資源路徑嫁怀,由于涉及到摘要信息,引用資源的摘要信息也會(huì)引起引用文件本身的內(nèi)容改變借浊,從而形成級(jí)聯(lián)的摘要變化塘淑,大概示意圖就是:


好了,目前我們快速的學(xué)習(xí)了一下前端工程中關(guān)于靜態(tài)資源緩存要面臨的優(yōu)化和部署問(wèn)題蚂斤,新的問(wèn)題又來(lái)了:這?讓工程師怎么寫碼按孓唷!J镎簟捌治!

要解釋優(yōu)化與工程的結(jié)合處理思路,又會(huì)扯出一堆有關(guān)模塊化開發(fā)纽窟、資源加載肖油、請(qǐng)求合并、前端框架等等的工程問(wèn)題师倔,以上只是開了個(gè)頭构韵,解決方案才是精髓周蹭,但要說(shuō)的太多太多,有空再慢慢展開吧疲恢⌒桌剩或者大家可以去我的blog看其中的一些拆解:fouber/blog · GitHub

總之,前端性能優(yōu)化絕逼是一個(gè)工程問(wèn)題显拳!

以上不是我YY的棚愤,可以觀察 百度 或者 facebook 的頁(yè)面以及靜態(tài)資源源代碼,查看它們的資源引用路徑處理杂数,以及網(wǎng)絡(luò)請(qǐng)中靜態(tài)資源的緩存控制部分宛畦。再次贊嘆facebook的前端工程建設(shè)水平,跪舔了揍移。

建議前端工程師多多關(guān)注前端工程領(lǐng)域次和,也許有人會(huì)覺得自己的產(chǎn)品很小,不用這么變態(tài)那伐,但很有可能說(shuō)不定某天你就需要做出這樣的改變了踏施。而且,如果我們能把事情做得更極致罕邀,為什么不去做呢畅形?

另外,也不要覺得這些是運(yùn)維或者后端工程師要解決的問(wèn)題诉探。如果由其他角色來(lái)解決日熬,大家總是把自己不關(guān)心的問(wèn)題丟給別人,那么前端工程師的開發(fā)過(guò)程將受到極大的限制肾胯,這種情況甚至在某些大公司都不少見竖席!

媽媽,我再也不玩前端了阳液。怕敬。。帘皿。5555

========================[ 10.29更新 ]========================

這里更新一下:

在評(píng)論中东跪,

@陳鋼

@fleuria@林翔 提到了rails,剛剛?cè)タ戳艘幌掠チ铮_實(shí)是完成了以上所說(shuō)的優(yōu)化細(xì)節(jié)虽填,對(duì)整個(gè)靜態(tài)資源的管理上的思考于本答案描述的一致。很遺憾我直到今天(2014-10-29)才了解到rails中的assets pipeline曹动。這里向以上3位同學(xué)道歉斋日,原諒我的無(wú)知。

不過(guò)整篇回答沒有講解到具體的解決方案實(shí)現(xiàn)思路墓陈,只是介紹了前端在工程化方向的思考恶守,答案本身是可用的第献,了解rails的人也可以把此答案當(dāng)做是對(duì)rails中assets pipeline設(shè)計(jì)原理的分析。

rails通過(guò)把靜態(tài)資源變成erb模板文件兔港,然后加入<%= asset_path 'image.png' %>庸毫,上線前預(yù)編譯完成處理,不得不承認(rèn)衫樊,fis的實(shí)現(xiàn)思路跟這個(gè)幾乎完全一樣飒赃,但我們當(dāng)初確實(shí)不知道有rails的這套方案存在。

相關(guān)資料:英文版:The Asset Pipeline科侈,中文版:Asset Pipeline

========================[ 10.31更新 ]========================

F.I.S包裝了一個(gè)小工具载佳,完整實(shí)現(xiàn)整個(gè)回答所說(shuō)的最佳部署方案,并提供了源碼對(duì)照臀栈,可以感受一下項(xiàng)目源碼和部署代碼的對(duì)照蔫慧。

源碼項(xiàng)目:fouber/static-resource-digest-project · GitHub

部署項(xiàng)目:fouber/static-resource-digest-project-release · GitHub

部署項(xiàng)目可以理解為線上發(fā)布后的結(jié)果,可以在部署項(xiàng)目里查看所有資源引用的md5化處理挂脑。

這個(gè)示例也可以用于和assets pipeline做比較藕漱。fis沒有assets的目錄規(guī)范約束欲侮,而且可以以獨(dú)立工具的方式組合各種前端開發(fā)語(yǔ)言(coffee崭闲、less、sass/scss威蕉、stylus刁俭、markdown、jade韧涨、ejs牍戚、handlebars等等你能想到的),并與其他后端開發(fā)語(yǔ)言結(jié)合虑粥。

assets pipeline的設(shè)計(jì)思想值得獨(dú)立成工具用于前端工程如孝,fis就當(dāng)做這樣的一個(gè)選擇吧。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末娩贷,一起剝皮案震驚了整個(gè)濱河市第晰,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌彬祖,老刑警劉巖茁瘦,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異储笑,居然都是意外死亡甜熔,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門突倍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)腔稀,“玉大人盆昙,你說(shuō)我怎么就攤上這事『嘎玻” “怎么了弱左?”我有些...
    開封第一講書人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)炕淮。 經(jīng)常有香客問(wèn)我拆火,道長(zhǎng),這世上最難降的妖魔是什么涂圆? 我笑而不...
    開封第一講書人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任们镜,我火速辦了婚禮,結(jié)果婚禮上润歉,老公的妹妹穿的比我還像新娘模狭。我一直安慰自己,他們只是感情好踩衩,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開白布嚼鹉。 她就那樣靜靜地躺著,像睡著了一般驱富。 火紅的嫁衣襯著肌膚如雪锚赤。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,031評(píng)論 1 285
  • 那天褐鸥,我揣著相機(jī)與錄音线脚,去河邊找鬼。 笑死叫榕,一個(gè)胖子當(dāng)著我的面吹牛浑侥,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播晰绎,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼寓落,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了荞下?” 一聲冷哼從身側(cè)響起伶选,我...
    開封第一講書人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎锄弱,沒想到半個(gè)月后考蕾,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡会宪,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年肖卧,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片掸鹅。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡塞帐,死狀恐怖拦赠,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情葵姥,我是刑警寧澤荷鼠,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站榔幸,受9級(jí)特大地震影響允乐,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜削咆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一牍疏、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧拨齐,春花似錦鳞陨、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至歼狼,卻和暖如春掏导,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蹂匹。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工碘菜, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人限寞。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像仰坦,于是被迫代替她去往敵國(guó)和親履植。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容