Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
可以使用 的插件選項。
optimization:?{
??????runtimeChunk:?{
????????name:?'manifest'?//?將 webpack 的 runtime 代碼拆分為一個單獨的 chunk。
????},
????splitChunks:?{
????????cacheGroups:?{
????????????vendor:?{
????????????????name:?'chunk-vendors',
????????????????test:?/[\\/]node_modules[\\/]/,
????????????????priority:?-10,
????????????????chunks:?'initial'
????????????},
????????????common:?{
????????????????name:?'chunk-common',
????????????????minChunks:?2,
????????????????priority:?-20,
????????????????chunks:?'initial',
????????????????reuseExistingChunk:?true
????????????}
????????},
????}
},
5.采用svg圖片或者字體圖標
因為字體圖標或者SVG是矢量圖,代碼編寫出來的,放大不會失真,而且渲染速度快。字體圖標使用時就跟字體一樣,可以設置屬性,例如 font-size、color 等等,非常方便,還有一個優(yōu)點是生成的文件特別小。
6.按需加載代碼,減少冗余代碼
按需加載
在開發(fā)SPA項目時,項目中經常存在十幾個甚至更多的路由頁面, 如果將這些頁面都打包進一個JS文件, 雖然減少了HTTP請求數(shù)量, 但是會導致文件比較大,同時加載了大量首頁不需要的代碼,有些得不償失,這時候就可以使用按需加載, 將每個路由頁面單獨打包為一個文件,當然不僅僅是路由可以按需加載。
根據(jù)文件內容生成文件名,結合 import 動態(tài)引入組件實現(xiàn)按需加載:
通過配置 output 的 屬性可以實現(xiàn)這個需求。 屬性的值選項中有一個[],它將根據(jù)文件內容創(chuàng)建出唯一 hash。當文件內容發(fā)生變化時,[]也會發(fā)生變化。
output:?{
????filename:?'[name].[contenthash].js',
????chunkFilename:?'[name].[contenthash].js',
????path:?path.resolve(__dirname,?'../dist'),
},
減少冗余代碼
一方面避免不必要的轉義:babel-loader用include或exclude來幫我們避免不必要的轉譯,不轉譯中的js文件,其次在緩存當前轉譯的js文件,設置loader: 'babel-loader?=true'
其次減少ES6 轉為 ES5 的冗余代碼:Babel 轉化后的代碼想要實現(xiàn)和原來代碼一樣的功能需要借助一些幫助函數(shù),比如:
class?Person?{}
會被轉換為:
"use?strict";
function?_classCallCheck(instance,?Constructor)?{
??if?(!(instance?instanceof?Constructor))?{
????throw?new?TypeError("Cannot?call?a?class?as?a?function");
??}
}
var?Person?=?function?Person()?{
??_classCallCheck(this,?Person);
};
這里就是一個helper函數(shù),如果在很多文件里都聲明了類,那么就會產生很多個這樣的helper函數(shù)。
這里的@babel/runtime包就聲明了所有需要用到的幫助函數(shù),而@babel/plugin--runtime的作用就是將所有需要helper函數(shù)的文件,從@babel/runtime包 引進來:
"use?strict";
var?_classCallCheck2?=?require("@babel/runtime/helpers/classCallCheck");
var?_classCallCheck3?=?_interopRequireDefault(_classCallCheck2);
function?_interopRequireDefault(obj)?{
??return?obj?&&?obj.__esModule???obj?:?{?default:?obj?};
}
var?Person?=?function?Person()?{
??(0,?_classCallCheck3.default)(this,?Person);
};
這里就沒有再編譯出helper函數(shù)了,而是直接引用了@babel/runtime中的helpers/。
npm i -D @babel/plugin--runtime @babel/runtime使用 在.babelrc文件中
"plugins":?[
????????"@babel/plugin-transform-runtime"
]
7.服務器端渲染
客戶端渲染: 獲取 HTML 文件,根據(jù)需要下載 文件,運行文件,生成 DOM,再渲染。
服務端渲染:服務端返回 HTML 文件,客戶端只需解析 HTML。
優(yōu)點:首屏渲染快,SEO 好。缺點:配置麻煩,增加了服務器的計算壓力。
8. 使用 Defer 加載JS
盡量將 CSS 放在文件頭部, 文件放在底部
所有放在 head 標簽里的 CSS 和 JS 文件都會堵塞渲染。如果這些 CSS 和 JS 需要加載和解析很久的話,那么頁面就空白了。所以 JS 文件要放在底部,等 HTML 解析完了再加載 JS 文件。
那為什么 CSS 文件還要放在頭部呢?
因為先加載 HTML 再加載 CSS,會讓用戶第一時間看到的頁面是沒有樣式的、“丑陋”的,為了避免這種情況發(fā)生,就要將 CSS 文件放在頭部了。
另外,JS 文件也不是不可以放在頭部,只要給 script 標簽加上 defer 屬性就可以了,異步下載,延遲執(zhí)行。
9. 靜態(tài)資源使用 CDN
用戶與服務器的物理距離對響應時間也有影響。把內容部署在多個地理位置分散的服務器上能讓用戶更快地載入頁面, CDN就是為了解決這一問題,在多個位置部署服務器,讓用戶離服務器更近,從而縮短請求時間。
10. 圖片優(yōu)化雪碧圖
圖片可以合并么?當然。最為常用的圖片合并場景就是雪碧圖(Sprite)。
在網站上通常會有很多小的圖標,不經優(yōu)化的話,最直接的方式就是將這些小圖標保存為一個個獨立的圖片文件,然后通過 CSS 將對應元素的背景圖片設置為對應的圖標圖片。這么做的一個重要問題在于,頁面加載時可能會同時請求非常多的小圖標圖片,這就會受到瀏覽器并發(fā) HTTP 請求數(shù)的限制。
雪碧圖的核心原理在于設置不同的背景偏移量,大致包含兩點:
圖片懶加載
一般來說,我們訪問網站頁面時,其實很多圖片并不在首屏中,如果我們都加載的話,相當于是加載了用戶不一定會看到圖片, 這顯然是一種浪費。解決的核心思路就是懶加載:實現(xiàn)方式就是先不給圖片設置路徑,當圖片出現(xiàn)在瀏覽器可視區(qū)域時才設置真正的圖片路徑。
實現(xiàn)上就是先將圖片路徑設置給-src,當頁面不可見時,圖片不會加載:
<img?original-src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9eb06680a16044feb794f40fc3b1ac3d~tplv-k3u1fbpfcp-watermark.image"?/>
通過監(jiān)聽頁面滾動,等頁面可見時設置圖片src:
const?img?=?document.querySelector('img')
img.src?=?img.getAttribute("original-src")
如果想使用懶加載,還可以借助一些已有的工具庫,例如 aFarkas/、verlok/、tuupola/ 等。
css中圖片懶加載
除了對于
元素的圖片進行來加載,在 CSS 中使用的圖片一樣可以懶加載,最常見的場景就是-url。
.login?{
????background-url:?url(/static/img/login.png);
}
對于上面這個樣式規(guī)則,如果不應用到具體的元素,瀏覽器不會去下載該圖片。所以你可以通過切換的方式,放心得進行 CSS 中圖片的懶加載。
運行時性能優(yōu)化1. 減少重繪與重排
有前端經驗的開發(fā)者對這個概念一定不會陌生,瀏覽器下載完頁面需要的所有資源后, 就開始渲染頁面,主要經歷這5個過程:
解析HTML生成DOM樹
解析CSS生成CSSOM規(guī)則樹
將DOM樹與CSSOM規(guī)則樹合并生成Render(渲染)樹
遍歷Render(渲染)樹開始布局, 計算每一個節(jié)點的位置大小信息
將渲染樹每個節(jié)點繪制到屏幕上
瀏覽器渲染過程
重排
當改變DOM元素位置或者大小時, 會導致瀏覽器重新生成Render樹, 這個過程叫重排
重繪
當重新生成渲染樹后, 將要將渲染樹每個節(jié)點繪制到屏幕, 這個過程叫重繪。
重排觸發(fā)時機
重排發(fā)生后的根本原理就是元素的幾何屬性發(fā)生改變, 所以從能夠改變幾何屬性的角度入手:
二者關系:重排會導致重繪, 但是重繪不會導致重排
了解了重排和重繪這兩個概念,我們還要知道重排和重繪的開銷都是非常昂貴的,如果不停的改變頁面的布局,就會造成瀏覽器消耗大量的開銷在進行頁面的計算上,這樣容易造成頁面卡頓。那么回到我們的問題如何減少重繪與重排呢?
1.1 避免table布局1.2 分離讀寫操作
DOM 的多個讀操作(或多個寫操作),應該放在一起。不要兩個讀操作之間,加入一個寫操作。
//?bad?強制刷新?觸發(fā)四次重排+重繪
div.style.left?=?div.offsetLeft?+?1?+?'px';
div.style.top?=?div.offsetTop?+?1?+?'px';
div.style.right?=?div.offsetRight?+?1?+?'px';
div.style.bottom?=?div.offsetBottom?+?1?+?'px';
//?good?緩存布局信息?相當于讀寫分離?觸發(fā)一次重排+重繪
var?curLeft?=?div.offsetLeft;
var?curTop?=?div.offsetTop;
var?curRight?=?div.offsetRight;
var?curBottom?=?div.offsetBottom;
div.style.left?=?curLeft?+?1?+?'px';
div.style.top?=?curTop?+?1?+?'px';
div.style.right?=?curRight?+?1?+?'px';
div.style.bottom?=?curBottom?+?1?+?'px';
1.3 樣式集中改變
不要頻發(fā)的操作樣式,雖然現(xiàn)在大部分瀏覽器有渲染隊列優(yōu)化,但是在一些老版本的瀏覽器仍然存在效率低下的問題:
//?三次重排
div.style.left?=?'10px';
div.style.top?=?'10px';
div.style.width?=?'20px';
//?一次重排
el.style.cssText?=?'left:?10px;top:?10px;?width:?20px';
或者可以采用更改類名而不是修改樣式的方式。
1.4 屬性為或fixed
使用絕對定位會使的該元素單獨成為渲染樹中 body 的一個子元素,重排開銷比較小,不會對其它節(jié)點造成太多影響。當你在這些節(jié)點上放置這個元素時,一些其它在這個區(qū)域內的節(jié)點可能需要重繪,但是不需要重排。
2. 避免頁面卡頓
我們目前大多數(shù)屏幕的刷新率-60次/s,瀏覽器渲染更新頁面的標準幀率也為60次/s --60FPS(frames/pre second), 那么每一幀的預算時間約為16.6ms ≈ 1s/60,瀏覽器在這個時間內要完成所有的整理工作,如果無法符合此預算, 幀率將下降,內容會在屏幕抖動, 此現(xiàn)象通常稱為卡頓。
瀏覽器需要做的工作包含下面這個流程:
首先你用js做了些邏輯,還觸發(fā)了樣式變化,style把應用的樣式規(guī)則計算好之后,把影響到的頁面元素進行重新布局,叫做layout,再把它畫到內存的一個畫布里面,paint成了像素,最后把這個畫布刷到屏幕上去,叫做,形成一幀。
這幾項的任何一項如果執(zhí)行時間太長了,就會導致渲染這一幀的時間太長,平均幀率就會掉。假設這一幀花了50ms,那么此時的幀率就為1s / 50ms = 20fps.
當然上面的過程并不一定每一步都會執(zhí)行,例如:
3. 長列表優(yōu)化
有時會有這樣的需求, 需要在頁面上展示包含上百個元素的列表(例如一個Feed流)。每個列表元素還有著復雜的內部結構,這顯然提高了頁面渲染的成本。當你使用了React時,長列表的問題就會被進一步的放大。那么怎么來優(yōu)化長列表呢?
1.1 實現(xiàn)虛擬列表
虛擬列表是一種用來優(yōu)化長列表的技術。它可以保證在列表元素不斷增加,或者列表元素很多的情況下,依然擁有很好的滾動、瀏覽性能。它的核心思想在于:只渲染可見區(qū)域附近的列表元素。下圖左邊就是虛擬列表的效果,可以看到只有視口內和臨近視口的上下區(qū)域內的元素會被渲染。
Virtual List.png
具體實現(xiàn)步驟如下所示:
除了自己實現(xiàn)外, 常用的框架也有不錯的開源實現(xiàn), 例如:
4. 滾動事件性能優(yōu)化
前端最容易碰到的性能問題的場景之一就是監(jiān)聽滾動事件并進行相應的操作。由于滾動事件發(fā)生非常頻繁,所以頻繁地執(zhí)行監(jiān)聽回調就容易造成執(zhí)行與頁面渲染之間互相阻塞的情況。
對應滾動這個場景,可以采用防抖和節(jié)流來處理。
當一個事件頻繁觸發(fā),而我們希望間隔一定的時間再觸發(fā)相應的函數(shù)時, 就可以使用節(jié)流()來處理。比如判斷頁面是否滾動到底部,然后展示相應的內容;就可以使用節(jié)流,在滾動時每300ms進行一次計算判斷是否滾動到底部的邏輯,而不用無時無刻地計算。
當一個事件頻繁觸發(fā),而我們希望在事件觸發(fā)結束一段時間后(此段時間內不再有觸發(fā))才實際觸發(fā)響應函數(shù)時會使用防抖()。例如用戶一直點擊按鈕,但你不希望頻繁發(fā)送請求,你就可以設置當點擊后 200ms 內用戶不再點擊時才發(fā)送請求。
對節(jié)流和防抖不太了解的可以看這篇文章:
5. 使用 Web Workers
前面提到了大量數(shù)據(jù)的渲染環(huán)節(jié)我們可以采用虛擬列表的方式實現(xiàn),但是大量數(shù)據(jù)的計算環(huán)節(jié)依然會產生瀏覽器假死或者卡頓的情況.
通常情況下我們CPU密集型的任務都是交給后端計算的,但是有些時候我們需要處理一些離線場景或者解放后端壓力,這個時候此方法就不奏效了.
還有一種方法是計算切片,使用 拆分密集型任務,但是有些計算無法利用此方法拆解,同時還可能產生副作用,這個方法需要視具體場景而動.
最后一種方法也是目前比較奏效的方法就是利用Web Worker 進行多線程編程.
Web Worker 是一個獨立的線程(獨立的執(zhí)行環(huán)境),這就意味著它可以完全和 UI 線程(主線程)并行的執(zhí)行 js 代碼,從而不會阻塞 UI,它和主線程是通過 和 接口進行通信的。
Web Worker 使得網頁中進行多線程編程成為可能。當主線程在處理界面事件時,worker 可以在后臺運行,幫你處理大量的數(shù)據(jù)計算,當計算完成,將計算結果返回給主線程,由主線程更新 DOM 元素。
6. 寫代碼時的優(yōu)化點
提升性能,有時候在我們寫代碼時注意一些細節(jié)也是有效果的。
6.1 使用事件委托
看一下下面這段代碼:
??<li>字節(jié)跳動li>
??<li>阿里li>
??<li>騰訊li>
??<li>京東li>
</ul>
//?good
document.querySelector('ul').onclick?=?(event)?=>?{
??const?target?=?event.target
??if?(target.nodeName?===?'LI')?{
????console.log(target.innerHTML)
??}
}
//?bad
document.querySelectorAll('li').forEach((e)?=>?{
??e.onclick?=?function()?{
????console.log(this.innerHTML)
??}
})?
綁定的事件越多, 瀏覽器內存占有就越多,從而影響性能,利用事件代理的方式就可節(jié)省一些內存。
6.2 if-else 對比 switch
當判定條件越來越多時, 越傾向于使用switch,而不是if-else:
if?(state?==0)?{
????console.log("待開通")
}?else?if?(state?==?1)?{
????console.log("學習中")
}?else?if?(state?==?2)?{
????console.log("休學中")
}?else?if?(state?==?3)?{
????console.log("已過期")
}?esle?if?(state?==4){
????console.log("未購買")
}
switch?(state)?{
????case?0:
????????break
????case?1:
????????break
????case?2:
????????break
????case?3:
????????break
????case?4:
????????break
}
向上面這種情況使用switch更好, 假設state為4,那么if-else語句就要進行4次判定,switch只要進行一次即可。
但是有的情況下switch也做不到if-else的事情, 例如有多個判斷條件的情況下,無法使用switch
6.3 布局上使用flexbox
在早期的 CSS 布局方式中我們能對元素實行絕對定位、相對定位或浮動定位。而現(xiàn)在,我們有了新的布局方式 flexbox,它比起早期的布局方式來說有個優(yōu)勢,那就是性能比較好。
關于前端性能優(yōu)化就寫到這里了,相信還有很多在代碼細節(jié)上注意就能進行性能優(yōu)化的點,大家可以到公眾號【程序員成長指北】后臺留言, 后期也可以繼續(xù)完善文章,謝謝
參考文章:
#comment
推薦閱讀:
VUE中文社區(qū)? 編程技巧?·?行業(yè)秘聞?·?技術動向
*請認真填寫需求信息,我們會在24小時內與您取得聯(lián)系。