webpack 5.x 快速入门

简介

通过具体的示例,结合 Bootstrap 4.x、Vue.js 3.x 和 Electron 11.x,全面介绍 webpack 5.x 的配置、用法。

本文源码 在此

起步

检查 node.js 和 npm 的版本。

$ node -v
v15.10.0
$ npm  -v
7.6.0

新建一个目录,进入。新建 package.json 文件。

$ mkdir webpack-demo
$ cd webpack-demo
$ npm init -y

为了提高下载速度,安装 cnpm。

$ npm install -g cnpm --registry=https://registry.npm.taobao.org

用 cnpm 安装 webpack 及其命令行工具。检查安装的版本。

$ cnpm i -D webpack webpack-cli
$ npx webpack -v
webpack 5.24.2
webpack-cli 4.5.0

package.jsonscripts 中添加 "build": "webpack"

  "scripts": {
    "build": "webpack"
  }

这样就可以用 npm run build 命令来执行一次打包。

新建两个子目录 srcdist,分别用来放置源文件和打包输出。

$ mkdir src dist

新建 ./src/app.js 文件,作为打包入口。

$ touch ./src/app.js
alert('Hello webpack!');

新建 index.html 文件,假设打包输出为 ./dist/bundle.js

$ touch index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <title>Hello World!</title>
  </head>
  <body>
    <p>Hello webpack, Bootstrap 4.x, Vue.js 3.x and Electron 11.x!</p>

    <script src="./dist/bundle.js"></script>
  </body>
</html>

最终的目录结构:

$ tree -L 2 -I "node_modules"
.
|-- dist
|-- index.html
|-- package.json
`-- src
    `-- app.js

2 directories, 3 files

基本配置

习惯上将 webpack 的配置文件命名为 webpack.config.js,须手动创建,放在项目根目录下,webpack 会自动引用此配置文件。

$ touch webpack.config.js
const path = require('path');

module.exports = {
  // production 生产环境
  // 或 development 开发环境
  mode: 'production',

  // 打包的起点
  entry: './src/app.js',

  // 打包的输出
  output: {
    filename: 'bundle.js',
    // 输出目录的绝对路径和相对路径(相对网站目录)
    path: path.resolve(__dirname, 'dist'),
    publicPath: '/'
  },

  // 模块,装载器等,见下文
  module: {},

  // 插件,`.css` 的剥离和压缩、`.html` 的动态生成等,见下文
  plugins: []
};

尝试进行一次打包:

$ npm run build

> webpack-demo@1.0.0 build
> npx webpack

asset bundle.js 24 bytes [compared for emit] [minimized] (name: main)
./src/app.js 28 bytes [built] [code generated]
webpack 5.24.2 compiled successfully in 278 ms

$ tree -L 2 -I "node_modules"
.
|-- dist
|   `-- bundle.js
|-- index.html
|-- package.json
|-- src
|   `-- app.js
`-- webpack.config.js

2 directories, 5 files

尝试用浏览器打开 index.html,看是否有弹窗。

认识装载器

装载器在 webpack.config.jsmodule.rules 中列出。

  module: {
    rules: [
    ]
  }

rules 中的元素是对象类型,通常包含 test use 两个属性。test 匹配文件名,use 列出装载器,由此形成一条流水线(调用顺序与书写顺序相反)。

  module: {
    rules: [
      {
        test: /\.less$/,
        use: [
            'style-loader',
            {
                loader: 'css-loader',
                options: { importLoaders: 1 }
            },
            {
                loader: 'less-loader',
                options: { noIeCompat: true }
            }
        ]
      }
    ]
  }

上例表示以 .less 结尾的文件依次用 less-loader css-loader style-loader 处理。

只需一个装载器时,用 loader 代替 use

      {
        test: /\.(png|svg|jpg|gif)$/,
        loader: 'file-loader'
      }

添加转载器

安装上述提到的 3 个的装载器:

$ cnpm i -D style-loader css-loader file-loader
  • css-loader 负责解析 .js 文件中的 import '.css' 以及 .css 文件中的 @import '.css'
  • style-loader 负责生成 <style> 标签并追加到 <head> 标签中。

webpack.config.jsmodule.rules 中添加:

  module: {
    rules: [
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      },
      {
        test: /\.(png|svg|jpg|gif)$/,
        loader: 'file-loader'
      },
      {
        test: /\.(woff|woff2|eot|ttf|otf)$/,
        loader: 'file-loader'
      }
    ]
  }

新建 ./src/app.css 文件。

$ touch ./src/app.css
p {
  background-color: blue;
}

./src/app.js 中引入:

import './app.css';
$ rm -f ./dist/* && npm run build
$ tree -L 2 -I "node_modules"
.
|-- dist
|   `-- bundle.js
|-- index.html
|-- package.json
|-- src
|   |-- app.css
|   `-- app.js
`-- webpack.config.js

2 directories, 6 files

检查 <p> 标签的背景色。

使用 Bootstrap 4.x

安装 Bootstrap 及其依赖包。

$ cnpm i -D bootstrap jquery popper.js

./src/app.js 的顶部插入:

import 'bootstrap';
import 'bootstrap/dist/css/bootstrap.min.css';

index.html 中使用 Bootstrap 的样式类 text-success

    <p class="text-danger">Hello webpack, Bootstrap 4.x, Vue.js 3.x and Electron 11.x!</p>

重新打包,检查字体的颜色。

省略后缀名

.js 文件中用 import 导入模块时,可以省略模块的后缀名,但必须在 webpack.config.jsresolve.extensions 中列出。

  resolve: {
    extensions: ['.js', '.json', '.css', '.sass', '.vue']
  }

多入口

新建 JS 文件。

$ touch ./src/admin.js
alert('Hello admin!');

webpack.config.js 中,entry 改成对象类型,output.filename[name] 拼接。

  entry: {
    app: './src/app.js',
    admin: './src/admin.js'
  },
  output: {
    filename: '[name].bundle.js',
  },

相应修改 index.html

    <script src="./dist/app.bundle.js"></script>
    <script src="./dist/admin.bundle.js"></script>
$ rm -f ./dist/* && npm run build
$ tree -L 2 -I "node_modules"
.
|-- dist
|   |-- admin.bundle.js
|   |-- app.bundle.js
|   |-- app.bundle.js.LICENSE.txt
|-- index.html
|-- package.json
|-- src
|   |-- admin.js
|   |-- app.css
|   `-- app.js
`-- webpack.config.js

2 directories, 9 files

重新打包,检查是否有两次弹窗。

第二次弹窗

分离第三方库

可以单独打包第三方库,而不在 .js 文件中引入,只须在 entry.vendors 中列出第三方库,这会得到 vendors.bundle.js 文件(文件名取决于 output.filename)。

  entry: {
    vendors: ['bootstrap', 'jquery', 'popper.js']
  }

index.html 中添加:

    <script src="./dist/vendors.bundle.js"></script>

注释 ./src/app.js 中的 import 'bootstrap'

// import 'bootstrap';

剥离 CSS

剥离 .js 引入的 .css,合并成单独的 .css 文件,每个入口对应一个。

安装插件 mini-css-extract-plugin

$ cnpm i -D mini-css-extract-plugin

plugins 中实例化插件,在 module.rules 中应用插件。同时,为方便演示:

  • 删除 ./src/admin.js,相应修改 entry
  • 修改 output.filename
$ rm -f ./src/admin.js
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

  entry: {
    // admin: './src/admin.js',
  },
  output: {
    // filename: '[name].bundle.js',
    filename: '[name].js',
  },
  module: {
    rules: [
      {
        test: /\.css/,
        use: [MiniCssExtractPlugin.loader, 'css-loader']
      },
    ]
  },
  plugins: [
    // 将输出 `[name].css`
    new MiniCssExtractPlugin({ filename: '[name].css' })
  ]

修改 index.html

    <link rel="stylesheet" href="./dist/app.css">

    <script src="./dist/vendors.js"></script>
    <script src="./dist/app.js"></script>
$ rm -f ./dist/* && npm run build
$ tree -L 2 -I "node_modules"
.
|-- dist
|   |-- app.css
|   |-- app.js
|   |-- vendors.js
|   `-- vendors.js.LICENSE.txt
|-- index.html
|-- package.json
|-- src
|   |-- app.css
|   `-- app.js
`-- webpack.config.js

2 directories, 9 files

压缩 CSS

安装插件 css-minimizer-webpack-plugin

$ cnpm i -D css-minimizer-webpack-plugin

optimization.minimizer 中实例化即可。

const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');

  optimization: {
    minimizer: ['...', new CssMinimizerPlugin()]
  }

... 表示对 minimizer 进行扩展而不是覆盖,以保留内置的 .js 压缩插件。

自动清理打包输出

安装插件 clean-webpack-plugin

$ cnpm i -D clean-webpack-plugin

plugins 中实例化即可。

const { CleanWebpackPlugin } = require('clean-webpack-plugin');

  plugins: [
    new CleanWebpackPlugin()
  ]

带 hash 的文件名、自动生成 index.html

安装插件 html-webpack-plugin

$ cnpm i -D html-webpack-plugin

文件名用 [fullhash] 拼接。在 plugins 中调用 HtmlWebpackPlugin 指定模板 .ejs、输出文件名、是否将 .css .js 直接嵌入模板而不通过 <link> <script> 引用。

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

  output: {
    filename: '[name].[fullhash].js',
  },
  plugins: [
    new MiniCssExtractPlugin({ filename: '[name].[fullhash].css' }),
    new HtmlWebpackPlugin({
      template: './src/index.ejs',
      filename: 'index.html',
      inject: false
    })
  ]

删除没用的 index.html,新建模板 ./src/index.ejs

$ rm -f ./index.html
$ touch ./src/index.ejs
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <title>Hello World!</title>

    <link rel="stylesheet" href="<%= htmlWebpackPlugin.files.css[0] %>">
  </head>
  <body>
    <p class="text-danger">Hello webpack, Bootstrap 4.x, Vue.js 3.x and Electron 11.x!</p>

    <script src="<%= htmlWebpackPlugin.files.js[0] %>"></script>
    <script src="<%= htmlWebpackPlugin.files.js[1] %>"></script>
  </body>
</html>

重新打包,检查是否正确生成 ./dist/index.html

$ npm run build
$ tree -L 2 -I "node_modules"
.
|-- dist
|   |-- app.7faf852820591e38bbd0.css
|   |-- app.7faf852820591e38bbd0.js
|   |-- index.html
|   |-- vendors.7faf852820591e38bbd0.js
|   `-- vendors.7faf852820591e38bbd0.js.LICENSE.txt
|-- package.json
|-- src
|   |-- app.css
|   |-- app.js
|   `-- index.ejs
`-- webpack.config.js

2 directories, 10 files

开发时配置

安装 webpack-merge

$ cnpm i -D webpack-merge

新增配置文件 webpack.dev.config.js

$ touch webpack.dev.config.js
const { merge } = require('webpack-merge');
const common = require('./webpack.config.js');

module.exports = merge(common, {
  // 开发环境
  mode: 'development',
  // 到源码的映射
  devtool: 'inline-source-map'
});

用处见下文。

使用 webpack-dev-server

webpack-dev-server 可以快速开启一个 Web 服务,并在文件发生改变时重新编译并通知浏览器。

安装 webpack-dev-server

$ cnpm i -D webpack-dev-server

webpack.dev.config.js 中新增配置项 devServer

  devServer: {
    host: "127.0.0.1",
    port: 8080,
    contentBase: __dirname + '/assets', // 图标、图片等静态资源的位置
    historyApiFallback: true // 兼容 HTML5 history API
  }

修改 package.jsonscripts

  "scripts": {
    "dev": "webpack serve -c webpack.dev.config.js --open"
  }

执行 npm run dev 查看效果。

$ tree -L 2 -I "node_modules"
.
|-- dist
|-- package.json
|-- src
|   |-- app.css
|   |-- app.js
|   `-- index.ejs
|-- webpack.config.js
`-- webpack.dev.config.js

2 directories, 6 files

使用 Vue.js 3.x

安装。

$ cnpm i -D vue@next vue-loader@next @vue/compiler-sfc

修改 webpack.config.jsmodule.rulesplugins

const webpack = require('webpack');
const { VueLoaderPlugin } = require("vue-loader");

  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader'
      },
    ]
  },
  plugins: [
    new VueLoaderPlugin(),
    new webpack.DefinePlugin({
      "__VUE_OPTIONS_API__": true,
      "__VUE_PROD_DEVTOOLS__": false,
    })
  ],

修改 ./src/index.ejs

  <body>
    <div id="app" v-cloak></div>

  </body>

修改 ./src/app.css

[v-cloak] {
  display: none;
}

新增文件 ./src/app.vue

$ touch ./src/app.vue
<template>
  <p @click="onClick" class="text-danger">
    Hello webpack, Bootstrap 4.x, Vue.js 3.x and Electron 11.x!
  </p>
</template>

<script>
  export default {
    methods: {
      onClick() {
        alert('clicked');
      }
    }
  };
</script>

./src/app.js 中添加:

import { createApp } from 'vue';
import app from './app.vue';

createApp(app).mount('#app');
$ tree -L 2 -I "node_modules"
.
|-- dist
|-- package.json
|-- src
|   |-- app.css
|   |-- app.js
|   |-- app.vue
|   `-- index.ejs
|-- webpack.config.js
`-- webpack.dev.config.js

2 directories, 7 files

执行 npm run dev 查看效果。

使用 Electron 11.x

webpack.config.js 中增加 target 并修改 output

  target: 'electron-renderer',

  output: {
    // filename: '[name].[fullhash].js',
    filename: '[name].js',
    // publicPath: '/'
    publicPath: './'

  plugins: [
    // new MiniCssExtractPlugin({ filename: '[name].[fullhash].css' }),
    new MiniCssExtractPlugin({ filename: '[name].css' }),

必须设定 target,否则不能在渲染进程中使用 fs path 等模块。

安装 Electron。

$ cnpm i -D electron electron-packager
$ npx electron -v
v11.3.0
$ npx electron-packager --version
Electron Packager 15.2.0
Node v15.10.0
Host Operating system: win32 10.0.19042 (x64)

新建 main.js 作为主进程。

$ touch main.js
const { app, BrowserWindow } = require('electron');

function createWindow() {
    const options = {
        width: 400,
        height: 300,
        webPreferences: { nodeIntegration: true }
    };
    const win = new BrowserWindow(options);
    win.loadFile('./dist/index.html');
}

app.whenReady().then(createWindow);

app.on('window-all-closed', () => {
    if (process.platform !== 'darwin') app.quit();
});

app.on('activate', () => {
    if (BrowserWindow.getAllWindows().length === 0) createWindow();
});

win.loadFile('./dist/index.html'); 取决于 electron 的工作目录。

修改 package.json

  "main": "main.js",
  "scripts": {
    "build": "webpack",
    "dev": "electron .",
    "test": "webpack && electron .",
    "make": "electron-packager . --ignore='\\.gitignore|webpack*\\.js|node_modules|src' --overwrite --download.mirrorOptions.mirror=https://npm.taobao.org/mirrors/electron/"
  }
  • --ignore 忽略 .gitignore webpack*.js node_modules/ src/ 等文件。
  • --overwrite 如存在旧的软件包,直接覆盖。
  • --download 从淘宝 NPM 镜像下载。

./src/index.ejs 添加:

    <meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';">

用 webpack 打包,用 Electron 运行。

$ npm run build
$ tree -L 2 -I "node_modules"
.
|-- dist
|   |-- app.css
|   |-- app.js
|   |-- index.html
|   |-- vendors.js
|   `-- vendors.js.LICENSE.txt
|-- main.js
|-- package.json
|-- src
|   |-- app.css
|   |-- app.js
|   |-- app.vue
|   `-- index.ejs
|-- webpack.config.js
`-- webpack.dev.config.js

2 directories, 13 files

$ npm run dev

可以用 npm test 代替以上两个命令。

打包成 .exe 文件。

$ npm run make

> webpack-demo@1.0.0 make
> electron-packager . --ignore='\\.gitignore|webpack*\\.js|node_modules|src' --overwrite --download.mirrorOptions.mirror=https://npm.taobao.org/mirrors/electron/

Packaging app for platform win32 x64 using electron v11.3.0
Wrote new app to...
本作品采用《CC 协议》,转载必须注明作者和本文链接
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!