<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          如何將網(wǎng)頁性能提升5倍的 — 構(gòu)建優(yōu)化篇

          共 3216字,需瀏覽 7分鐘

           ·

          2020-12-30 17:52

          最近對公司的一個 PC 站點做了一次整體的性能優(yōu)化,由于這個系統(tǒng)業(yè)務(wù)復(fù)雜、依賴非常多,加載速度非常慢,優(yōu)化后各個性能指標(biāo)都有了顯著提升,大約加載速度快了 5 倍左右。

          我在 構(gòu)建、網(wǎng)絡(luò)、資源加載、運行時、服務(wù)端、功能組織等多個方面都進行了優(yōu)化,準(zhǔn)備做一個系列,分章節(jié)給大家分享下我的優(yōu)化經(jīng)驗。

          今天,我們從優(yōu)化效果最為明顯的構(gòu)建角度開始。

          優(yōu)化前

          首先我們看一下在優(yōu)化前站點的資源加載情況:

          可見最大的 vendor 包居然有 3MB(經(jīng)過 gzip 壓縮后),沒有做額外配置的話,webpack 將所有的第三方依賴都打入了這個包,如果引入依賴越來越多,那么這個包就會越來越大。

          另外,系統(tǒng)本身的邏輯打的包也達(dá)到了 600kb

          分析依賴關(guān)系

          我們可以借助 webpack-bundle-analyzer 將打包后的內(nèi)容展示為方便交互的樹狀圖,我們可以很直觀的看到有哪些比較大的模塊,然后做針對性優(yōu)化。

          npm?install?--save-dev?webpack-bundle-analyzer

          const?BundleAnalyzerPlugin?=?require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
          module.exports?=?{
          ??plugins:?[
          ????new?BundleAnalyzerPlugin()
          ??]
          }

          CDN 引入

          CDN 的工作原理是將源站的資源緩存到位于全球各地的 CDN 節(jié)點上,用戶請求資源時,就近返回節(jié)點上緩存的資源,而不需要每個用戶的請求都回您的源站獲取,避免網(wǎng)絡(luò)擁塞、緩解源站壓力,保證用戶訪問資源的速度和體驗。

          這個估計大家都明白,因為打包后的產(chǎn)物本身也是上傳到 CDN 的。但是我們要做的是將體積較大的第三方依賴單獨拆出來放到 CDN 上,這樣這個依賴既不會占用打包資源,也不會影響最終包體積。

          如果一個依賴有直接打包壓縮好的單文件 CDN 資源,例如上面圖中的 g6,就可以直接使用。

          按照官方文檔的解釋,如果我們想引用一個庫,但是又不想讓 webpack 打包,并且又不影響我們在程序中以 import、require 或者 window/global 全局等方式進行使用,那就可以通過配置 externals

          externals 配置選項提供了「從輸出的 bundle 中排除依賴」的方法。相反,所創(chuàng)建的 bundle 依賴于那些存在于用戶環(huán)境(consumer's environment)中的依賴。

          首先將 CDN 引入的依賴加入到 externals 中。

          然后借助 html-webpack-pluginCDN 文件打入 html:

          這里有一點需要注意,在 html 中配置的 CDN 引入腳本一定要在 body 內(nèi)的最底部,因為:

          • 如果放在 body 上面或 header 內(nèi),則加載會阻塞整個頁面渲染。
          • 如果放在 body 外,則會在業(yè)務(wù)代碼被加載之后加載,模塊中使用了該模塊將會報錯。

          拆 vendor

          某些場景下, 一個第三方依賴可能拆成了多個子依賴,例如上面的 monaco,或者沒有提供可直接通過 CDN 引入的文件,我們就無法通過配置一個 CDN 文件來引入它了。

          這時我們需要自己去 webpack 設(shè)置一些規(guī)則,將我們想拆出來的依賴單獨打包一個 vendor。

          動態(tài) import

          vendor 拆分后,依賴仍然會在首屏被加載,如果依賴不在首屏使用,仍然會造成網(wǎng)絡(luò)資源的浪費,并阻塞頁面渲染,對于沒必要在首屏進行加載的依賴,我們可以采用動態(tài) import 的方式。

          例如上面這個 js-export-excel 這個依賴,自己本身有將近 500 kb,但是其只會在用戶點擊【導(dǎo)出】按鈕的時候使用,我們首先在 vendor 中將其拆出來。

          使用時,將 import 的邏輯由首屏改到運行時異步加載

          這樣的話,js-export-excel 這個依賴包只會在用戶點擊【導(dǎo)出】按鈕時引入,首屏不再引入。

          不是所有依賴都適合異步加載,如果你對使用該依賴有很高的性能要求,然后依賴本身也比較大,這種情況是不適合的,因為你可能會看到明顯的延遲。以上 export 其實是一個比較合適的場景,下載 excel 本身需要延遲時間,加上動態(tài)加載依賴的時間是可接收的。

          React 懶加載

          類似的,對于某些第三方依賴組件,例如 monaco editor ,我們只有在很少的業(yè)務(wù)場景下才會用到,但是其本身一個包占用了 5MB 。。我們每次在打開頁面時都要加載它,這太耗費性能了。

          對于一個依賴包,我們可以通過動態(tài) import 的方式進行懶加載,但是對于一個 React 組件,直接使用動態(tài) import 可能就不太合適了,組件渲染的運行時都是可多次觸發(fā)了,不可能在每次組件渲染時都加載一次組件。

          React.lazy 函數(shù)能讓你像渲染常規(guī)組件一樣處理動態(tài)引入組件。React.lazy 接受一個函數(shù),這個函數(shù)需要動態(tài)調(diào)用 import()。它必須返回一個 Promise,該 Promise 需要 resolve 一個 default exportReact 組件。

          const?MonacoEditor?=?React.lazy(()?=>?import('react-monaco-editor'));

          此代碼將會在組件首次渲染時,自動導(dǎo)入包含 MonacoEditor 組件的包。但是直接使用React.lazy引入的組件是無法直接使用的,因為 React 無法預(yù)測組件何時被加載,直接渲染會導(dǎo)致頁面崩潰。

          Suspense 組件中渲染 lazy 組件,可以使用在等待加載 lazy 組件時做優(yōu)雅降級(如 loading )。fallback 屬性接受任何在組件加載過程中你想展示的 React 元素。你可以將 Suspense 組件置于懶加載組件之上的任何位置。你甚至可以用一個 Suspense 組件包裹多個懶加載組件。

          將所有 monaco editor 改為懶加載后,首屏已經(jīng)不會加載 monaco editor

          路由懶加載

          上面 React 懶加載的方式,同樣適用于路由,對于每個路由都使用懶加載的方式引入,則每個模塊都會被單獨打為一個 js,首屏只會加載當(dāng)前模塊引入的 js

          不過 路由懶加載 也有一個很明顯的弊端,就是每個模塊的資源是只有加載這個模塊的時候才回去下載的,所以在切換模塊的時候可能會有一小段白屏或 loading 效果,這個要結(jié)合業(yè)務(wù)自身的情況綜合判斷要不要使用。

          語言包優(yōu)化

          在某些場景下,語言包會占用整個包體積的非常大一部分。實際上庫本身的邏輯不會很大,moment 就是一個很好例子。

          如果最開始選擇日期庫,那直接推薦使用 dayjs 了,如果你選擇了 moment ,一定要注意把不使用的語言包過濾掉,推薦使用 ContextReplacementPlugin,它會告訴 webpack 我們會使用到哪個本地文件:

          plugins:?[
          ????new?webpack.ContextReplacementPlugin(/moment[/\\]locale$/,?/zh-cn/),
          ??]

          優(yōu)化效果

          最終優(yōu)化后,會發(fā)現(xiàn)模塊已經(jīng)被我們拆的非常均勻,并且只會在對應(yīng)頁面渲染時加載對應(yīng)模塊,這對首屏渲染速度有顯著提升。

          未完待續(xù),期待一下后續(xù)文章吧 !




          推薦閱讀




          我的公眾號能帶來什么價值?(文末有送書規(guī)則,一定要看)

          每個前端工程師都應(yīng)該了解的圖片知識(長文建議收藏)

          為什么現(xiàn)在面試總是面試造火箭?

          瀏覽 55
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  久久久久久久久久av | 国内无码自拍 | 三级片大香蕉 | 中文字幕在线观看第一页2019 | 国产特级AAAAAA大片 |