整合營銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          萬字長文:分享前端性能優(yōu)化知識(shí)體系

          可以使用 的插件選項(xiàng)。

          optimization:?{
          ??????runtimeChunk:?{
          ????????name:?'manifest'?//?將 webpack 的 runtime 代碼拆分為一個(gè)單獨(dú)的 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圖片或者字體圖標(biāo)

          因?yàn)樽煮w圖標(biāo)或者SVG是矢量圖,代碼編寫出來的,放大不會(huì)失真,而且渲染速度快。字體圖標(biāo)使用時(shí)就跟字體一樣,可以設(shè)置屬性,例如 font-size、color 等等,非常方便,還有一個(gè)優(yōu)點(diǎn)是生成的文件特別小。

          6.按需加載代碼,減少冗余代碼

          按需加載

          在開發(fā)SPA項(xiàng)目時(shí),項(xiàng)目中經(jīng)常存在十幾個(gè)甚至更多的路由頁面, 如果將這些頁面都打包進(jìn)一個(gè)JS文件, 雖然減少了HTTP請(qǐng)求數(shù)量, 但是會(huì)導(dǎo)致文件比較大,同時(shí)加載了大量首頁不需要的代碼,有些得不償失,這時(shí)候就可以使用按需加載, 將每個(gè)路由頁面單獨(dú)打包為一個(gè)文件,當(dāng)然不僅僅是路由可以按需加載。

          根據(jù)文件內(nèi)容生成文件名,結(jié)合 import 動(dòng)態(tài)引入組件實(shí)現(xiàn)按需加載:

          通過配置 output 的 屬性可以實(shí)現(xiàn)這個(gè)需求。 屬性的值選項(xiàng)中有一個(gè)[],它將根據(jù)文件內(nèi)容創(chuàng)建出唯一 hash。當(dāng)文件內(nèi)容發(fā)生變化時(shí),[]也會(huì)發(fā)生變化。

          output:?{
          ????filename:?'[name].[contenthash].js',
          ????chunkFilename:?'[name].[contenthash].js',
          ????path:?path.resolve(__dirname,?'../dist'),
          },

          減少冗余代碼

          一方面避免不必要的轉(zhuǎn)義:babel-loader用include或exclude來幫我們避免不必要的轉(zhuǎn)譯,不轉(zhuǎn)譯中的js文件,其次在緩存當(dāng)前轉(zhuǎn)譯的js文件,設(shè)置loader: 'babel-loader?=true'

          其次減少ES6 轉(zhuǎn)為 ES5 的冗余代碼:Babel 轉(zhuǎn)化后的代碼想要實(shí)現(xiàn)和原來代碼一樣的功能需要借助一些幫助函數(shù),比如:

          class?Person?{}

          會(huì)被轉(zhuǎn)換為:

          "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);
          };

          這里就是一個(gè)helper函數(shù),如果在很多文件里都聲明了類,那么就會(huì)產(chǎn)生很多個(gè)這樣的helper函數(shù)。

          這里的@babel/runtime包就聲明了所有需要用到的幫助函數(shù),而@babel/plugin--runtime的作用就是將所有需要helper函數(shù)的文件,從@babel/runtime包 引進(jìn)來:

          "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.服務(wù)器端渲染

          客戶端渲染: 獲取 HTML 文件,根據(jù)需要下載 文件,運(yùn)行文件,生成 DOM,再渲染。

          服務(wù)端渲染:服務(wù)端返回 HTML 文件,客戶端只需解析 HTML。

          優(yōu)點(diǎn):首屏渲染快,SEO 好。缺點(diǎn):配置麻煩,增加了服務(wù)器的計(jì)算壓力。

          8. 使用 Defer 加載JS

          盡量將 CSS 放在文件頭部, 文件放在底部

          所有放在 head 標(biāo)簽里的 CSS 和 JS 文件都會(huì)堵塞渲染。如果這些 CSS 和 JS 需要加載和解析很久的話,那么頁面就空白了。所以 JS 文件要放在底部,等 HTML 解析完了再加載 JS 文件。

          那為什么 CSS 文件還要放在頭部呢?

          因?yàn)橄燃虞d HTML 再加載 CSS,會(huì)讓用戶第一時(shí)間看到的頁面是沒有樣式的、“丑陋”的,為了避免這種情況發(fā)生,就要將 CSS 文件放在頭部了。

          另外,JS 文件也不是不可以放在頭部,只要給 script 標(biāo)簽加上 defer 屬性就可以了,異步下載,延遲執(zhí)行。

          9. 靜態(tài)資源使用 CDN

          用戶與服務(wù)器的物理距離對(duì)響應(yīng)時(shí)間也有影響。把內(nèi)容部署在多個(gè)地理位置分散的服務(wù)器上能讓用戶更快地載入頁面, CDN就是為了解決這一問題,在多個(gè)位置部署服務(wù)器,讓用戶離服務(wù)器更近,從而縮短請(qǐng)求時(shí)間。

          性能優(yōu)化過程中遵守的原則_js性能優(yōu)化有哪些方法_優(yōu)化js性能的方法

          10. 圖片優(yōu)化雪碧圖

          圖片可以合并么?當(dāng)然。最為常用的圖片合并場景就是雪碧圖(Sprite)。

          在網(wǎng)站上通常會(huì)有很多小的圖標(biāo),不經(jīng)優(yōu)化的話,最直接的方式就是將這些小圖標(biāo)保存為一個(gè)個(gè)獨(dú)立的圖片文件,然后通過 CSS 將對(duì)應(yīng)元素的背景圖片設(shè)置為對(duì)應(yīng)的圖標(biāo)圖片。這么做的一個(gè)重要問題在于,頁面加載時(shí)可能會(huì)同時(shí)請(qǐng)求非常多的小圖標(biāo)圖片,這就會(huì)受到瀏覽器并發(fā) HTTP 請(qǐng)求數(shù)的限制。

          雪碧圖的核心原理在于設(shè)置不同的背景偏移量,大致包含兩點(diǎn):

          圖片懶加載

          一般來說,我們訪問網(wǎng)站頁面時(shí),其實(shí)很多圖片并不在首屏中,如果我們都加載的話,相當(dāng)于是加載了用戶不一定會(huì)看到圖片, 這顯然是一種浪費(fèi)。解決的核心思路就是懶加載:實(shí)現(xiàn)方式就是先不給圖片設(shè)置路徑,當(dāng)圖片出現(xiàn)在瀏覽器可視區(qū)域時(shí)才設(shè)置真正的圖片路徑。

          優(yōu)化js性能的方法_js性能優(yōu)化有哪些方法_性能優(yōu)化過程中遵守的原則

          實(shí)現(xiàn)上就是先將圖片路徑設(shè)置給-src,當(dāng)頁面不可見時(shí),圖片不會(huì)加載:

          <img?original-src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9eb06680a16044feb794f40fc3b1ac3d~tplv-k3u1fbpfcp-watermark.image"?/>

          通過監(jiān)聽頁面滾動(dòng),等頁面可見時(shí)設(shè)置圖片src:

          const?img?=?document.querySelector('img')
          img.src?=?img.getAttribute("original-src")

          如果想使用懶加載,還可以借助一些已有的工具庫,例如 aFarkas/、verlok/、tuupola/ 等。

          css中圖片懶加載

          除了對(duì)于

          元素的圖片進(jìn)行來加載,在 CSS 中使用的圖片一樣可以懶加載,最常見的場景就是-url。

          .login?{
          ????background-url:?url(/static/img/login.png);
          }

          對(duì)于上面這個(gè)樣式規(guī)則,如果不應(yīng)用到具體的元素,瀏覽器不會(huì)去下載該圖片。所以你可以通過切換的方式,放心得進(jìn)行 CSS 中圖片的懶加載。

          運(yùn)行時(shí)性能優(yōu)化1. 減少重繪與重排

          有前端經(jīng)驗(yàn)的開發(fā)者對(duì)這個(gè)概念一定不會(huì)陌生,瀏覽器下載完頁面需要的所有資源后, 就開始渲染頁面,主要經(jīng)歷這5個(gè)過程:

          解析HTML生成DOM樹

          解析CSS生成CSSOM規(guī)則樹

          將DOM樹與CSSOM規(guī)則樹合并生成Render(渲染)樹

          遍歷Render(渲染)樹開始布局, 計(jì)算每一個(gè)節(jié)點(diǎn)的位置大小信息

          將渲染樹每個(gè)節(jié)點(diǎn)繪制到屏幕上

          性能優(yōu)化過程中遵守的原則_js性能優(yōu)化有哪些方法_優(yōu)化js性能的方法

          瀏覽器渲染過程

          重排

          當(dāng)改變DOM元素位置或者大小時(shí), 會(huì)導(dǎo)致瀏覽器重新生成Render樹, 這個(gè)過程叫重排

          重繪

          當(dāng)重新生成渲染樹后, 將要將渲染樹每個(gè)節(jié)點(diǎn)繪制到屏幕, 這個(gè)過程叫重繪。

          重排觸發(fā)時(shí)機(jī)

          重排發(fā)生后的根本原理就是元素的幾何屬性發(fā)生改變, 所以從能夠改變幾何屬性的角度入手:

          二者關(guān)系:重排會(huì)導(dǎo)致重繪, 但是重繪不會(huì)導(dǎo)致重排

          了解了重排和重繪這兩個(gè)概念,我們還要知道重排和重繪的開銷都是非常昂貴的,如果不停的改變頁面的布局,就會(huì)造成瀏覽器消耗大量的開銷在進(jìn)行頁面的計(jì)算上,這樣容易造成頁面卡頓。那么回到我們的問題如何減少重繪與重排呢?

          1.1 避免table布局1.2 分離讀寫操作

          DOM 的多個(gè)讀操作(或多個(gè)寫操作),應(yīng)該放在一起。不要兩個(gè)讀操作之間,加入一個(gè)寫操作。

          //?bad?強(qiáng)制刷新?觸發(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?緩存布局信息?相當(dāng)于讀寫分離?觸發(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)在大部分瀏覽器有渲染隊(duì)列優(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

          使用絕對(duì)定位會(huì)使的該元素單獨(dú)成為渲染樹中 body 的一個(gè)子元素,重排開銷比較小,不會(huì)對(duì)其它節(jié)點(diǎn)造成太多影響。當(dāng)你在這些節(jié)點(diǎn)上放置這個(gè)元素時(shí),一些其它在這個(gè)區(qū)域內(nèi)的節(jié)點(diǎn)可能需要重繪,但是不需要重排。

          2. 避免頁面卡頓

          我們目前大多數(shù)屏幕的刷新率-60次/s,瀏覽器渲染更新頁面的標(biāo)準(zhǔn)幀率也為60次/s --60FPS(frames/pre second), 那么每一幀的預(yù)算時(shí)間約為16.6ms ≈ 1s/60,瀏覽器在這個(gè)時(shí)間內(nèi)要完成所有的整理工作,如果無法符合此預(yù)算, 幀率將下降,內(nèi)容會(huì)在屏幕抖動(dòng), 此現(xiàn)象通常稱為卡頓。

          瀏覽器需要做的工作包含下面這個(gè)流程:

          性能優(yōu)化過程中遵守的原則_優(yōu)化js性能的方法_js性能優(yōu)化有哪些方法

          首先你用js做了些邏輯,還觸發(fā)了樣式變化,style把應(yīng)用的樣式規(guī)則計(jì)算好之后,把影響到的頁面元素進(jìn)行重新布局,叫做layout,再把它畫到內(nèi)存的一個(gè)畫布里面,paint成了像素,最后把這個(gè)畫布刷到屏幕上去,叫做,形成一幀。

          js性能優(yōu)化有哪些方法_性能優(yōu)化過程中遵守的原則_優(yōu)化js性能的方法

          這幾項(xiàng)的任何一項(xiàng)如果執(zhí)行時(shí)間太長了,就會(huì)導(dǎo)致渲染這一幀的時(shí)間太長,平均幀率就會(huì)掉。假設(shè)這一幀花了50ms,那么此時(shí)的幀率就為1s / 50ms = 20fps.

          當(dāng)然上面的過程并不一定每一步都會(huì)執(zhí)行,例如:

          3. 長列表優(yōu)化

          有時(shí)會(huì)有這樣的需求, 需要在頁面上展示包含上百個(gè)元素的列表(例如一個(gè)Feed流)。每個(gè)列表元素還有著復(fù)雜的內(nèi)部結(jié)構(gòu),這顯然提高了頁面渲染的成本。當(dāng)你使用了React時(shí),長列表的問題就會(huì)被進(jìn)一步的放大。那么怎么來優(yōu)化長列表呢?

          1.1 實(shí)現(xiàn)虛擬列表

          虛擬列表是一種用來優(yōu)化長列表的技術(shù)。它可以保證在列表元素不斷增加,或者列表元素很多的情況下,依然擁有很好的滾動(dòng)、瀏覽性能。它的核心思想在于:只渲染可見區(qū)域附近的列表元素。下圖左邊就是虛擬列表的效果,可以看到只有視口內(nèi)和臨近視口的上下區(qū)域內(nèi)的元素會(huì)被渲染。

          js性能優(yōu)化有哪些方法_性能優(yōu)化過程中遵守的原則_優(yōu)化js性能的方法

          Virtual List.png

          具體實(shí)現(xiàn)步驟如下所示:

          除了自己實(shí)現(xiàn)外, 常用的框架也有不錯(cuò)的開源實(shí)現(xiàn), 例如:

          4. 滾動(dòng)事件性能優(yōu)化

          前端最容易碰到的性能問題的場景之一就是監(jiān)聽滾動(dòng)事件并進(jìn)行相應(yīng)的操作。由于滾動(dòng)事件發(fā)生非常頻繁,所以頻繁地執(zhí)行監(jiān)聽回調(diào)就容易造成執(zhí)行與頁面渲染之間互相阻塞的情況。

          對(duì)應(yīng)滾動(dòng)這個(gè)場景,可以采用防抖和節(jié)流來處理。

          當(dāng)一個(gè)事件頻繁觸發(fā),而我們希望間隔一定的時(shí)間再觸發(fā)相應(yīng)的函數(shù)時(shí), 就可以使用節(jié)流()來處理。比如判斷頁面是否滾動(dòng)到底部,然后展示相應(yīng)的內(nèi)容;就可以使用節(jié)流,在滾動(dòng)時(shí)每300ms進(jìn)行一次計(jì)算判斷是否滾動(dòng)到底部的邏輯,而不用無時(shí)無刻地計(jì)算。

          當(dāng)一個(gè)事件頻繁觸發(fā),而我們希望在事件觸發(fā)結(jié)束一段時(shí)間后(此段時(shí)間內(nèi)不再有觸發(fā))才實(shí)際觸發(fā)響應(yīng)函數(shù)時(shí)會(huì)使用防抖()。例如用戶一直點(diǎn)擊按鈕,但你不希望頻繁發(fā)送請(qǐng)求,你就可以設(shè)置當(dāng)點(diǎn)擊后 200ms 內(nèi)用戶不再點(diǎn)擊時(shí)才發(fā)送請(qǐng)求。

          對(duì)節(jié)流和防抖不太了解的可以看這篇文章:

          5. 使用 Web Workers

          前面提到了大量數(shù)據(jù)的渲染環(huán)節(jié)我們可以采用虛擬列表的方式實(shí)現(xiàn),但是大量數(shù)據(jù)的計(jì)算環(huán)節(jié)依然會(huì)產(chǎn)生瀏覽器假死或者卡頓的情況.

          通常情況下我們CPU密集型的任務(wù)都是交給后端計(jì)算的,但是有些時(shí)候我們需要處理一些離線場景或者解放后端壓力,這個(gè)時(shí)候此方法就不奏效了.

          還有一種方法是計(jì)算切片,使用 拆分密集型任務(wù),但是有些計(jì)算無法利用此方法拆解,同時(shí)還可能產(chǎn)生副作用,這個(gè)方法需要視具體場景而動(dòng).

          最后一種方法也是目前比較奏效的方法就是利用Web Worker 進(jìn)行多線程編程.

          Web Worker 是一個(gè)獨(dú)立的線程(獨(dú)立的執(zhí)行環(huán)境),這就意味著它可以完全和 UI 線程(主線程)并行的執(zhí)行 js 代碼,從而不會(huì)阻塞 UI,它和主線程是通過 和 接口進(jìn)行通信的。

          Web Worker 使得網(wǎng)頁中進(jìn)行多線程編程成為可能。當(dāng)主線程在處理界面事件時(shí),worker 可以在后臺(tái)運(yùn)行,幫你處理大量的數(shù)據(jù)計(jì)算,當(dāng)計(jì)算完成,將計(jì)算結(jié)果返回給主線程,由主線程更新 DOM 元素。

          6. 寫代碼時(shí)的優(yōu)化點(diǎn)

          提升性能,有時(shí)候在我們寫代碼時(shí)注意一些細(xì)節(jié)也是有效果的。

          6.1 使用事件委托

          看一下下面這段代碼:


            ??<li>字節(jié)跳動(dòng)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)
            ??}
            })?

          綁定的事件越多, 瀏覽器內(nèi)存占有就越多,從而影響性能,利用事件代理的方式就可節(jié)省一些內(nèi)存。

          6.2 if-else 對(duì)比 switch

          當(dāng)判定條件越來越多時(shí), 越傾向于使用switch,而不是if-else:

          if?(state?==0)?{
          ????console.log("待開通")
          }?else?if?(state?==?1)?{
          ????console.log("學(xué)習(xí)中")
          }?else?if?(state?==?2)?{
          ????console.log("休學(xué)中")
          }?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更好, 假設(shè)state為4,那么if-else語句就要進(jìn)行4次判定,switch只要進(jìn)行一次即可。

          但是有的情況下switch也做不到if-else的事情, 例如有多個(gè)判斷條件的情況下,無法使用switch

          6.3 布局上使用flexbox

          在早期的 CSS 布局方式中我們能對(duì)元素實(shí)行絕對(duì)定位、相對(duì)定位或浮動(dòng)定位。而現(xiàn)在,我們有了新的布局方式 flexbox,它比起早期的布局方式來說有個(gè)優(yōu)勢,那就是性能比較好。

          關(guān)于前端性能優(yōu)化就寫到這里了,相信還有很多在代碼細(xì)節(jié)上注意就能進(jìn)行性能優(yōu)化的點(diǎn),大家可以到公眾號(hào)【程序員成長指北】后臺(tái)留言, 后期也可以繼續(xù)完善文章,謝謝

          參考文章:

          #comment

          推薦閱讀:


          VUE中文社區(qū)?

          編程技巧?·?行業(yè)秘聞?·?技術(shù)動(dòng)向

          性能優(yōu)化過程中遵守的原則_優(yōu)化js性能的方法_js性能優(yōu)化有哪些方法


          主站蜘蛛池模板: 精品人妻少妇一区二区三区不卡 | 国产精品一区在线麻豆| 一区二区三区www| 亚洲国产一区国产亚洲| 亚洲丶国产丶欧美一区二区三区 | 久久精品黄AA片一区二区三区| 人妻AV一区二区三区精品| 国产高清视频一区二区| 人妻无码一区二区三区AV | 精品无码综合一区二区三区| 国产精品亚洲不卡一区二区三区 | 久久精品日韩一区国产二区| 久久成人国产精品一区二区| 精品无码国产AV一区二区三区| 国产一区三区二区中文在线 | 中文字幕一区二区人妻性色 | 无码人妻aⅴ一区二区三区| 岛国无码av不卡一区二区| 北岛玲在线一区二区| 国内精品一区二区三区在线观看| 天堂国产一区二区三区| 一区二区中文字幕在线观看| 精品福利一区3d动漫| 国产成人无码精品一区不卡| 亚洲AV成人一区二区三区在线看 | 亚洲线精品一区二区三区| 成人区人妻精品一区二区不卡视频 | 久久久国产精品一区二区18禁| 国产成人精品一区二三区熟女 | 中文字幕av日韩精品一区二区| 久久亚洲国产精品一区二区| 国产麻豆精品一区二区三区v视界| av无码精品一区二区三区四区| 精品视频一区二区三区| 狠狠做深爱婷婷久久综合一区| 免费看AV毛片一区二区三区| 国产韩国精品一区二区三区| 亚洲熟妇av一区二区三区| 一区二区三区在线免费看| 无码中文字幕一区二区三区| 美女AV一区二区三区|