webpack4.x 性能優化

文章首發於:github.com/USTB-musion…css

寫在前面

webpack能夠說是當下最流行的打包庫,當webpack處理應用程序時,它會遞歸地構建一個依賴關係圖,其中包含應用程序須要的每個模塊,而後將全部這些模塊打包成一個或多個bundle。這篇文章將介紹webpack很是重要的一部分——性能優化。文章的代碼的在此: github.com/USTB-musion…html

本文將從如下幾部分進行總結:前端

  1. noParse
  2. ignorePlugin
  3. dllPlugin
  4. happypack
  5. Tree-Shaking
  6. 抽離公共代碼
  7. 懶加載
  8. 熱更新

noParse

  • noParse 配置項可讓 Webpack 忽略對部分沒采用模塊化的文件的遞歸解析和處理,這樣作的好處是能提升構建性能。 緣由是一些庫例如 jQuery 、ChartJS 它們龐大又沒有采用模塊化標準,讓 Webpack 去解析這些文件耗時又沒有意義。

啓用noParse:vue

module: {
    // 不去解析jquery的依賴關係
    noParse: /jquery/
  },
複製代碼

ignorePlugin

  • moment 2.18 會將全部本地化內容和核心功能一塊兒打包)。可使用 IgnorePlugin 在打包時忽略本地化內容,通過實驗,使用 ignorePlugin 以後 📦 以後的體積由 1.2M 下降至 800K

ignorePlugin啓用方法:node

// 用法:
new webpack.IgnorePlugin(requestRegExp, [contextRegExp]);

//eg.
plugins: [new webpack.IgnorePlugin(/\.\/local/, /moment/)];
複製代碼

DllPlugin

  • DllPlugin 是基於 Windows 動態連接庫(dll)的思想被創做出來的。這個插件會把第三方庫單獨打包到一個文件中,這個文件就是一個單純的依賴庫。這個依賴庫不會跟着你的業務代碼一塊兒被從新打包,只有當依賴自身發生版本變化時纔會從新打包。

用 DllPlugin 處理文件,要分兩步走:

  • 基於 dll 專屬的配置文件,打包 dll 庫
let path = require("path");
let webpack = require("webpack");

module.exports = {
  mode: "development",
  entry: {
    react: ["react", "react-dom"]
  },
  output: {
    filename: "_dll_[name].js", // 產生的文件名
    path: path.resolve(__dirname, "dist"),
    library: "_dll_[name]"
  },
  plugins: [
    // name要等於library裏的name
    new webpack.DllPlugin({
      name: "_dll_[name]",
      path: path.resolve(__dirname, "dist", "manifest.json")
    })
  ]
};
複製代碼
  • 基於 webpack.config.js 文件,打包業務代碼
let path = require("path");
let HtmlWebpackPlugin = require("html-webpack-plugin");
let webpack = require("webpack");

module.exports = {
  mode: "development",
  // 多入口
  entry: {
    home: "./src/index.js"
  },
  devServer: {
    port: 3000,
    open: true,
    contentBase: "./dist"
  },
  module: {
    // 不去解析jquery的依賴關係
    noParse: /jquery/,
    rules: [
      {
        test: /\.css$/,
        use: ["style-loader", "css-loader"]
      },
      {
        test: /\.js$/,
        exclude: /node_modules/,
        include: path.resolve("src"),
        use: {
          loader: "babel-loader",
          options: {
            presets: ["@babel/preset-env", "@babel/preset-react"]
          }
        }
      }
    ]
  },
  output: {
    // name -> home a
    filename: "[name].js",
    path: path.resolve(__dirname, "dist")
  },
  plugins: [
    new webpack.DllReferencePlugin({
      manifest: path.resolve(__dirname, "dist", "manifest.json")
    }),
    new webpack.IgnorePlugin(/\.\/local/, /moment/),
    new HtmlWebpackPlugin({
      template: "./src/index.html",
      filename: "index.html"
    }),
    new webpack.DefinePlugin({
      DEV: JSON.stringify("production")
    })
  ]
};
複製代碼

Happypack——將 loader 由單進程轉爲多進程

  • 你們知道,webpack 是單線程的,就算此刻存在多個任務,你也只能排隊一個接一個地等待處理。這是 webpack 的缺點,好在咱們的 CPU 是多核的,Happypack 會充分釋放 CPU 在多核併發方面的優點,幫咱們把任務分解給多個子進程去併發執行,大大提高打包效率。

happypack的使用方法:

將loader中的配置轉移到happypack中就好:react

let path = require("path");
let HtmlWebpackPlugin = require("html-webpack-plugin");
let webpack = require("webpack");
// 模塊 happypack 能夠實現多線程📦
let Happypack = require("happypack");

module.exports = {
  mode: "development",
  // 多入口
  entry: {
    home: "./src/index.js"
  },
  devServer: {
    port: 3000,
    open: true,
    contentBase: "./dist"
  },
  module: {
    // 不去解析jquery的依賴關係
    noParse: /jquery/,
    rules: [
      {
        test: /\.css$/,
        use: "Happypack/loader?id=css"
      },
      {
        test: /\.js$/,
        exclude: /node_modules/,
        include: path.resolve("src"),
        use: "Happypack/loader?id=js"
      }
    ]
  },
  output: {
    // name -> home a
    filename: "[name].js",
    path: path.resolve(__dirname, "dist")
  },
  plugins: [
    new Happypack({
      id: "css",
      use: ["style-loader", "css-loader"]
    }),
    new Happypack({
      id: "js",
      use: [
        {
          loader: "babel-loader",
          options: {
            presets: ["@babel/preset-env", "@babel/preset-react"]
          }
        }
      ]
    }),
    new webpack.DllReferencePlugin({
      manifest: path.resolve(__dirname, "dist", "manifest.json")
    }),
    new webpack.IgnorePlugin(/\.\/local/, /moment/),
    new HtmlWebpackPlugin({
      template: "./src/index.html",
      filename: "index.html"
    }),
    new webpack.DefinePlugin({
      DEV: JSON.stringify("production")
    })
  ]
};
複製代碼

Tree-Shaking

  • 基於 import/export 語法,Tree-Shaking 能夠在編譯的過程當中獲悉哪些模塊並無真正被使用,這些沒用的代碼,在最後打包的時候會被去除。適合於處理模塊級別的代碼,因此儘可能使用es6的import/export語法。

抽離公共代碼

把公共代碼抽離出來的好處:jquery

  • 減小網絡傳輸流量,下降服務器成本;
  • 雖然用戶第一次打開網站的速度得不到優化,但以後訪問其它頁面的速度將大大提高。

啓用抽離代碼:

// webpack 4.x版本以前的commonChunkPlugins
  optimization: {
    // 分割代碼塊
    splitChunks: {
      // 緩存組
      cacheGroups: {
        // 公共模塊
        common: {
          chunks: "initial",
          minSize: 0,
          // 最小公用模塊次數
          minChunks: 2
        },
        vendor: {
          priority: 1,
          // 抽離出來
          test: /node_modules/,
          chunks: "initial",
          minSize: 0,
          minChunks: 2
        }
      }
    }
  }
複製代碼

按需加載

按需加載的思想webpack

  • 一次不加載完全部的文件內容,只加載此刻須要用到的那部分(會提早作拆分)
  • 當須要更多內容時,再對用到的內容進行即時加載

經過 es6 的 import 實現按需加載,在使用 import() 分割代碼後,你的瀏覽器而且要支持 Promise API 才能讓代碼正常運行, 由於 import() 返回一個 Promise,它依賴 Promise。對於不原生支持 Promise 的瀏覽器,你能夠注入 Promise polyfill。git

let button = document.createElement("button");

button.innerHTML = "musion";

// vue,react的懶加載原理也是如此
button.addEventListener("click", function() {
  // es6草案中的語法, jsonp實現動態加載文件
  import("./source.js").then(data => {
    console.log(data.default);
  });
  console.log("click");
});

document.body.appendChild(button);
複製代碼

熱更新

模塊熱替換(HMR - Hot Module Replacement)是 webpack 提供的最有用的功能之一。它容許在運行時替換,添加,刪除各類模塊,而無需進行徹底刷新從新加載整個頁面,其思路主要有如下幾個方面:

  • 保留在徹底從新加載頁面時丟失的應用程序的狀態
  • 只更新改變的內容,以節省開發時間
  • 調整樣式更加快速,幾乎等同於就在瀏覽器調試器中更改樣式

啓用HRMes6

  1. 引入了webpack庫
  2. 使用了new webpack.HotModuleReplacementPlugin()
  3. 設置devServer選項中的hot字段爲true
let path = require("path");
let HtmlWebpackPlugin = require("html-webpack-plugin");
let webpack = require("webpack");

module.exports = {
  mode: "production",
  // 多入口
  entry: {
    index: "./src/index.js",
    other: "./src/other.js"
  },
  devServer: {
    // 啓用熱更新
    hot: true,
    port: 3000,
    open: true,
    contentBase: "./dist"
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        include: path.resolve("src"),
        use: {
          loader: "babel-loader",
          options: {
            presets: ["@babel/preset-env", "@babel/preset-react"],
            plugins: ["@babel/plugin-syntax-dynamic-import"]
          }
        }
      }
    ]
  },
  output: {
    // name -> home a
    filename: "[name].js",
    path: path.resolve(__dirname, "dist")
  },
  plugins: [
    new webpack.NamedModulesPlugin(), // 打印更新的模塊路徑
    new webpack.HotModuleReplacementPlugin() // 熱更新插件
  ]
};
複製代碼

參考文章

Webpack 如何實現熱更新?

前端性能優化原理與實踐

相關文章
相關標籤/搜索