Working with Modules and Code Splitting
Tags: javascript, review, webpack, beginners
Published: 2022-04-08 06:30:19

---
title: Working with Modules and Code Splitting
tags: javascript, review, webpack, beginners
published: true
series: Webpack 5 Up and Running Review
author: Corey Lai
---

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

此章節包含內容：
- 解釋 Webpack 中的 Modules
- 了解 Code Splitting 機制
- 將所需要的模組預先載入
- 範例展示


## Module
  透過 Webpack 模組會將共用的方法集合在同一檔案內，並會依照不同類型進行分類，如下圖所釋：
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pjqv2fd622cr3kjyj3u3.png)  


### 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**
```javascript
import _ from 'lodash';
console.log(_.join(['Another', 'module', 'loaded!'], ' '));
```

**webpack.config.js**
```javascript
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')
  }
};
```

**編譯專案**
```bash
$ 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
```

  編譯完成後再 dist 目錄下產生了 index.bundle.js/another.bundle.js 倆的檔案，但因為兩支js程式都有加載 lodash 導致兩支程式代碼重複。
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pr0sikcl7avdkk8wdbam.png) 

### SplitChunksPlugin
預防重複程式碼

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

**webpack.config.js**
在 module 中 增加 optimization 參數，再進行編譯
```javascript
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'
    }
  }
};
```

**編譯專案**
```bash
$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
```

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

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h96k3c3p81gwco81d43h.png)

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

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

**webpack.config.js**
output 中增加 chunkFilename 指定Dynamic Import Moudle 檔案名稱
```javascript
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')
  },
};
```

**index.js**
透過在程式中 import 以動態的方式載入第三方模組，編譯過後就會產生相應檔案，並透過 webpackChunkName 指定產出名稱。
```javascript
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);
});
```

**編譯專案**
```bash
$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
```

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

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

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


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