Working with Modules and Code Splitting

hyperredstart

HyperRedStart

Posted on April 8, 2022

Working with Modules and Code Splitting

此章節會介紹 Webpack 中的 模組 (Module) 以及程式碼分離 (Code Splitting) 兩個 Webpack 特色,透過以上特色使專案代碼分割並以最適合的方式分割代碼。

此章節包含內容:

  • 解釋 Webpack 中的 Modules
  • 了解 Code Splitting 機制
  • 將所需要的模組預先載入
  • 範例展示

Module

透過 Webpack 模組會將共用的方法集合在同一檔案內,並會依照不同類型進行分類,如下圖所釋:
Image description

Webpack 根據不同的模組標準有以下幾種語句:

ECMA 2015 : import
CommonJS : require
ASM(Asynchronous Module Definition) : define / require
CSS : @import stylesheet

模組支援語言

確保 Webpack 支援這些模組,必須要使用允許的程式語言,透過 Webpack 社群所建立的 Loaders 支援了大量的語言例如以下幾種。

  • TypeScript
  • SASS
  • LESS
  • C++
  • Babel
  • Bootstrap

載入模組路徑

  • 絕對路徑
    從根目錄開始定位到引用檔案
    import 'C:\Users\project\file';

  • 相對路徑
    根據當前檔案定位到引用檔案
    import '../src/file';

  • 模組路徑
    從模組中搜尋名稱,引用模組內的檔案
    import 'module/sub-directory/file';

Code Splitting

允許使用者將片段的程式放入不同的套件中,並且可以按需求或是並行載入,被認為是 Webpack 最具矚目的功能。

達成方法

  • Entry Points: 可以手動設置片段程式進入點設定檔
  • SplitChunksPlugin: 並免相同程式載入,將片段的程式碼整合成群組這種群組稱為 "chunks"
  • Dynamic Imports: 透過程式來動態將片段程式碼載入

Entry Points

使用 Entry points 是最簡單可以達成 code splitting 的方法。

我們根據第一章範例的專案,增改以下內容

src/another-module.js

import _ from 'lodash';
console.log(_.join(['Another', 'module', 'loaded!'], ' '));
Enter fullscreen mode Exit fullscreen mode

webpack.config.js

const path = require('path');
module.exports = {
  mode: 'development',
  entry: {
    index: './src/index.js',
    another: './src/another-module.js'
  },
  output: {
    filename: '[name].bundle.js',
    path: path.resolve(__dirname, 'dist')
  }
};
Enter fullscreen mode Exit fullscreen mode

編譯專案

$ npm run build
Asset Size Chunks Chunk Names
another.bundle.js 550 KiB another [emitted] another
index.bundle.js 550 KiB index [emitted] index
Entrypoint index = index.bundle.js
Entrypoint another = another.bundle.js
Enter fullscreen mode Exit fullscreen mode

編譯完成後再 dist 目錄下產生了 index.bundle.js/another.bundle.js 倆的檔案,但因為兩支js程式都有加載 lodash 導致兩支程式代碼重複。
Image description

SplitChunksPlugin

預防重複程式碼

從剛剛的 entrypoints 結果我們的程式碼重複了,為了避免這個狀況我們可以使用 SplitChunks 的 Webpack 套件

webpack.config.js
在 module 中 增加 optimization 參數,再進行編譯

const path = require('path');
module.exports = {
  mode: 'development',
  entry: {
    index: './src/index.js',
    another: './src/another-module.js'
  },
  output: {
    filename: '[name].bundle.js',
    path: path.resolve(__dirname, 'dist')
  },
  optimization: {
    splitChunks: {
      chunks: 'all'
    }
  }
};
Enter fullscreen mode Exit fullscreen mode

編譯專案

$npm run build

> 01-sample-app2@1.0.0 build
> webpack --config webpack.config.js
asset vendors-node_modules_lodash_lodash_js.bundle.js 550 KiB [emitted] (id hint: vendors)
asset index.bundle.js 8.83 KiB [emitted] (name: index)
asset another.bundle.js 8.75 KiB [emitted] (name: another)
Entrypoint index 559 KiB = vendors-node_modules_lodash_lodash_js.bundle.js 550 KiB index.bundle.js 8.83 KiB
Entrypoint another 559 KiB = vendors-node_modules_lodash_lodash_js.bundle.js 550 KiB another.bundle.js 8.75 KiB
runtime modules 7.3 KiB 16 modules
cacheable modules 532 KiB
  ./src/index.js 212 bytes [built] [code generated]
  ./src/another-module.js 83 bytes [built] [code generated]
  ./node_modules/lodash/lodash.js 531 KiB [built] [code generated]
webpack 5.69.1 compiled successfully in 326 ms
Enter fullscreen mode Exit fullscreen mode

此時除了 anotherjs 與 indexjs 外多了一個檔案 "vendors-node_modules_lodash_lodash_js.bundle.js" lodash 的代碼就統一放在這個檔案,避免代碼重複

Image description

現在我們了解了如何避免重複代碼,接下來就要換到另一個更難的主題,Dynamic Imports。

Dynamic Imports

Dynamic Imports 是實現 on-demand imports 的必要方法。

webpack.config.js
output 中增加 chunkFilename 指定Dynamic Import Moudle 檔案名稱

const path = require('path');
module.exports = {
  mode: 'development',
  entry: {
    index: './src/index.js'
  },
  output: {
    filename: '[name].bundle.js',
    chunkFilename: '[name].bundle.js',
    path: path.resolve(__dirname, 'dist')
  },
};
Enter fullscreen mode Exit fullscreen mode

index.js
透過在程式中 import 以動態的方式載入第三方模組,編譯過後就會產生相應檔案,並透過 webpackChunkName 指定產出名稱。

function getComponent() {
  return import(/* webpackChunkName: "lodash" */ 'lodash').then(({ default: _ }) => {
  var element = document.createElement('div');

  element.innerHTML = _.join(['Hello', 'Webpack'], ' ');

  return element;

  }).catch(error => 'An error occurred while loading the component');
}

getComponent().then(component => {
  document.body.appendChild(component);
});
Enter fullscreen mode Exit fullscreen mode

編譯專案

$npm run build
> 01-sample-app2@1.0.0 build
> webpack --config webpack.config.js

asset another.bundle.js 554 KiB [emitted] (name: another)
asset lodash.bundle.js 550 KiB [emitted] (name: lodash) (id hint: vendors)
asset index.bundle.js 14.2 KiB [emitted] (name: index)
runtime modules 9.2 KiB 17 modules
cacheable modules 532 KiB
  ./src/index.js 390 bytes [built] [code generated]
  ./src/another-module.js 83 bytes [built] [code generated]
  ./node_modules/lodash/lodash.js 531 KiB [built] [code generated]
webpack 5.69.1 compiled successfully in 316 ms
Enter fullscreen mode Exit fullscreen mode

透過這些方法可以改善瀏覽器的快取速度。

Caching

章節最後也介紹了其他的 Plugin 來協助我們來預先載入或是減少記憶體用量。

const path = require("path");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
  entry: "./src/index.js",
  plugins: [
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      title: "Output Management",
      title: "Caching",
    }),
  ],
  output: {
    filename: "bundle.js",
    filename: "[name].[contenthash].js",
    path: path.resolve(__dirname, "dist"),
  },
};
Enter fullscreen mode Exit fullscreen mode

好了我們的第二章節 Working with Modules and Code Splitting 結束 🎉
後面的章節會再介紹有關 Webpack 的強大功能!敬請期待!

💖 💪 🙅 🚩
hyperredstart
HyperRedStart

Posted on April 8, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

Using Configurations and Options
javascript Using Configurations and Options

April 12, 2022

Introduction to Webpack 5
javascript Introduction to Webpack 5

March 3, 2022