<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>

          寫給中高級(jí)前端關(guān)于性能優(yōu)化的9大策略和6大指標(biāo) | 網(wǎng)易四年實(shí)踐

          共 28186字,需瀏覽 57分鐘

           ·

          2021-07-14 15:00

          「鏈接和長(zhǎng)圖失效,請(qǐng)大家點(diǎn)擊閱讀原文查看詳情」

          前言

          筆者近半年一直在參與項(xiàng)目重構(gòu),在重構(gòu)過(guò)程中大量應(yīng)用「性能優(yōu)化」「設(shè)計(jì)模式」兩方面的知識(shí)。「性能優(yōu)化」「設(shè)計(jì)模式」兩方面的知識(shí)不管在工作還是面試時(shí)都是高頻應(yīng)用場(chǎng)景,趁著這次參與大規(guī)模項(xiàng)目重構(gòu)的機(jī)會(huì),筆者認(rèn)真梳理出一些常規(guī)且必用的性能優(yōu)化建議,同時(shí)結(jié)合日常開發(fā)經(jīng)驗(yàn)整理出筆者在網(wǎng)易四年來(lái)實(shí)踐到的認(rèn)為有用的所有性能優(yōu)化建議,與大家一起分享分享!(由于篇幅有限,那設(shè)計(jì)模式在后面再專門出一篇文章唄)

          可能有些性能優(yōu)化建議已被大家熟知,不過(guò)也不影響這次分享,當(dāng)然筆者也將一些平時(shí)可能不會(huì)注意的細(xì)節(jié)羅列出來(lái)。

          平時(shí)大家認(rèn)為性能優(yōu)化是一種無(wú)序的應(yīng)用場(chǎng)景,但在筆者看來(lái)它是一種有序的應(yīng)用場(chǎng)景且很多性能優(yōu)化都是互相鋪墊甚至一帶一路。從過(guò)程趨勢(shì)來(lái)看,性能優(yōu)化可分為「網(wǎng)絡(luò)層面」「渲染層面」;從結(jié)果趨勢(shì)來(lái)看,性能優(yōu)化可分為「時(shí)間層面」「體積層面」。簡(jiǎn)單來(lái)說(shuō)就是要在訪問(wèn)網(wǎng)站時(shí)使其快準(zhǔn)狠地立馬呈現(xiàn)在用戶眼前。

          性能優(yōu)化.png

          所有的性能優(yōu)化都圍繞著兩大層面兩小層面實(shí)現(xiàn),核心層面是網(wǎng)絡(luò)層面渲染層面,輔助層面是時(shí)間層面體積層面,而輔助層面則充滿在核心層面里。于是筆者通過(guò)本文整理出關(guān)于前端性能優(yōu)化「九大策略」「六大指標(biāo)」。當(dāng)然這些策略指標(biāo)都是筆者自己定義,方便通過(guò)某種方式為性能優(yōu)化做一些規(guī)范。

          因此在工作或面試時(shí)結(jié)合這些特征就能完美地詮釋性能優(yōu)化所延伸出來(lái)的知識(shí)了。「前方高能,不看也得收藏,走起!??!」

          所有代碼示例為了凸顯主題,只展示核心配置代碼,其他配置并未補(bǔ)上,請(qǐng)自行腦補(bǔ)

          九大策略

          網(wǎng)絡(luò)層面

          「網(wǎng)絡(luò)層面」的性能優(yōu)化,無(wú)疑是如何讓資源體積更小加載更快,因此筆者從以下四方面做出建議。

          • 「構(gòu)建策略」:基于構(gòu)建工具(Webpack/Rollup/Parcel/Esbuild/Vite/Gulp)
          • 「圖像策略」:基于圖像類型(JPG/PNG/SVG/WebP/Base64)
          • 「分發(fā)策略」:基于內(nèi)容分發(fā)網(wǎng)絡(luò)(CDN)
          • 「緩存策略」:基于瀏覽器緩存(強(qiáng)緩存/協(xié)商緩存)

          上述四方面都是一步接著一步完成,充滿在整個(gè)項(xiàng)目流程里。「構(gòu)建策略」「圖像策略」處于開發(fā)階段,「分發(fā)策略」「緩存策略」處于生產(chǎn)階段,因此在每個(gè)階段都可檢查是否按順序接入上述策略。通過(guò)這種方式就能最大限度增加性能優(yōu)化應(yīng)用場(chǎng)景。

          構(gòu)建策略

          該策略主要圍繞webpack做相關(guān)處理,同時(shí)也是接入最普遍的性能優(yōu)化策略。其他構(gòu)建工具的處理也是大同小異,可能只是配置上不一致。說(shuō)到webpack性能優(yōu)化,無(wú)疑是從時(shí)間層面體積層面入手。

          筆者發(fā)現(xiàn)目前webpack v5整體兼容性還不是特別好,某些功能配合第三方工具可能出現(xiàn)問(wèn)題,故暫未升級(jí)到v5,繼續(xù)使用v4作為生產(chǎn)工具,故以下配置均基于v4,但總體與v5的配置出入不大

          筆者對(duì)兩層面分別做出6個(gè)性能優(yōu)化建議總共12個(gè)性能優(yōu)化建議,為了方便記憶均使用四字詞語(yǔ)概括,方便大家消化。?表示減少打包時(shí)間,??表示減少打包體積。

          • 「減少打包時(shí)間」縮減范圍、緩存副本、定向搜索、提前構(gòu)建、并行構(gòu)建、可視結(jié)構(gòu)
          • 「減少打包體積」分割代碼搖樹優(yōu)化、動(dòng)態(tài)墊片按需加載、作用提升壓縮資源

          ?縮減范圍

          「配置include/exclude縮小Loader對(duì)文件的搜索范圍」,好處是避免不必要的轉(zhuǎn)譯node_modules目錄的體積這么大,那得增加多少時(shí)間成本去檢索所有文件???

          include/exclude通常在各大Loader里配置,src目錄通常作為源碼目錄,可做如下處理。當(dāng)然include/exclude可根據(jù)實(shí)際情況修改。

          export default {
              // ...
              module: {
                  rules: [{
                      exclude/node_modules/,
                      include/src/,
                      test/\.js$/,
                      use"babel-loader"
                  }]
              }
          };

          ?緩存副本

          「配置cache緩存Loader對(duì)文件的編譯副本」,好處是再次編譯時(shí)只編譯修改過(guò)的文件。未修改過(guò)的文件干嘛要隨著修改過(guò)的文件重新編譯呢?

          大部分Loader/Plugin都會(huì)提供一個(gè)可使用編譯緩存的選項(xiàng),通常包含cache字眼。以babel-loadereslint-webpack-plugin為例。

          import EslintPlugin from "eslint-webpack-plugin";

          export default {
              // ...
              module: {
                  rules: [{
                      // ...
                      test/\.js$/,
                      use: [{
                          loader"babel-loader",
                          options: { cacheDirectorytrue }
                      }]
                  }]
              },
              plugins: [
                  new EslintPlugin({ cachetrue })
              ]
          };

          ?定向搜索

          「配置resolve提高文件的搜索速度」,好處是定向指定必須文件路徑。若某些第三方庫(kù)以常規(guī)形式引入可能報(bào)錯(cuò)或希望程序自動(dòng)索引特定類型文件都可通過(guò)該方式解決。

          alias映射模塊路徑,extensions表明文件后綴,noParse過(guò)濾無(wú)依賴文件。通常配置aliasextensions就足夠。

          export default {
              // ...
              resolve: {
                  alias: {
                      "#": AbsPath(""), // 根目錄快捷方式
                      "@": AbsPath("src"), // src目錄快捷方式
                      swiper"swiper/js/swiper.min.js"
                  }, // 模塊導(dǎo)入快捷方式
                  extensions: [".js"".ts"".jsx"".tsx"".json"".vue"// import路徑時(shí)文件可省略后綴名
              }
          };

          ?提前構(gòu)建

          「配置DllPlugin將第三方依賴提前打包」,好處是將DLL與業(yè)務(wù)代碼完全分離且每次只構(gòu)建業(yè)務(wù)代碼。這是一個(gè)古老配置,在webpack v2時(shí)已存在,不過(guò)現(xiàn)在webpack v4+已不推薦使用該配置,因?yàn)槠浒姹镜鷰?lái)的性能提升足以忽略DllPlugin所帶來(lái)的效益。

          「DLL」意為動(dòng)態(tài)鏈接庫(kù),指一個(gè)包含可由多個(gè)程序同時(shí)使用的代碼庫(kù)。在前端領(lǐng)域里可認(rèn)為是另類緩存的存在,它把公共代碼打包為DLL文件并存到硬盤里,再次打包時(shí)動(dòng)態(tài)鏈接DLL文件就無(wú)需再次打包那些公共代碼,從而提升構(gòu)建速度,減少打包時(shí)間。

          配置DLL總體來(lái)說(shuō)相比其他配置復(fù)雜,配置流程可大致分為三步。

          首先告知構(gòu)建腳本哪些依賴做成DLL并生成DLL文件DLL映射表文件。

          import { DefinePlugin, DllPlugin } from "webpack";

          export default {
              // ...
              entry: {
                  vendor: ["react""react-dom""react-router-dom"]
              },
              mode"production",
              optimization: {
                  splitChunks: {
                      cacheGroups: {
                          vendor: {
                              chunks"all",
                              name"vendor",
                              test/node_modules/
                          }
                      }
                  }
              },
              output: {
                  filename"[name].dll.js"// 輸出路徑和文件名稱
                  library"[name]"// 全局變量名稱:其他模塊會(huì)從此變量上獲取里面模塊
                  path: AbsPath("dist/static"// 輸出目錄路徑
              },
              plugins: [
                  new DefinePlugin({
                      "process.env.NODE_ENV"JSON.stringify("development"// DLL模式下覆蓋生產(chǎn)環(huán)境成開發(fā)環(huán)境(啟動(dòng)第三方依賴調(diào)試模式)
                  }),
                  new DllPlugin({
                      name"[name]"// 全局變量名稱:減小搜索范圍,與output.library結(jié)合使用
                      path: AbsPath("dist/static/[name]-manifest.json"// 輸出目錄路徑
                  })
              ]
          };

          然后在package.json里配置執(zhí)行腳本且每次構(gòu)建前首先執(zhí)行該腳本打包出DLL文件

          {
              "scripts": {
                  "dll""webpack --config webpack.dll.js"
              }
          }

          最后鏈接DLL文件并告知webpack可命中的DLL文件讓其自行讀取。使用html-webpack-tags-plugin在打包時(shí)自動(dòng)插入DLL文件。

          import { DllReferencePlugin } from "webpack";
          import HtmlTagsPlugin from "html-webpack-tags-plugin";

          export default {
              // ...
              plugins: [
                  // ...
                  new DllReferencePlugin({
                      manifest: AbsPath("dist/static/vendor-manifest.json"// manifest文件路徑
                  }),
                  new HtmlTagsPlugin({
                      appendfalse// 在生成資源后插入
                      publicPath"/"// 使用公共路徑
                      tags: ["static/vendor.dll.js"// 資源路徑
                  })
              ]
          };

          為了那幾秒鐘的時(shí)間成本,筆者建議配置上較好。當(dāng)然也可使用autodll-webpack-plugin代替手動(dòng)配置。

          ?并行構(gòu)建

          「配置Thread將Loader單進(jìn)程轉(zhuǎn)換為多進(jìn)程」,好處是釋放CPU多核并發(fā)的優(yōu)勢(shì)。在使用webpack構(gòu)建項(xiàng)目時(shí)會(huì)有大量文件需解析和處理,構(gòu)建過(guò)程是計(jì)算密集型的操作,隨著文件增多會(huì)使構(gòu)建過(guò)程變得越慢。

          運(yùn)行在Node里的webpack是單線程模型,簡(jiǎn)單來(lái)說(shuō)就是webpack待處理的任務(wù)需一件件處理,不能同一時(shí)刻處理多件任務(wù)。

          文件讀寫計(jì)算操作無(wú)法避免,能不能讓webpack同一時(shí)刻處理多個(gè)任務(wù),發(fā)揮多核CPU電腦的威力以提升構(gòu)建速度呢?thread-loader來(lái)幫你,根據(jù)CPU個(gè)數(shù)開啟線程。

          在此需注意一個(gè)問(wèn)題,若項(xiàng)目文件不算多就不要使用該性能優(yōu)化建議,畢竟開啟多個(gè)線程也會(huì)存在性能開銷。

          import Os from "os";

          export default {
              // ...
              module: {
                  rules: [{
                      // ...
                      test/\.js$/,
                      use: [{
                          loader"thread-loader",
                          options: { workers: Os.cpus().length }
                      }, {
                          loader"babel-loader",
                          options: { cacheDirectorytrue }
                      }]
                  }]
              }
          };

          ?可視結(jié)構(gòu)

          「配置BundleAnalyzer分析打包文件結(jié)構(gòu)」,好處是找出導(dǎo)致體積過(guò)大的原因。從而通過(guò)分析原因得出優(yōu)化方案減少構(gòu)建時(shí)間。BundleAnalyzerwebpack官方插件,可直觀分析打包文件的模塊組成部分、模塊體積占比、模塊包含關(guān)系、模塊依賴關(guān)系、文件是否重復(fù)、壓縮體積對(duì)比等可視化數(shù)據(jù)。

          可使用webpack-bundle-analyzer配置,有了它,我們就能快速找到相關(guān)問(wèn)題。

          import { BundleAnalyzerPlugin } from "webpack-bundle-analyzer";

          export default {
              // ...
              plugins: [
                  // ...
                  BundleAnalyzerPlugin()
              ]
          };

          ??分割代碼

          「分割各個(gè)模塊代碼,提取相同部分代碼」,好處是減少重復(fù)代碼的出現(xiàn)頻率webpack v4使用splitChunks替代CommonsChunksPlugin實(shí)現(xiàn)代碼分割。

          splitChunks配置較多,詳情可參考官網(wǎng),在此筆者貼上常用配置。

          export default {
              // ...
              optimization: {
                  runtimeChunk: { name"manifest" }, // 抽離WebpackRuntime函數(shù)
                  splitChunks: {
                      cacheGroups: {
                          common: {
                              minChunks2,
                              name"common",
                              priority5,
                              reuseExistingChunktrue// 重用已存在代碼塊
                              test: AbsPath("src")
                          },
                          vendor: {
                              chunks"initial"// 代碼分割類型
                              name"vendor"// 代碼塊名稱
                              priority10// 優(yōu)先級(jí)
                              test/node_modules/ // 校驗(yàn)文件正則表達(dá)式
                          }
                      }, // 緩存組
                      chunks"all" // 代碼分割類型:all全部模塊,async異步模塊,initial入口模塊
                  } // 代碼塊分割
              }
          };

          ??搖樹優(yōu)化

          「刪除項(xiàng)目中未被引用代碼」,好處是移除重復(fù)代碼和未使用代碼。搖樹優(yōu)化首次出現(xiàn)于rollup,是rollup的核心概念,后來(lái)在webpack v2里借鑒過(guò)來(lái)使用。

          搖樹優(yōu)化只對(duì)ESM規(guī)范生效,對(duì)其他模塊規(guī)范失效。搖樹優(yōu)化針對(duì)靜態(tài)結(jié)構(gòu)分析,只有import/export才能提供靜態(tài)的導(dǎo)入/導(dǎo)出功能。因此在編寫業(yè)務(wù)代碼時(shí)必須使用ESM規(guī)范才能讓搖樹優(yōu)化移除重復(fù)代碼和未使用代碼。

          webpack里只需將打包環(huán)境設(shè)置成生產(chǎn)環(huán)境就能讓搖樹優(yōu)化生效,同時(shí)業(yè)務(wù)代碼使用ESM規(guī)范編寫,使用import導(dǎo)入模塊,使用export導(dǎo)出模塊。

          export default {
              // ...
              mode"production"
          };

          ??動(dòng)態(tài)墊片

          「通過(guò)墊片服務(wù)根據(jù)UA返回當(dāng)前瀏覽器代碼墊片」,好處是無(wú)需將繁重的代碼墊片打包進(jìn)去。每次構(gòu)建都配置@babel/preset-envcore-js根據(jù)某些需求將Polyfill打包進(jìn)來(lái),這無(wú)疑又為代碼體積增加了貢獻(xiàn)。

          @babel/preset-env提供的useBuiltIns可按需導(dǎo)入Polyfill。

          • 「false」:無(wú)視target.browsers將所有Polyfill加載進(jìn)來(lái)
          • 「entry」:根據(jù)target.browsers將部分Polyfill加載進(jìn)來(lái)(僅引入有瀏覽器不支持的Polyfill,需在入口文件import "core-js/stable")
          • 「usage」:根據(jù)target.browsers和檢測(cè)代碼里ES6的使用情況將部分Polyfill加載進(jìn)來(lái)(無(wú)需在入口文件import "core-js/stable")

          在此推薦大家使用動(dòng)態(tài)墊片動(dòng)態(tài)墊片可根據(jù)瀏覽器UserAgent返回當(dāng)前瀏覽器Polyfill,其思路是根據(jù)瀏覽器的UserAgentbrowserlist查找出當(dāng)前瀏覽器哪些特性缺乏支持從而返回這些特性的Polyfill。對(duì)這方面感興趣的同學(xué)可參考polyfill-library和polyfill-service的源碼。

          在此提供兩個(gè)動(dòng)態(tài)墊片服務(wù),可在不同瀏覽器里點(diǎn)擊以下鏈接看看輸出不同的Polyfill。相信IExplore還是最多Polyfill的,它自豪地說(shuō):我就是我,不一樣的煙火

          • 「官方CDN服務(wù)」:https://polyfill.io/v3/polyfill.min.js
          • 「阿里CDN服務(wù)」:https://polyfill.alicdn.com/polyfill.min.js

          使用html-webpack-tags-plugin在打包時(shí)自動(dòng)插入動(dòng)態(tài)墊片。

          import HtmlTagsPlugin from "html-webpack-tags-plugin";

          export default {
              plugins: [
                  new HtmlTagsPlugin({
                      appendfalse// 在生成資源后插入
                      publicPathfalse// 使用公共路徑
                      tags: ["https://polyfill.alicdn.com/polyfill.min.js"// 資源路徑
                  })
              ]
          };

          ??按需加載

          「將路由頁(yè)面/觸發(fā)性功能單獨(dú)打包為一個(gè)文件,使用時(shí)才加載」,好處是減輕首屏渲染的負(fù)擔(dān)。因?yàn)轫?xiàng)目功能越多其打包體積越大,導(dǎo)致首屏渲染速度越慢。

          首屏渲染時(shí)只需對(duì)應(yīng)JS代碼而無(wú)需其他JS代碼,所以可使用按需加載。webpack v4提供模塊按需切割加載功能,配合import()可做到首屏渲染減包的效果,從而加快首屏渲染速度。只有當(dāng)觸發(fā)某些功能時(shí)才會(huì)加載當(dāng)前功能的JS代碼。

          webpack v4提供魔術(shù)注解命名切割模塊,若無(wú)注解則切割出來(lái)的模塊無(wú)法分辨出屬于哪個(gè)業(yè)務(wù)模塊,所以一般都是一個(gè)業(yè)務(wù)模塊共用一個(gè)切割模塊的注解名稱。

          const Login = () => import/* webpackChunkName: "login" */ "../../views/login");
          const Logon = () => import/* webpackChunkName: "logon" */ "../../views/logon");

          運(yùn)行起來(lái)控制臺(tái)可能會(huì)報(bào)錯(cuò),在package.jsonbabel相關(guān)配置里接入@babel/plugin-syntax-dynamic-import即可。

          {
              // ...
              "babel": {
                  // ...
                  "plugins": [
                      // ...
                      "@babel/plugin-syntax-dynamic-import"
                  ]
              }
          }

          ??作用提升

          「分析模塊間依賴關(guān)系,把打包好的模塊合并到一個(gè)函數(shù)中」,好處是減少函數(shù)聲明和內(nèi)存花銷作用提升首次出現(xiàn)于rollup,是rollup的核心概念,后來(lái)在webpack v3里借鑒過(guò)來(lái)使用。

          在未開啟作用提升前,構(gòu)建后的代碼會(huì)存在大量函數(shù)閉包。由于模塊依賴,通過(guò)webpack打包后會(huì)轉(zhuǎn)換成IIFE,大量函數(shù)閉包包裹代碼會(huì)導(dǎo)致打包體積增大(模塊越多越明顯)。在運(yùn)行代碼時(shí)創(chuàng)建的函數(shù)作用域變多,從而導(dǎo)致更大的內(nèi)存開銷。

          在開啟作用提升后,構(gòu)建后的代碼會(huì)按照引入順序放到一個(gè)函數(shù)作用域里,通過(guò)適當(dāng)重命名某些變量以防止變量名沖突,從而減少函數(shù)聲明和內(nèi)存花銷。

          webpack里只需將打包環(huán)境設(shè)置成生產(chǎn)環(huán)境就能讓作用提升生效,或顯式設(shè)置concatenateModules。

          export default {
              // ...
              mode"production"
          };
          // 顯式設(shè)置
          export default {
              // ...
              optimization: {
                  // ...
                  concatenateModulestrue
              }
          };

          ??壓縮資源

          「壓縮HTML/CSS/JS代碼,壓縮字體/圖像/音頻/視頻」,好處是更有效減少打包體積。極致地優(yōu)化代碼都有可能不及優(yōu)化一個(gè)資源文件的體積更有效。

          針對(duì)HTML代碼,使用html-webpack-plugin開啟壓縮功能。

          import HtmlPlugin from "html-webpack-plugin";

          export default {
              // ...
              plugins: [
                  // ...
                  HtmlPlugin({
                      // ...
                      minify: {
                          collapseWhitespacetrue,
                          removeCommentstrue
                      } // 壓縮HTML
                  })
              ]
          };

          針對(duì)CSS/JS代碼,分別使用以下插件開啟壓縮功能。其中OptimizeCss基于cssnano封裝,UglifyjsTerser都是webpack官方插件,同時(shí)需注意壓縮JS代碼需區(qū)分ES5ES6。

          • optimize-css-assets-webpack-plugin:壓縮CSS代碼
          • uglifyjs-webpack-plugin:壓縮ES5版本的JS代碼
          • terser-webpack-plugin:壓縮ES6版本的JS代碼
          import OptimizeCssAssetsPlugin from "optimize-css-assets-webpack-plugin";
          import TerserPlugin from "terser-webpack-plugin";
          import UglifyjsPlugin from "uglifyjs-webpack-plugin";

          const compressOpts = type => ({
              cachetrue// 緩存文件
              paralleltrue// 并行處理
              [`${type}Options`]: {
                  beautifyfalse,
                  compress: { drop_consoletrue }
              } // 壓縮配置
          });
          const compressCss = new OptimizeCssAssetsPlugin({
              cssProcessorOptions: {
                  autoprefixer: { removefalse }, // 設(shè)置autoprefixer保留過(guò)時(shí)樣式
                  safetrue // 避免cssnano重新計(jì)算z-index
              }
          });
          const compressJs = USE_ES6
              ? new TerserPlugin(compressOpts("terser"))
              : new UglifyjsPlugin(compressOpts("uglify"));

          export default {
              // ...
              optimization: {
                  // ...
                  minimizer: [compressCss, compressJs] // 代碼壓縮
              }
          };

          針對(duì)字體/音頻/視頻文件,還真沒相關(guān)Plugin供我們使用,就只能拜托大家在發(fā)布項(xiàng)目到生產(chǎn)服前使用對(duì)應(yīng)的壓縮工具處理了。針對(duì)圖像文件,大部分Loader/Plugin封裝時(shí)均使用了某些圖像處理工具,而這些工具的某些功能又托管在國(guó)外服務(wù)器里,所以導(dǎo)致經(jīng)常安裝失敗。具體解決方式可回看筆者曾經(jīng)發(fā)布的《聊聊NPM鏡像那些險(xiǎn)象環(huán)生的坑》一文尋求答案。

          鑒于此,筆者花了一點(diǎn)小技巧開發(fā)了一個(gè)Plugin用于配合webpack壓縮圖像,詳情請(qǐng)參考tinyimg-webpack-plugin。

          import TinyimgPlugin from "tinyimg-webpack-plugin";

          export default {
              // ...
              plugins: [
                  // ...
                  TinyimgPlugin()
              ]
          };

          上述構(gòu)建策略都集成到筆者開源的bruce-cli里,它是一個(gè)「React/Vue」應(yīng)用自動(dòng)化構(gòu)建腳手架,其零配置開箱即用的優(yōu)點(diǎn)非常適合入門級(jí)、初中級(jí)、快速開發(fā)項(xiàng)目的前端同學(xué)使用,還可通過(guò)創(chuàng)建brucerc.js文件覆蓋其默認(rèn)配置,只需專注業(yè)務(wù)代碼的編寫無(wú)需關(guān)注構(gòu)建代碼的編寫,讓項(xiàng)目結(jié)構(gòu)更簡(jiǎn)潔。詳情請(qǐng)戳這里,使用時(shí)記得查看文檔,支持一個(gè)Star哈!

          圖像策略

          該策略主要圍繞圖像類型做相關(guān)處理,同時(shí)也是接入成本較低的性能優(yōu)化策略。只需做到以下兩點(diǎn)即可。

          • 「圖像選型」:了解所有圖像類型的特點(diǎn)及其何種應(yīng)用場(chǎng)景最合適
          • 「圖像壓縮」:在部署到生產(chǎn)環(huán)境前使用工具或腳本對(duì)其壓縮處理

          圖像選型一定要知道每種圖像類型的體積/質(zhì)量/兼容/請(qǐng)求/壓縮/透明/場(chǎng)景等參數(shù)相對(duì)值,這樣才能迅速做出判斷在何種場(chǎng)景使用何種類型的圖像。

          類型 體積 質(zhì)量 兼容 請(qǐng)求 壓縮 透明 場(chǎng)景
          JPG 有損 不支持 背景圖、輪播圖、色彩豐富圖
          PNG 無(wú)損 支持 圖標(biāo)、透明圖
          SVG 無(wú)損 支持 圖標(biāo)、矢量圖
          WebP 兼?zhèn)?/td> 支持 看兼容情況
          Base64 看情況 無(wú)損 支持 圖標(biāo)

          圖像壓縮可在上述構(gòu)建策略-壓縮資源里完成,也可自行使用工具完成。由于現(xiàn)在大部分webpack圖像壓縮工具不是安裝失敗就是各種環(huán)境問(wèn)題(你懂的),所以筆者還是推薦在發(fā)布項(xiàng)目到生產(chǎn)服前使用圖像壓縮工具處理,這樣運(yùn)行穩(wěn)定也不會(huì)增加打包時(shí)間。

          好用的圖像壓縮工具無(wú)非就是以下幾個(gè),若有更好用的工具麻煩在評(píng)論里補(bǔ)充喔!

          工具 開源 收費(fèi) API 免費(fèi)體驗(yàn)
          QuickPicture ?? ?? ?? 可壓縮類型較多,壓縮質(zhì)感較好,有體積限制,有數(shù)量限制
          ShrinkMe ?? ?? ?? 可壓縮類型較多,壓縮質(zhì)感一般,無(wú)數(shù)量限制,有體積限制
          Squoosh ?? ?? ?? 可壓縮類型較少,壓縮質(zhì)感一般,無(wú)數(shù)量限制,有體積限制
          TinyJpg ?? ?? ?? 可壓縮類型較少,壓縮質(zhì)感很好,有數(shù)量限制,有體積限制
          TinyPng ?? ?? ?? 可壓縮類型較少,壓縮質(zhì)感很好,有數(shù)量限制,有體積限制
          Zhitu ?? ?? ?? 可壓縮類型一般,壓縮質(zhì)感一般,有數(shù)量限制,有體積限制

          若不想在網(wǎng)站里來(lái)回拖動(dòng)圖像文件,可使用筆者開源的圖像批處理工具img-master代替,不僅有壓縮功能,還有分組功能、標(biāo)記功能和變換功能。目前筆者負(fù)責(zé)的全部項(xiàng)目都使用該工具處理,一直用一直爽!

          圖像策略也許處理一張圖像就能完爆所有構(gòu)建策略,因此是一種很廉價(jià)但極有效的性能優(yōu)化策略。

          分發(fā)策略

          該策略主要圍繞內(nèi)容分發(fā)網(wǎng)絡(luò)做相關(guān)處理,同時(shí)也是接入成本較高的性能優(yōu)化策略,需足夠資金支持。

          雖然接入成本較高,但大部分企業(yè)都會(huì)購(gòu)買一些CDN服務(wù)器,所以在部署的事情上就不用過(guò)分擔(dān)憂,盡管使用就好。該策略盡量遵循以下兩點(diǎn)就能發(fā)揮CDN最大作用。

          • 「所有靜態(tài)資源走CDN」:開發(fā)階段確定哪些文件屬于靜態(tài)資源
          • 「把靜態(tài)資源與主頁(yè)面置于不同域名下」:避免請(qǐng)求帶上Cookie

          「內(nèi)容分發(fā)網(wǎng)絡(luò)」簡(jiǎn)稱「CDN」,指一組分布在各地存儲(chǔ)數(shù)據(jù)副本并可根據(jù)就近原則滿足數(shù)據(jù)請(qǐng)求的服務(wù)器。其核心特征是緩存回源,緩存是把資源復(fù)制到CDN服務(wù)器里,回源是資源過(guò)期/不存在就向上層服務(wù)器請(qǐng)求并復(fù)制到CDN服務(wù)器里。

          使用CDN可降低網(wǎng)絡(luò)擁塞,提高用戶訪問(wèn)響應(yīng)速度和命中率。構(gòu)建在現(xiàn)有網(wǎng)絡(luò)基礎(chǔ)上的智能虛擬網(wǎng)絡(luò),依靠部署在各地服務(wù)器,通過(guò)中心平臺(tái)的調(diào)度、負(fù)載均衡、內(nèi)容分發(fā)等功能模塊,使用戶就近獲取所需資源,這就是CDN的終極使命。

          基于CDN「就近原則」所帶來(lái)的優(yōu)點(diǎn),可將網(wǎng)站所有靜態(tài)資源全部部署到CDN服務(wù)器里。那靜態(tài)資源包括哪些文件?通常來(lái)說(shuō)就是無(wú)需服務(wù)器產(chǎn)生計(jì)算就能得到的資源,例如不常變化的樣式文件、腳本文件多媒體文件(字體/圖像/音頻/視頻)等。

          若需單獨(dú)配置CDN服務(wù)器,可考慮阿里云OSS、網(wǎng)易樹帆NOS和七牛云Kodo,當(dāng)然配置起來(lái)還需購(gòu)買該產(chǎn)品對(duì)應(yīng)的CDN服務(wù)。由于篇幅問(wèn)題,這些配置在購(gòu)買后會(huì)有相關(guān)教程,可自行體會(huì),在此就不再敘述了。

          筆者推薦大家首選網(wǎng)易樹帆NOS,畢竟對(duì)自家產(chǎn)品還是挺有信心的,不小心給自家產(chǎn)品打了個(gè)小廣告了,哈哈!

          緩存策略

          該策略主要圍繞瀏覽器緩存做相關(guān)處理,同時(shí)也使接入成本最低的性能優(yōu)化策略。其顯著減少網(wǎng)絡(luò)傳輸所帶來(lái)的損耗,提升網(wǎng)頁(yè)訪問(wèn)速度,是一種很值得使用的性能優(yōu)化策略。

          通過(guò)下圖可知,為了讓瀏覽器緩存發(fā)揮最大作用,該策略盡量遵循以下五點(diǎn)就能發(fā)揮瀏覽器緩存最大作用。

          • 「考慮拒絕一切緩存策略」Cache-Control:no-store
          • 「考慮資源是否每次向服務(wù)器請(qǐng)求」Cache-Control:no-cache
          • 「考慮資源是否被代理服務(wù)器緩存」Cache-Control:public/private
          • 「考慮資源過(guò)期時(shí)間」Expires:t/Cache-Control:max-age=t,s-maxage=t
          • 「考慮協(xié)商緩存」Last-Modified/Etag
          緩存判斷機(jī)制

          同時(shí)瀏覽器緩存也是高頻面試題之一,筆者覺得上述涉及到的名詞在不同語(yǔ)序串聯(lián)下也能完全理解才能真正弄懂瀏覽器緩存性能優(yōu)化里起到的作用。

          緩存策略通過(guò)設(shè)置HTTP報(bào)文實(shí)現(xiàn),在形式上分為「強(qiáng)緩存/強(qiáng)制緩存」「協(xié)商緩存/對(duì)比緩存」。為了方便對(duì)比,筆者將某些細(xì)節(jié)使用圖例展示,相信你有更好的理解。

          強(qiáng)緩存.png
          協(xié)商緩存.png

          整個(gè)緩存策略機(jī)制很明了,先走強(qiáng)緩存,若命中失敗才走協(xié)商緩存。若命中強(qiáng)緩存,直接使用強(qiáng)緩存;若未命中強(qiáng)緩存,發(fā)送請(qǐng)求到服務(wù)器檢查是否命中協(xié)商緩存;若命中協(xié)商緩存,服務(wù)器返回304通知瀏覽器使用本地緩存,否則返回最新資源。

          有兩種較常用的應(yīng)用場(chǎng)景值得使用緩存策略一試,當(dāng)然更多應(yīng)用場(chǎng)景都可根據(jù)項(xiàng)目需求制定。

          • 「頻繁變動(dòng)資源」:設(shè)置Cache-Control:no-cache,使瀏覽器每次都發(fā)送請(qǐng)求到服務(wù)器,配合Last-Modified/ETag驗(yàn)證資源是否有效
          • 「不常變化資源」:設(shè)置Cache-Control:max-age=31536000,對(duì)文件名哈希處理,當(dāng)代碼修改后生成新的文件名,當(dāng)HTML文件引入文件名發(fā)生改變才會(huì)下載最新文件

          渲染層面

          「渲染層面」的性能優(yōu)化,無(wú)疑是如何讓代碼解析更好執(zhí)行更快。因此筆者從以下五方面做出建議。

          • 「CSS策略」:基于CSS規(guī)則
          • 「DOM策略」:基于DOM操作
          • 「阻塞策略」:基于腳本加載
          • 「回流重繪策略」:基于回流重繪
          • 「異步更新策略」:基于異步更新

          上述五方面都是編寫代碼時(shí)完成,充滿在整個(gè)項(xiàng)目流程的開發(fā)階段里。因此在開發(fā)階段需時(shí)刻注意以下涉及到的每一點(diǎn),養(yǎng)成良好的開發(fā)習(xí)慣,性能優(yōu)化也自然而然被使用上了。

          渲染層面性能優(yōu)化更多表現(xiàn)在編碼細(xì)節(jié)上,而并非實(shí)體代碼。簡(jiǎn)單來(lái)說(shuō)就是遵循某些編碼規(guī)則,才能將渲染層面性能優(yōu)化發(fā)揮到最大作用。

          「回流重繪策略」渲染層面性能優(yōu)化里占比較重,也是最常規(guī)的性能優(yōu)化之一。上年筆者發(fā)布的掘金小冊(cè)《玩轉(zhuǎn)CSS的藝術(shù)之美》使用一整章講解回流重繪,本章已開通試讀,更多細(xì)節(jié)請(qǐng)戳這里。

          CSS策略
          • 避免出現(xiàn)超過(guò)三層的嵌套規(guī)則
          • 避免為ID選擇器添加多余選擇器
          • 避免使用標(biāo)簽選擇器代替類選擇器
          • 避免使用通配選擇器,只對(duì)目標(biāo)節(jié)點(diǎn)聲明規(guī)則
          • 避免重復(fù)匹配重復(fù)定義,關(guān)注可繼承屬性
          DOM策略
          • 緩存DOM計(jì)算屬性
          • 避免過(guò)多DOM操作
          • 使用DOMFragment緩存批量化DOM操作
          阻塞策略
          • 腳本與DOM/其它腳本的依賴關(guān)系很強(qiáng):對(duì)<script>設(shè)置defer
          • 腳本與DOM/其它腳本的依賴關(guān)系不強(qiáng):對(duì)<script>設(shè)置async
          回流重繪策略
          • 緩存DOM計(jì)算屬性
          • 使用類合并樣式,避免逐條改變樣式
          • 使用display控制DOM顯隱,將DOM離線化
          異步更新策略
          • 異步任務(wù)中修改DOM時(shí)把其包裝成微任務(wù)

          六大指標(biāo)

          筆者根據(jù)性能優(yōu)化的重要性和實(shí)際性劃分出九大策略六大指標(biāo),其實(shí)它們都是一條條活生生的性能優(yōu)化建議。有些性能優(yōu)化建議接不接入影響都不大,因此筆者將九大策略定位高于六大指標(biāo)。針對(duì)九大策略還是建議在開發(fā)階段和生產(chǎn)階段接入,在項(xiàng)目復(fù)盤時(shí)可將六大指標(biāo)的條條框框根據(jù)實(shí)際應(yīng)用場(chǎng)景接入。

          六大指標(biāo)基本囊括大部分性能優(yōu)化細(xì)節(jié),可作為九大策略的補(bǔ)充。筆者根據(jù)每條性能優(yōu)化建議的特征將指標(biāo)劃分為以下六方面。

          • 「加載優(yōu)化」:資源在加載時(shí)可做的性能優(yōu)化
          • 「執(zhí)行優(yōu)化」:資源在執(zhí)行時(shí)可做的性能優(yōu)化
          • 「渲染優(yōu)化」:資源在渲染時(shí)可做的性能優(yōu)化
          • 「樣式優(yōu)化」:樣式在編碼時(shí)可做的性能優(yōu)化
          • 「腳本優(yōu)化」:腳本在編碼時(shí)可做的性能優(yōu)化
          • 「V8引擎優(yōu)化」:針對(duì)V8引擎特征可做的性能優(yōu)化
          加載優(yōu)化
          六大指標(biāo)-加載優(yōu)化.png
          執(zhí)行優(yōu)化
          六大指標(biāo)-執(zhí)行優(yōu)化.png
          渲染優(yōu)化
          六大指標(biāo)-渲染優(yōu)化.png
          樣式優(yōu)化
          六大指標(biāo)-樣式優(yōu)化.png
          腳本優(yōu)化
          六大指標(biāo)-腳本優(yōu)化.png
          V8引擎優(yōu)化
          六大指標(biāo)-V8引擎優(yōu)化.png

          總結(jié)

          「性能優(yōu)化」作為老生常談的知識(shí),必然會(huì)在工作或面試時(shí)遇上。很多時(shí)候不是想到某條性能優(yōu)化建議就去做或答,而是要對(duì)這方面有一個(gè)整體認(rèn)知,知道為何這樣設(shè)計(jì),這樣設(shè)計(jì)的目的能達(dá)到什么效果。

          性能優(yōu)化不是通過(guò)一篇文章就能全部講完,若詳細(xì)去講可能要寫兩本書的篇幅才能講完。本文能到給大家的就是一個(gè)方向一種態(tài)度,學(xué)以致用唄,希望閱讀完本文會(huì)對(duì)你有所幫助。

          最后,筆者將本文所有內(nèi)容整理成一張高清腦圖,由于體積太大無(wú)法上傳,可關(guān)注筆者個(gè)人公眾號(hào)「IQ前端」并回復(fù)性能優(yōu)化獲取口袋知識(shí)圖譜吧!

          往期超過(guò)5萬(wàn)閱讀量的掘金爆文

          • 靈活運(yùn)用CSS開發(fā)技巧:4500+點(diǎn)贊量,13.8w閱讀量
          • 靈活運(yùn)用JS開發(fā)技巧:1700+點(diǎn)贊量,5.4w閱讀量
          • 1.5萬(wàn)字概括ES6全部特性:4500+點(diǎn)贊量,16.5w閱讀量
          • 中高級(jí)前端必須注意的40條移動(dòng)端H5坑位指南|網(wǎng)易三年實(shí)踐:3800+點(diǎn)贊量,5.7w閱讀量


          如果你覺得這篇內(nèi)容對(duì)你挺有啟發(fā),我想邀請(qǐng)你幫我三個(gè)小忙:

          1. 點(diǎn)個(gè)「在看」,讓更多的人也能看到這篇內(nèi)容(喜歡不點(diǎn)在看,都是耍流氓 -_-)

          2. 歡迎加我微信「huab119」拉你進(jìn)技術(shù)群,長(zhǎng)期交流學(xué)習(xí)...

            關(guān)注公眾號(hào)「前端勸退師」,持續(xù)為你推送精選好文,也可以加我為好友,隨時(shí)聊騷。



          點(diǎn)個(gè)在看支持我吧,轉(zhuǎn)發(fā)就更好了




          瀏覽 46
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <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>
                  疯狂做爰XXXⅩa久久久久久 | 日韩无码成人三级片 | 国产精品五月天久久久 | 波多野结衣视频网址 | 在线国产激情 |