學習 Webpack5 之路(實踐篇)| 8月更文挑戰

前言

在上篇 《學習 Webpack5 之路(基礎篇)》中介紹了 Webpack 是什麼,爲何選擇 Webpack,Webpack 的基本概念介紹 3 個問題。javascript

本篇將從實踐出發,在第一章節《基礎配置》中使用 webpack 搭建一個基礎的支持模塊化開發的項目,在第二章節《進階配置》中使用 webpack 搭建一個 SASS + TS + React 的項目。css

本文依賴的 webpack 版本信息以下:html

1、基礎配置

接下來一塊兒配置一個基礎的 Webpack。java

將支持如下功能:node

  • 分離開發環境、生產環境配置;
  • 模塊化開發;
  • sourceMap 定位警告和錯誤;
  • 動態生成引入 bundle.js 的 HTML5 文件;
  • 實時編譯;
  • 封裝編譯、打包命令。

想直接看配置的同窗 -> 本文源碼地址:webpack Demo0react

1. 新建項目

新建一個空項目:webpack

// 新建 webpack-demo 文件夾
mkdir webpack-demo

// 進入 webpack-demo 目錄
cd ./webpack-demo

// 初始化項目
npm init -y
複製代碼

新建 2 個 js 文件,並進行模塊化開發:git

// 進入項目目錄
cd ./webpack-demo

// 建立 src 文件夾
mkdir src

// 建立 js文件
touch index.js
touch hello.js
複製代碼

index.js:es6

// index.js

import './hello.js'

console.log('index')
複製代碼

hello.js:github

// hello.js
console.log('hello webpack')
複製代碼

項目結構以下:

- src
    - index.js
    - hello.js
- package.json
- node_modules
複製代碼

2. 安裝

  1. 安裝 Node

Node 須要是最新版本,推薦使用 nvm 來管理 Node 版本。

將 Node.js 更新到最新版本,也有助於提升性能。除此以外,將你的 package 管理工具(例如 npm 或者 yarn)更新到最新版本,也有助於提升性能。較新的版本可以創建更高效的模塊樹以及提升解析速度。

我安裝的版本信息以下:

  • node v14.17.3
  • npm v6.14.13)
  1. 安裝 webpack
npm install webpack webpack-cli --save-dev
複製代碼

3. 新建配置文件

development(開發環境)  和 production(生產環境)  這兩個環境下的構建目標存在着巨大差別。爲代碼清晰簡明,爲每一個環境編寫彼此獨立的 webpack 配置

// 進入項目目錄
cd ./webpack-demo

// 建立 config 目錄
mkdir config

// 進入 config 目錄
cd ./config

// 建立通用環境配置文件
touch webpack.common.js

// 建立開發環境配置文件
touch webpack.dev.js

// 建立生產環境配置文件
touch webpack.prod.js
複製代碼

webpack-merge

使用 webpack-marge 合併通用配置和特定環境配置。

安裝 webpack-merge:

npm i webpack-merge -D
複製代碼

通用環境配置:

// webpack.common.js

module.exports = {} // 暫不添加配置

複製代碼

開發環境配置:

// webpack.dev.js

const { merge } = require('webpack-merge')
const common = require('./webpack.common')

module.exports = merge(common, {}) // 暫不添加配置

複製代碼

生產環境配置:

// webpack.prod.js

const { merge } = require('webpack-merge')
const common = require('./webpack.common')

module.exports = merge(common, {}) // 暫不添加配置

複製代碼

項目結構以下:

- config
    - webpack.common.js
    - webpack.dev.js
    - webpack.prod.js
- src
    - index.js
    - hello.js
- package.json
- node_modules
複製代碼

4. 入口(entry)

入口起點(entry point)  指示 webpack 應該使用哪一個模塊,來做爲構建其內部 依賴圖(dependency graph) 的開始。進入入口起點後,webpack 會找出有哪些模塊和庫是入口起點(直接和間接)依賴的。

在本例中,使用 src/index.js 做爲項目入口,webpack 以 src/index.js 爲起點,查找全部依賴的模塊。

修改 webpack.commom.js:

module.exports = merge(common, {
  // 入口
  entry: {
    index: './src/index.js',
  },
})
複製代碼

5. 輸出(output)

output 屬性告訴 webpack 在哪裏輸出它所建立的 bundle,以及如何命名這些文件。

生產環境的 output 須要經過 contenthash 值來區分版本和變更,可達到清緩存的效果,而本地環境爲了構建效率,則不引人 contenthash

新增 paths.js,封裝路徑方法:

const fs = require('fs')
const path = require('path')

const appDirectory = fs.realpathSync(process.cwd());
const resolveApp = relativePath => path.resolve(appDirectory, relativePath);

module.exports = {
  resolveApp,
  appPublic: resolveApp('public'),
  appHtml: resolveApp('public/index.html'),
  appSrc: resolveApp('src'),
  appDist: resolveApp('dist'),
  appTsConfig: resolveApp('tsconfig.json'),
}
複製代碼

修改開發環境配置文件 webpack.dev.js:

module.exports =  merge(common, {
  // 輸出
  output: {
    // bundle 文件名稱
    filename: '[name].bundle.js',
    
    // bundle 文件路徑
    path: resolveApp('dist'),
    
    // 編譯前清除目錄
    clean: true
  },
})
複製代碼

修改生產環境配置文件 webpack.prod.js:

module.exports =  merge(common, {
  // 輸出
  output: {
    // bundle 文件名稱 【只有這裏和開發環境不同】
    filename: '[name].[contenthash].bundle.js',
    
    // bundle 文件路徑
    path: resolveApp('dist'),
    
    // 編譯前清除目錄
    clean: true
  },
})
複製代碼

上述 filename 的佔位符解釋以下:

  • [name] - chunk name(例如 [name].js -> app.js)。若是 chunk 沒有名稱,則會使用其 id 做爲名稱
  • [contenthash] - 輸出文件內容的 md4-hash(例如 [contenthash].js -> 4ea6ff1de66c537eb9b2.js

6. 模式(mode)

經過 mode 配置選項,告知 webpack 使用相應模式的內置優化。

選項 描述
development 會將 DefinePlugin 中 process.env.NODE_ENV 的值設置爲 development. 爲模塊和 chunk 啓用有效的名。
production 會將 DefinePlugin 中 process.env.NODE_ENV 的值設置爲 production。爲模塊和 chunk 啓用肯定性的混淆名稱,FlagDependencyUsagePluginFlagIncludedChunksPluginModuleConcatenationPluginNoEmitOnErrorsPlugin 和 TerserPlugin 。

修改開發環境配置文件 webpack.dev.js:

module.exports =  merge(common, {
  // 開發模式
  mode: 'development',
})
複製代碼

修改生產環境配置文件 webpack.prod.js:

module.exports =  merge(common, {
  // 生產模式
  mode: 'production',
})
複製代碼

7. Source Map

當 webpack 打包源代碼時,可能會很難追蹤到 error 和 warning 在源代碼中的原始位置。

爲了更容易地追蹤 error 和 warning,JavaScript 提供了 source maps 功能,能夠將編譯後的代碼映射回原始源代碼。

修改開發環境配置文件 webpack.dev.js:

module.exports =  merge(common, {
  // 開發工具,開啓 source map,編譯調試
  devtool: 'eval-cheap-module-source-map',
})
複製代碼

source map 有許多 可用選項。本例選擇的是 eval-cheap-module-source-map

注:爲加快生產環境打包速度,不爲生產環境配置 devtool。

完成上述配置後,能夠經過 npx webpack --config config/webpack.prod.js 打包編譯。

編譯後,會生成這樣的目錄結構:

image.png

8. HtmlWebpackPlugin

npx webpack --config config/webpack.prod.js 後僅生成了 bundle.js,咱們還須要一個 HTML5 文件,用來動態引入打包生成的 bundle 文件。

引入 HtmlWebpackPlugin 插件,生成一個 HTML5 文件, 其中包括使用 script 標籤的 body 中的全部 webpack 包。

安裝:

npm install --save-dev html-webpack-plugin
複製代碼

修改通用環境配置文件 webpack.commom.js:

module.exports = {
  plugins: [
    // 生成html,自動引入全部bundle
    new HtmlWebpackPlugin({
      title: 'release_v0',
    }),
  ],
}
複製代碼

從新 webpack 編譯 npx webpack --config config/webpack.prod.js,生成的目錄結構以下:

image.png

新生成了 index.html,動態引入了 bundle.js 文件:

<!DOCTYPE html>
<html>
 <head>
  <meta charset="utf-8" />
  <title>release_v0</title>
  <meta name="viewport" content="width=device-width,initial-scale=1" />
  <script defer="defer" src="index.468d741515bfc390d1ac.bundle.js"></script>
 </head>
 <body></body>
</html>
複製代碼

9. DevServer

在每次編譯代碼時,手動運行 npx webpack --config config/webpack.prod.js 會顯得很麻煩,webpack-dev-server 幫助咱們在代碼發生變化後自動編譯代碼。

webpack-dev-server 提供了一個基本的 web server,而且具備實時從新加載功能。

webpack-dev-server 默認配置 conpress: true,爲每一個靜態文件開啓 gzip compression

安裝:

npm install --save-dev webpack-dev-server
複製代碼

修改開發環境配置文件 webpack.dev.js:

module.exports = merge(common, {
  devServer: {
    // 告訴服務器從哪裏提供內容,只有在你想要提供靜態文件時才須要。
    contentBase: './dist',
  },
})
複製代碼

完成上述配置後,能夠經過 npx webpack serve --open --config config/webpack.dev.js 實時編譯。

效果如圖:

image.png

10.執行命令

上述配置文件完成後,優化 webpack 的實時編譯、打包編譯指令。

經過 cross-env 配置環境變量,區分開發環境和生產環境。

安裝:

npm install --save-dev cross-env
複製代碼

修改 package.json:

{
    "scripts": {
        "dev": "cross-env NODE_ENV=development webpack serve --open --config config/webpack.dev.js",
        "build": "cross-env NODE_ENV=production webpack --config config/webpack.prod.js"
      },
}
複製代碼

如今能夠運行 webpack 指令:

  • npm run dev:本地構建;
  • npm run build:生產打包。

以上咱們完成了一個基於 webpack 編譯的支持模塊化開發的簡單項目。

源碼地址:webpack Demo0

2、進階配置

本章節將繼續完善配置,在上述配置基礎上,用 Webpack 搭建一個 SASS + TS + React 的項目。

將支持如下功能:

  • 加載圖片;
  • 加載字體;
  • 加載 CSS;
  • 使用 SASS;
  • 使用 PostCSS,並自動爲 CSS 規則添加前綴,解析最新的 CSS 語法,引入 css-modules 解決全局命名衝突問題;
  • 使用 React;
  • 使用 TypeScript。

想直接看配置的同窗 -> 本文源碼地址:webpack Demo1

1. 加載圖片(Image)

在 webpack 5 中,可使用內置的 Asset Modules,將 images 圖像混入咱們的系統中。

修改通用環境配置文件 webpack.commom.js:

const paths = require('./paths');
module.exports = {
    module: {
        rules: [
          {
            test: /\.(png|svg|jpg|jpeg|gif)$/i,
            include: paths.appSrc,
            type: 'asset/resource',
          },
        ],
      },
}
複製代碼

在實際開發過程當中,推薦將大圖片上傳至 CDN,提升加載速度。

2. 加載字體(Font)

使用 Asset Modules 接收字體文件。

修改通用環境配置文件 webpack.commom.js:

module.exports = {
    module: {
        rules: [
            {
               test: /.(woff|woff2|eot|ttf|otf)$/i,
               include: [
                  resolveApp('src'),
                ],
               type: 'asset/resource',
             },
         ]
     }
 }
     
複製代碼

在這裏我發現,我不新增對 ttf 文件的配置,也可以引入字體不報錯,不知道是什麼問題,先記錄一下,有知道緣由的大佬移步評論區。

在實際開發過程當中,推薦將字體文件壓縮上傳至 CDN,提升加載速度。如配置字體的文字是固定的,還能夠針對固定的文字生成字體文件,能夠大幅縮小字體文件體積。

3. 加載 CSS

爲了在 JavaScript 模塊中 import 一個 CSS 文件,須要安裝並配置 style-loader 和 css-loader

3.1 style-loader

style-loader 用於將 CSS 插入到 DOM 中,經過使用多個 <style></style> 自動把 styles 插入到 DOM 中.

3.2 css-loader

css-loader 對 @import 和 url() 進行處理,就像 js 解析 import/require() 同樣,讓 CSS 也能模塊化開發。

3.3 安裝配置

安裝 CSS 相關依賴:

npm install --save-dev style-loader css-loader
複製代碼

修改通用環境配置文件 webpack.commom.js:

const paths = require('./paths');
module.exports = {
    module: {
        rules: [
            {
                test: /\.css$/,
                include: paths.appSrc,
                use: [
                  // 將 JS 字符串生成爲 style 節點
                  'style-loader',
                  // 將 CSS 轉化成 CommonJS 模塊
                  'css-loader',
                ],
              },
          ]
      }
  }
複製代碼

4. 使用 SASS

4.1 Sass

Sass 是一款強化 CSS 的輔助工具,它在 CSS 語法的基礎上增長了變量、嵌套、混合、導入等高級功能。

4.2 sass-loader

sass-loader 加載 Sass/SCSS 文件並將他們編譯爲 CSS。

4.3 安裝配置

安裝 SASS 相關依賴:

npm install --save-dev sass-loader sass 
複製代碼

修改通用環境配置文件 webpack.commom.js:

const paths = require('./paths');
module.exports = {
    module: {
        rules: [
        {
        test: /.(scss|sass)$/,
        include: paths.appSrc,
        use: [
          // 將 JS 字符串生成爲 style 節點
          'style-loader',
          // 將 CSS 轉化成 CommonJS 模塊
          'css-loader',
          // 將 Sass 編譯成 CSS
          'sass-loader',
        ],
      },
複製代碼

5. 使用 PostCSS

5.1 PostCSS

PostCSS 是一個用 JavaScript 工具和插件轉換 CSS 代碼的工具。

  • 能夠自動爲 CSS 規則添加前綴;
  • 將最新的 CSS 語法轉換成大多數瀏覽器都能理解的語法;
  • css-modules 解決全局命名衝突問題。

5.2 postcss-loader

postcss-loader 使用 PostCSS 處理 CSS 的 loader。

5.3 安裝配置

安裝 PostCSS 相關依賴:

npm install --save-dev postcss-loader postcss postcss-preset-env
複製代碼

修改通用環境配置文件 webpack.commom.js:

const paths = require('./paths');
module.exports = {
    module: {
        rules: [
          {
            test: /\.module\.(scss|sass)$/,
            include: paths.appSrc,
            use: [
              // 將 JS 字符串生成爲 style 節點
              'style-loader',
              // 將 CSS 轉化成 CommonJS 模塊
              {
                loader: 'css-loader',
                options: {
                  // Enable CSS Modules features
                  modules: true,
                  importLoaders: 2,
                  // 0 => no loaders (default);
                  // 1 => postcss-loader;
                  // 2 => postcss-loader, sass-loader
                },
              },
              // 將 PostCSS 編譯成 CSS
              {
                loader: 'postcss-loader',
                options: {
                  postcssOptions: {
                    plugins: [
                      [
                        // postcss-preset-env 包含 autoprefixer
                        'postcss-preset-env',
                      ],
                    ],
                  },
                },
              },
              // 將 Sass 編譯成 CSS
              'sass-loader',
            ],
          },
        ],
      },
}
複製代碼

爲提高構建效率,爲 loader 指定 include,經過使用 include 字段,僅將 loader 應用在實際須要將其轉換的模塊。

5. 使用 React + TypeScript

爲了讓項目的配置靈活性更高,不使用 create-reate-app 一鍵搭建項目,而是手動搭建 React 對應的配置項。

安裝 React 相關:

npm i react react-dom @types/react @types/react-dom -D
複製代碼

安裝 TypeScript 相關:

npm i -D typescript esbuild-loader
複製代碼

爲提升性能,摒棄了傳統的 ts-loader,選擇最新的 esbuild-loader。

修改通用環境配置文件 webpack.commom.js:

const paths = require('./paths');
 module.exports = {
    resolve: {
        extensions: ['.tsx', '.ts', '.js'],
    },
    module: {
        rules: [
            {
                test: /\.(js|ts|jsx|tsx)$/,
                include: paths.appSrc,
                use: [
                  {
                    loader: 'esbuild-loader',
                    options: {
                      loader: 'tsx',
                      target: 'es2015',
                    },
                  }
                ]
              },
         ]
     }
 }
複製代碼

TypeScript 是 JavaScript 的超集,爲其增長了類型系統,能夠編譯爲普通 JavaScript 代碼。

爲兼容 TypeScript 文件,新增 typescript 配置文件 tsconfig.json:

{
    "compilerOptions": {
        "outDir": "./dist/",
        "noImplicitAny": true,
        "module": "es6",
        "target": "es5",
        "jsx": "react",
        "allowJs": true,
        "moduleResolution": "node",
        "allowSyntheticDefaultImports": true,
        "esModuleInterop": true,
      }
}
複製代碼

若是想在 TypeScript 中保留如import _ from 'lodash';的語法被讓它做爲一種默認的導入方式,須要在文件 tsconfig.json 中設置 "allowSyntheticDefaultImports" : true 和 "esModuleInterop" : true 。

注意:這兒有坑

1. "allowSyntheticDefaultImports": true 配置

TypeScript 配置文件 tsconfig.json 須要加 "allowSyntheticDefaultImports": true 配置,不然會提示 can only be default-imported using the 'allowSyntheticDefaultImports' flag

{
    "compilerOptions": {
        "allowSyntheticDefaultImports": true
    },
}
複製代碼

不加 "allowSyntheticDefaultImports": true 的加報錯信息以下:

image.png

2. tsx 和 jsx 不能混合使用

在 tsx 中引入 jsx 文件報錯以下:

image.png

以上咱們完成了一個基於 webpack 編譯的 SASS + TS + React 項目。

源碼地址:webpack Demo1

3、總結

本文從 Webpack 基礎配置、Webpack 進階配置 2 個角度進行講述,從 Webpack 實踐着手,和你一塊兒瞭解 Webpack。

下一篇《學習 Webpack5 之路(優化篇)》將從繼續優化項目配置,嘗試搭建一個最優的 Webpack 配置,敬請期待。

本文源碼:

但願能對你有所幫助,感謝閱讀~

別忘了點個贊鼓勵一下我哦,筆芯❤️

往期精彩

參考資料

相關文章
相關標籤/搜索