做過(guò)前端開(kāi)發(fā)都知道前端的工作內(nèi)容是很多的,對(duì)于HTML、CSS、Javascript、Image、Flash等各種內(nèi)容的使用。為了更好提升應(yīng)用的性能,我們需要對(duì)各種資源內(nèi)容進(jìn)行不同方面的優(yōu)化。
對(duì)用戶(hù)而言,優(yōu)化可以讓?xiě)?yīng)用的響應(yīng)速度加快,加載更加迅速,可以帶來(lái)更好的使用體驗(yàn)。
對(duì)于服務(wù)商而言,前端優(yōu)化能夠減少頁(yè)面請(qǐng)求數(shù)量,寬帶所占帶寬,有效的節(jié)省資源。
前端優(yōu)化的內(nèi)容很多,按照粒度等級(jí)劃分可以大致分為兩類(lèi):頁(yè)面優(yōu)化級(jí)別和代碼級(jí)別優(yōu)化。
頁(yè)面優(yōu)化主要針對(duì)頁(yè)面加載環(huán)節(jié),包括:HTTP請(qǐng)求數(shù)、腳本的無(wú)阻塞加載、內(nèi)聯(lián)腳本的位置優(yōu)化等內(nèi)容。代碼優(yōu)化包括:Javascript中的DOM操作優(yōu)化、CSS選擇符優(yōu)化、圖片優(yōu)化以及HTML結(jié)構(gòu)優(yōu)化等內(nèi)容。
代碼級(jí)別優(yōu)化則更關(guān)注數(shù)據(jù)請(qǐng)求,很重要的一條就是減少HTTP請(qǐng)求的數(shù)量。一個(gè)完整的HTTP請(qǐng)求需要經(jīng)過(guò)路由查找,TCP握手,發(fā)送請(qǐng)求,服務(wù)器響應(yīng)和瀏覽器接收等一些列過(guò)程。對(duì)于小文件,實(shí)際下載文件的時(shí)間對(duì)于整個(gè)請(qǐng)求的時(shí)間占比很低,因此需要將小文件合并為大文件來(lái)傳輸。
頁(yè)面級(jí)別:提升頁(yè)面加載速度加載優(yōu)化是為了解決頁(yè)面內(nèi)容加載速度受限于網(wǎng)絡(luò)帶寬,過(guò)于耗時(shí)的問(wèn)題,主要手段有:
項(xiàng)目打包優(yōu)化
Webpack 是一個(gè)前端資源加載/打包工具。它將根據(jù)模塊的依賴(lài)關(guān)系進(jìn)行靜態(tài)分析,然后將這些模塊按照指定的規(guī)則生成對(duì)應(yīng)的靜態(tài)資源。通常我們使用Webpack將多種靜態(tài)資源js、css、less 轉(zhuǎn)換成一個(gè)靜態(tài)文件,減少了頁(yè)面的請(qǐng)求。
核心概念有:
Output:告訴 webpack 在哪里輸出它所創(chuàng)建的 bundles,以及如何命名這些文件,默認(rèn)值為 ./dist。
Module:Webpack 會(huì)從配置的 Entry 開(kāi)始遞歸找出所有依賴(lài)的模塊。
Chunk:一個(gè) Chunk 由多個(gè)模塊組合而成,用于代碼合并與分割。
Loader:loader 可以將所有類(lèi)型的文件轉(zhuǎn)換為 webpack 能夠處理的有效模塊,然后你就可以利用 webpack 的打包能力,對(duì)它們進(jìn)行處理。
Plugin:被用于轉(zhuǎn)換某些類(lèi)型的模塊,而插件則可以用于執(zhí)行范圍更廣的任務(wù)。
雪碧圖(CSS Sprite)
CSS雪碧 即CSS Sprite,也有人叫它CSS精靈,是一種CSS圖像合并技術(shù),該方法是將小圖標(biāo)和背景圖像合并到一張圖片上,然后利用css的背景定位來(lái)顯示需要顯示的圖片部分。
雪碧圖實(shí)現(xiàn)的基本原理是把我們從網(wǎng)上用到圖片整合在同一張圖片中,從而可以減少網(wǎng)站HTTP的請(qǐng)求數(shù)量。這一張圖片使用CSS background和background-position屬性渲染,
這意味著我們的標(biāo)簽變得更加復(fù)雜,圖片是在CSS中定義,而非<img>標(biāo)簽。
使用雪碧圖有兩個(gè)明顯的優(yōu)點(diǎn):
降低網(wǎng)頁(yè)圖片內(nèi)容對(duì)服務(wù)器的請(qǐng)求次數(shù)雪碧圖可以合并大多數(shù)的背景圖片和小圖標(biāo),方便我們?cè)谌魏挝恢檬褂?。不同位置的?qǐng)求只會(huì)調(diào)用同一個(gè)圖片,大大減少頁(yè)面對(duì)服務(wù)器的請(qǐng)求次數(shù),降低服務(wù)器的壓力;這樣也可以提高頁(yè)面的加載速度,節(jié)約服務(wù)器的流量。
提升頁(yè)面加載速度雪碧圖拼接的圖片尺寸明顯小于所有圖片拼合之前的打小。
從這兩方面可以明顯對(duì)前端請(qǐng)求速度進(jìn)行優(yōu)化。
在HTTP2之后,已經(jīng)不需要考慮減少請(qǐng)求數(shù),故雪碧圖現(xiàn)在在前端頁(yè)面優(yōu)化性能的意義已經(jīng)不大?,F(xiàn)在更加推薦使用字體圖標(biāo),文件很小并且是矢量圖標(biāo)
CDN加速
CDN的全稱(chēng)是Content Delivery Network,即內(nèi)容分發(fā)網(wǎng)絡(luò)。其目的是通過(guò)在現(xiàn)有的Internet中增加一層新的CACHE(緩存)層,將網(wǎng)站的內(nèi)容發(fā)布到最接近用戶(hù)的網(wǎng)絡(luò)”邊緣“的節(jié)點(diǎn),使用戶(hù)可以就近取得所需的內(nèi)容,提高用戶(hù)訪(fǎng)問(wèn)網(wǎng)站的響應(yīng)速度。從技術(shù)上全面解決由于網(wǎng)絡(luò)帶寬小、用戶(hù)訪(fǎng)問(wèn)量大、網(wǎng)點(diǎn)分布不均等原因,提高用戶(hù)訪(fǎng)問(wèn)網(wǎng)站的響應(yīng)速度。
Cache層技術(shù)可以用來(lái)消除峰值數(shù)據(jù)訪(fǎng)問(wèn)造成的節(jié)點(diǎn)設(shè)備阻塞。Cache服務(wù)器具有緩存功能,絕大部分的網(wǎng)頁(yè)對(duì)象的重復(fù)訪(fǎng)問(wèn)不需要從原始網(wǎng)站重新傳送文件,只需要通過(guò)簡(jiǎn)單認(rèn)證將副本發(fā)送即可。緩存服務(wù)器的位置通常不輸在用戶(hù)端附近,所以可以獲得局域網(wǎng)的響應(yīng)速度,有效減少?gòu)V域?qū)拵摹?/p>
對(duì)于提升響應(yīng)速、節(jié)約帶寬、有效減輕源服務(wù)器的負(fù)載十分有效。
總結(jié)來(lái)說(shuō)CDN對(duì)網(wǎng)絡(luò)的優(yōu)化作用主要體現(xiàn)在如下幾個(gè)方面:
解決服務(wù)器端的“第一公里”問(wèn)題 緩解甚至消除了不同運(yùn)營(yíng)商之間互聯(lián)的瓶頸造成的影響 減輕了各省的出口帶寬壓力 緩解了骨干網(wǎng)的壓力 優(yōu)化了網(wǎng)上熱點(diǎn)內(nèi)容的分布gzip壓縮
Gzip是GNUzip的縮寫(xiě),是一個(gè)GNU自由軟件的文件壓縮程序,在使用中基本可以壓縮50%的文本文件大小。在說(shuō)Gzip之前,我們先介紹一個(gè)概念,HTTP 壓縮。HTTP 壓縮是一種內(nèi)置到網(wǎng)頁(yè)和網(wǎng)頁(yè)客戶(hù)端中以改進(jìn)傳輸速度和帶寬利用率的方式。在使用 HTTP 壓縮的情況下,HTTP 數(shù)據(jù)在從服務(wù)器發(fā)送前就已壓縮:兼容的瀏覽器將在下載所需的格式前宣告支持何種方法給服務(wù)器;不支持壓縮方法的瀏覽器將下載未經(jīng)壓縮的數(shù)據(jù)。
HTTP 壓縮就是以縮小體積為目的,對(duì) HTTP 內(nèi)容進(jìn)行重新編碼的過(guò)程。
Gzip就是HTTP壓縮的經(jīng)典例題。
減少文件大小會(huì)帶來(lái)兩個(gè)明顯的好處:
減少存儲(chǔ)空間通過(guò)網(wǎng)絡(luò)傳輸時(shí)可以減少傳輸時(shí)間Gzip 壓縮背后的原理,是在一個(gè)文本文件中找出一些重復(fù)出現(xiàn)的字符串、臨時(shí)替換它們,從而使整個(gè)文件變小。也正是因?yàn)檫@個(gè)原理,文件中代碼的重復(fù)率越高,Gzip壓縮的效率就越高,使用 Gzip 的收益也就越大。反之亦然。
代碼級(jí)別:減少數(shù)據(jù)請(qǐng)求次數(shù)前面我們列舉了在頁(yè)面初始加載時(shí)的優(yōu)化方法,然而在某些場(chǎng)景下這還不夠,因?yàn)榻?jīng)常會(huì)出現(xiàn)頁(yè)面展示和使用時(shí),頻繁請(qǐng)求服務(wù)來(lái)更新信息的場(chǎng)景。
例如在開(kāi)發(fā)類(lèi)Excel在線(xiàn)協(xié)同系統(tǒng)時(shí),因?yàn)閱卧駱I(yè)務(wù)相互獨(dú)立,全屏刷新無(wú)法滿(mǎn)足需求。我們只能定時(shí)從服務(wù)器獲取每個(gè)單元格的值,檢測(cè)到變化后展示在頁(yè)面上。而每個(gè)單元格分別調(diào)用api獲取內(nèi)容,就會(huì)產(chǎn)生大量網(wǎng)絡(luò)請(qǐng)求。大量的請(qǐng)求一方面拖累了加載速度,頁(yè)面也會(huì)發(fā)生卡頓。
在這種場(chǎng)景下,WebSocket是一個(gè)很好的選擇,通過(guò)長(zhǎng)鏈接的方式保持與服務(wù)器的同步,服務(wù)端主動(dòng)推送更新到客戶(hù)端,減少了網(wǎng)絡(luò)的開(kāi)銷(xiāo)。但是WebSocket也有自身的缺點(diǎn),開(kāi)發(fā)成本高,無(wú)論是客戶(hù)端還是服務(wù)端都需要考慮斷開(kāi)重連、頻繁推送、資源占用等問(wèn)題。所以,我們還需要通過(guò)優(yōu)化,盡量減少請(qǐng)求頻率。
優(yōu)化思路如何減少數(shù)據(jù)請(qǐng)求數(shù)量?我們可以通過(guò)請(qǐng)求隊(duì)列的方式,對(duì)邏輯進(jìn)行優(yōu)化。
(通過(guò)請(qǐng)求隊(duì)列優(yōu)化Web請(qǐng)求)
經(jīng)過(guò)優(yōu)化,類(lèi)Excel在線(xiàn)協(xié)同系統(tǒng)獲取數(shù)據(jù)的邏輯變成了如下的樣子:
當(dāng)單元格發(fā)送請(qǐng)求時(shí),請(qǐng)求先添加ID,并通過(guò)ID緩存callback方法,然后進(jìn)入請(qǐng)求隊(duì)列,隊(duì)列管理器定時(shí)或者根據(jù)隊(duì)列中請(qǐng)求數(shù)量多少像服務(wù)端發(fā)送請(qǐng)求包。服務(wù)端接收到請(qǐng)求包后批量處理,處理后封裝新的返回包前端接受到返回包后根據(jù)請(qǐng)求的唯一ID,調(diào)用對(duì)應(yīng)的callback方法執(zhí)行,完成單元格的請(qǐng)求使用此方法進(jìn)行優(yōu)化,優(yōu)點(diǎn)是顯而易見(jiàn)的:
實(shí)現(xiàn)簡(jiǎn)單,代碼改動(dòng)小,原本的ajax請(qǐng)求改為隊(duì)列調(diào)用即可,請(qǐng)求后的callbak無(wú)需修改。服務(wù)端添加一個(gè)新接口拆分請(qǐng)求即可。根據(jù)實(shí)際場(chǎng)景設(shè)置請(qǐng)求頻率或者一次請(qǐng)求中數(shù)據(jù)的數(shù)量,兼顧更新頻率和相應(yīng)次數(shù)。應(yīng)用實(shí)例下面代碼是GETNUMBERFROMSERVER的實(shí)現(xiàn),該函數(shù)負(fù)責(zé)調(diào)用服務(wù)器的getData接口,傳遞參數(shù),獲取顯示內(nèi)容并展示在單元格。為了確保異步更新單元格的用戶(hù)體驗(yàn),這個(gè)函數(shù)源自SpreadJS的異步函數(shù)。
var GetNumberFromServer = function () { }; GetNumberFromServer.prototype = new GC.Spread.CalcEngine.Functions.AsyncFunction("GETNUMBERFROMSERVER", 1, 2); GetNumberFromServer.prototype.evaluate = function (context, arg1, arg2) { fetch("/spread/getData?data="+arg1) .then(function(response) { return response.text(); }) .then(function(text) { context.setAsyncResult(text); }); }; GC.Spread.CalcEngine.Functions.defineGlobalCustomFunction("GETNUMBERFROMSERVER", new GetNumberFromServer());為了減少請(qǐng)求,我們首先需要使用一個(gè)緩存對(duì)象存放請(qǐng)求數(shù)據(jù),定時(shí)調(diào)用接口處理。
let callStack = {}; //收集請(qǐng)求數(shù)據(jù)let callingStack = {}; //緩存正在請(qǐng)求中的數(shù)據(jù)信息let callStackCount = 0; //請(qǐng)求數(shù)量,當(dāng)作請(qǐng)求ID,用于區(qū)分請(qǐng)求內(nèi)容let timingId = 0; //用于判斷當(dāng)前是否有定時(shí)器等待請(qǐng)求中然后,我們定義新的隊(duì)列化請(qǐng)求方法,代替在函數(shù)中直接調(diào)用API接口。
// data 請(qǐng)求數(shù)據(jù)// context 異步函數(shù)context, 網(wǎng)絡(luò)請(qǐng)求結(jié)束后回調(diào)時(shí)使用// callback 回調(diào)函數(shù)function stackCall(data, context, callback){ let id = callStackCount++; callStack[id] = {}; callStack[id].data = data; callStack[id].context = context; callStack[id].callback = callback; if(timingId === 0){ // 同時(shí)只有一個(gè)定時(shí)器 timingId = setTimeout(function(){ callingStack = callStack; callStack = {}; let newData = "" //合并請(qǐng)求數(shù)據(jù),根據(jù)實(shí)際業(yè)務(wù)情況整理 for(let cId in callingStack){ newData += (cId + "," + callingStack[cId].data + ";"); } // 發(fā)送請(qǐng)求,這里模擬數(shù)據(jù),發(fā)送什么返回什么 fetch("/spread/getData?data=" + newData) .then(function(response) { return response.text(); }) .then(function(text) { let resData = newData.split(";"); let spread = designer.getWorkbook(); spread.suspendPaint(); //暫定頁(yè)面繪制 //解析返回的數(shù)據(jù) for(let resId in resData){ if(resData[resId]){ let ress = resData[resId].split(","); // 根據(jù)Id,獲取函數(shù)的context,調(diào)用callback回調(diào) callingStack[ress[0]].callback.call(null, callingStack[ress[0]].context, ress[1]) } } spread.resumePaint(); //重啟統(tǒng)一繪制 timingId = 0; }); }, 1000) }}最后更新異步函數(shù)的實(shí)現(xiàn)方式,在函數(shù)中調(diào)用stackCall堆棧函數(shù),批量調(diào)用成功后執(zhí)行callback回調(diào)中的setAsyncResult方法,最終實(shí)現(xiàn)業(yè)務(wù)邏輯。
GetNumberFromServer.prototype.evaluate = function (context, arg1, arg2) { stackCall(arg1, context, function(context, text){ context.setAsyncResult(text); }) };經(jīng)過(guò)這次優(yōu)化,當(dāng)頁(yè)面有大量異步請(qǐng)求時(shí),這些請(qǐng)求會(huì)放到隊(duì)列中,定時(shí)統(tǒng)一處理,一次刷新。
此外,我們還可以使用SpreadJS的doNotRecalculateAfterLoad導(dǎo)入選項(xiàng),在首次加載時(shí)不計(jì)算,改用json中原始值;以及calcOnDemand開(kāi)啟按需計(jì)算。進(jìn)一步優(yōu)化頁(yè)面初始化的速度和體驗(yàn)。
json.calcOnDemand = true;spread.fromJSON(json, { doNotRecalculateAfterLoad: true }); 總結(jié)本文分類(lèi)介紹了幾種前端性能優(yōu)化的方法。這些最佳實(shí)踐覆蓋了頁(yè)面加載和數(shù)據(jù)請(qǐng)求環(huán)節(jié)。在文章的后半部分,我們通過(guò)類(lèi)Excel在線(xiàn)協(xié)同編輯的實(shí)例,詳細(xì)介紹了“數(shù)據(jù)請(qǐng)求隊(duì)列化”的實(shí)現(xiàn),希望對(duì)您的前端開(kāi)發(fā)有幫助。
掃描二維碼推送至手機(jī)訪(fǎng)問(wèn)。
版權(quán)聲明:本文由信途科技轉(zhuǎn)載于網(wǎng)絡(luò),如有侵權(quán)聯(lián)系站長(zhǎng)刪除。
轉(zhuǎn)載請(qǐng)注明出處http://macbookprostickers.com/xintu/13882.html