webpack 打包工具入門


Posted by Nicolakacha on 2020-09-09

為什麼需要 webpack?

瀏覽器不支援 JavaScript 的 modules,所以我們透過 webpack 把寫好的 JavaScript 模組打包成一包,變成成瀏覽器可以看得懂的樣子。

index.js, utils.js → 經過 webpack 打包 → main.js

使用方法

在專案資料夾內安裝 webpack:

mkdir webpack-demo // 建立專案資料夾
cd webpack-demo // 切換目錄到專案資料夾
npm init -y // npm 初始化,建立 package.json
npm install webpack webpack-cli --save-dev // 安裝 webpack

通常開發者會把原始的檔案放在 src 資料夾,編譯過後的檔案放在 dist 資料夾,以方便管理。

執行 webpack 就可以進行編譯,預設會把 src 裡的檔案打包到 dist 內成名為 main.js 的檔案:

webpack

也可以在根目錄建立設定檔 webpack.config.js 來自定義路徑和要打包的模式,模式有兩種:

// production 的壓縮程度較高,檔案會變比較小,類似 uglify 過後的一串 JavaScript
// development 可讀性較高

const path = require('path');

module.exports = {
  mode: 'production',
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist'),
  },
};

可以在 package.json 建立 script:

"scripts": {
    "build": "webpack",
    "test": "echo \"Error: no test specified\" && exit 1"
  },

建立完 script 之後,輸入 npm run build 就可以執行 webpack 了:

npm run build

打包 npm 上別人第三方的 modules

webpack 強大之處在於它也可以把我們引入的第三方 modules 也一起打包進來。

使用 Loader 打包各種資源

除了打包 JavaScript 之外,webpack 把 modules 的概念延伸至更廣的範圍,各種資源都可以視為一個 module,所以我們也可以利用 webpack 幫我們打包圖片或 CSS。透過各種 loader 把資源載入 webpack,也可以順便做到 uglify JavaScript 或是 minify CSS 之類的編譯,以下會介紹一些常見的 loader 的使用。

css-loader

以打包 CSS 為例,在 webpack.config.js 加入 loader:

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: ['style-loader', 'css-loader'],
      },
    ],
  },
};

安裝 style.loader 和 css.loader:

npm install --save-dev css-loader style-loader

準備好 css 檔案:

body {
  background: rgba(255, 0, 0, 0.3);
}

把 style.css 引入 index.js:

import css from './style.css';
import $ from 'jquery';
import { first } from './utils';

$(document).ready(()=>{
  $('.btn').click(()=> {
    alert(first('hello'));
  })
})

打包成 main.js:

npm run build

在 index.html 引入 main.js:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <script src="dist/main.js"></script>
  <title>Document</title>
</head>
<body>
  <button class="btn">click me</button>
</body>
</html>

這樣就可以把 style.css 也融入 main.js 一起讓瀏覽器讀到了,它的原理其實就只是透過 DOM 元素動態建立一個 區塊並把 .css 當成字串插入 區塊裡面:

其他 loader 的使用也很簡單,安裝完之後,在 webpack.config.js 設定 loader 就可以了!

babel-loader

安裝:

npm install -D babel-loader @babel/core @babel/preset-env

在 webpack.config.js 設定檔加入:

rules: [
  {
    test: /\.m?js$/,
    exclude: /(node_modules|bower_components)/,
    use: {
      loader: 'babel-loader',
      options: {
        presets: ['@babel/preset-env']
      }
    }
  }

sass-loader

安裝:

npm install sass-loader sass webpack --save-dev

在 webpack.config.js 設定檔加入:

rules: [
  {
    test: /\.s[ac]ss$/i,
    use: [
      // Creates `style` nodes from JS strings
      'style-loader',
      // Translates CSS into CommonJS
      'css-loader',
      // Compiles Sass to CSS
      'sass-loader',
    ],
  },
],

利用 Dev Server 自動化打包

dev server 是一套可以在檔案改變的時候自動重新打包的 plugin。

要使用 dev server,一樣先安裝起來:

npm install webpack-dev-server --save-dev

在設定檔中加入以下程式碼,告訴 dev server 最後編譯完的檔案放在哪裡:

devServer: {
     contentBase: './dist',
 },

在 package.json 加入 start 指令:

"scripts": {
     "start": "webpack-dev-server --open",
}

暫時把 html 檔案也放在 dist 資料夾內,執行 npm run start:

npm run start

就會開始進入 dev server start 的狀態,之後 src 裡面的任何檔案有更改過,都會被 dev server 偵測到並自動重新打包:

使用 source map 看到原來程式碼的樣子

在 webpack.config.js 加入 source-map 程式碼:

module.exports = {
    mode: 'development',
    entry: {
      app: './src/index.js',
    },
      devtool: 'inline-source-map',
    output: {
      filename: '[name].bundle.js',
      path: path.resolve(__dirname, 'dist'),
    },
  };

在 debug 的時候,就可以在 chrome dev tool 溯源到真正的程式碼:

使用 HtmlWebpackPlugin

利用這個套件可以自動產生 HTML 檔案給我們的 webpack bundle 使用

安裝 HtmlWebpackPlugin:

npm install --save-dev html-webpack-plugin

在 webpack.config.js 加入 HtmlWebpackPlugin:
const HtmlWebpackPlugin = require('html-webpack-plugin');

並加上 plugins: [new HtmlWebpackPlugin()]

const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');

module.exports = {
  entry: 'index.js',
  output: {
    path: path.resolve(__dirname, './dist'),
    filename: 'index_bundle.js'
  },
  plugins: [new HtmlWebpackPlugin()]
};

自動產生的 HTML:

webpack 和 gulp 的差異

雖然這次介紹的 webpack 和上次介紹的 gulp 都有做到像是 babel, sass 轉 css 的功能,但它們本質上是不同的工具。

gulp 是一套 task manager,它的 task 可以有很多種,而不只侷限於 babel, sass, uglify js 這些,但它做不到像 webpack 那樣把所有的資源打包在一起。

而 webpack 的主要還是個打包工具,其目的是為了要讓瀏覽器能夠支援 module,只是我們在透過各種 loader 把資源載入給 webpack 打包成一包時,也可以進行 babel, sass 轉 css 之類的功能,所以才會覺得 gulp 和 webpack 很混淆。

簡單來說就是,gulp 可以做各種 tasks 但做不到打包,webpack 能做把各種資源打包,但做不到很多 gulp 才能做到的 tasks。

補充資源:

webpack 新手教學之淺談模組化與 snowpack


#Webpack #frontend







Related Posts

Day 180

Day 180

在迴圈內依條件判斷新增元素

在迴圈內依條件判斷新增元素

利用 Shell Scripts 寫一支小程式

利用 Shell Scripts 寫一支小程式


Comments