Webpack5 基础使用笔记

[webpack中文文档](概念 | webpack 中文文档 | webpack中文文档 | webpack中文网 (webpackjs.com)):

本质上,webpack 是一个用于现代 JavaScript 应用程序的 静态模块打包工具。当 webpack 处理应用程序时,它会在内部从一个或多个入口点构建一个 依赖图(dependency graph),然后将你项目中所需的每一个模块组合成一个或多个 bundles,它们均为静态资源,用于展示你的内容。

这篇笔记主要记录的是Webpack 5

基本使用

安装

安装webpack的同时需要安装webpack-cli。

npm i webpack webpack-cli -D

启动

启动分为开发模式和生产模式。

  • 开发模式
npx webpack /path/to/main.js --mode=development
  • 生产模式
npx webpack /path/to/main.js --mode=production

其中:

  • npx webpack:用于运行本地安装的webpack;
  • /path/to/main.js:指定webpack从main.js开始打包,会同时打包其依赖文件;
  • --mode=xxx:指定模式。

如果终端输出webpack is not recognized as an internal or external command,即无法被识别为指令。

那么可以考虑将webpack进行全局安装:

npm i -g webpack webpack-cli

输出

webpack会默认将文件打包输出到dist文件夹,并且只能处理js资源。

如果需要处理其它资源文件,或者需要更精细化地打包,则需要设置webpack的配置文件:webpack.config.js


Webpack的配置

webpack的配置文件命名为webpack.config.js,置于项目文件的根路径下。

webpack是基于node运行的,所以webpack.config.js采用CommonJS模块化规范。

webpack的配置有五大核心概念:

  1. entry(入口):webpack从这个文件开始进行打包,并打包其依赖文件;
  2. output(输出):决定了webpack打包后的文件的文件名、地址等;
  3. loader(加载器):webpack只能处理js文件,其它文件比如css文件、图片资源、jsx文件,需要使用loader才能解析;
  4. plugins(插件):扩展webpack的功能,每一个插件如何配置取决于插件的提供者如何设计;
  5. mode(模式):主要有两种模式,开发模式(development)和生产模式(production)。

简单的配置文件样例

// Node.js的核心模块,专门用来处理文件路径
const path = require("path");

module.exports = {
  // 入口
  // 相对路径和绝对路径都行
  entry: "./src/main.js",
  // 输出
  output: {
    // path: 文件输出目录,必须是绝对路径
    // path.resolve()方法返回一个绝对路径
    // __dirname 当前文件的文件夹绝对路径
    path: path.resolve(__dirname, "dist"),
    // filename: 输出文件名
    filename: "main.js",
  },
  // 加载器
  module: {
    rules: [],
  },
  // 插件
  plugins: [],
  // 模式
  mode: "development", // 开发模式
};

在上面基本使用那一栏里,由于没有配置文件,运行webpack的时候通常会带上后缀:npx webpack /path/to/main.js --mode=development,用于指定入口文件、模式等信息。

添加配置文件之后,webpack会自动读取webpack.config.js这个文件里的配置信息,因此可以直接运行:npx webpack

开发模式介绍

通常会创建两个配置文件,一个对应开发模式,另一个对应生产模式。

开发模式的webpack主要需要完成以下任务:

  1. 编译代码,使浏览器能识别运行

    webpack只能处理js文件,而其它文件,例如:css文件、字体图标、图片资源、html资源等,webpack需要通过配置loader才能处理这些资源;

  2. 代码质量检查

    使用例如eslint之类的代码检查工具,提前发现代码缺陷,也可以检查代码书写是否规范,统一团队编码风格。

处理样式资源

需要使用css-loaderstyle-loader才能处理css文件。

  • css-loader:负责将CSS文件编译成webpack可以识别的模块。
  • style-loader:会动态地创建一个style标签,用于放置webpack 中的css模块。

先安装:

npm i css-loader style-loader -D

配置webpack:

const path = require("path");

module.exports = {
  entry: "./src/main.js",
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "main.js",
  },
  module: {
    rules: [
      {
        // 用来匹配 .css 结尾的文件
        test: /\.css$/,
        // use 数组里面 Loader 执行顺序是从右到左
        use: ["style-loader", "css-loader"],
      },
    ],
  },
  plugins: [],
  mode: "development",
};

这里需要注意的是:use字段指定需要应用的loader,顺序是从右到左的,即css文件先经由css-loader变成webpack可以识别的模块,然后再由style-loader将这些模块注入到html里。

这里提供案例的相关代码:

  • /src/assets/css/style.css
#app{
    text-align: center;
    font-weight: bold;
}
  • /src/js/show.js
export function show(dom, msg){
	dom.innerText = msg;
}
  • /src/main.js
import {show} from './js/show'
import './assets/css/style.css'

const app = document.getElementById('app');
show(app, 'Hello Webpack');
  • /public/index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <title>webpack-learning</title>
</head>
<body>
    <div id="app"></div>
    <script src="../dist/main.js"></script>
</body>
</html>

同时,可以在package.json中设置常用的指令:

{
    ...
    "script":{
      	"start": "webpack --config webpack.config.js"  
    },
    ...
}

然后,只需要在终端运行:

npm run start

就会以webpack.config.js为配置,运行webpack,将main.js及其依赖文件,打包到/dist/main.js

此时,打开/public/index.html,可以看到依赖的js文件show函数的功能正常,且引入的style.css样式也被打包并注入到style标签中:

常用的样式资源还有Less,Sass等,这里再介绍Less样式表的处理,其它样式资源的处理方式是类似的。

首先安装相应的loader:

npm i -D less-loader

配置webpack:在webpack.config.js文件原有的基础上进行修改。

...
module.exports = {
    ...
    module: {
        rules: [
            ...
            {
                // 用来匹配 .less 结尾的文件
                test: /\.less$/,
                use: ['style-loader', 'css-loader', 'less-loader']
            }
        ]
    },
    ...
}

上述代码中的...表示原有的,但是这里为了突出重点而省略的代码片段。

配置less-loader只需要在rules数组中添加新的一项,注意use的顺序是从右到左,先把less代码转换成css代码,然后依此类推。

这里我只是在上述css的代码中做了一些修改:

  1. 新增/src/assets/less/style.less文件
#app{
    text-align: center;
    font-weight: bold;
}
  1. /src/main.js中不引入css文件,而是引入less文件
import {show} from './js/show'
import './assets/less/style.less'

const app = document.getElementById('app');
show(app, 'Hello Webpack');
  1. 执行npm run start

  2. 打开/pubcli/index.html,可以观察到less也能被正确地处理并注入到最终的style标签中。

这里再浅浅地提一嘴关于Sass/Scss的样式资源处理配置,他们的loader都是使用sass-loader,因此,在配置test的时候,正则表达式可以使用:/\.s[ac]ss$/去同时匹配这两种文件后缀。

处理图片资源

过去的Webpack4 ,处理图片资源需要使用file-loaderurl-loader

现在Webpack5已经将两个Loader功能内置到Webpack里了,只需要简单配置即可处理图片资源。

  • webpack.config.js
...
module.exports = {
  ...
  module: {
    rules: [
      ...
      {
        test: /\.(png|jpe?g|gif|webp)$/,
        type: "asset",
      },
    ],
  },
  ...
};

相关代码:

  1. 在代码中引入图片(记得需要出现在入口文件的依赖中,否则不会被webpack处理)
body{
    background-image: url('../images/normal.png');
}
#app{
    text-align: center;
    font-weight: bold;
    background-image: url('../images/small.png');
}

图片可以随便照几张测试,这里我习惯性地将图片放在/src/assets/images目录下。

  1. 运行webpack之后,可以发现图片也被输出了。

上面样式资源打包后没有相应的样式资源出现是因为:经过style-loader的处理,样式资源被打包进了/dist/main.js

对图片资源进行优化:将小于某个大小的图片转换成Base64格式。

...
module.exports = {
  ...
  module: {
    rules: [
      ...
      {
        test: /\.(png|jpe?g|gif|webp)$/,
        type: "asset",
        parser: {
            dataUrlCondition: {
                maxSize: 10 * 1024 // 小于10kb的图片会被base64处理
            }
        }
      },
    ],
  },
  ...
};

这里我用于测试的两张图片:normal.png和small.png,其中normal.png大于10kb,而small.png小于10kb。

再次打包并运行:

可以看到小于10kb的图片文件被转换成Base64格式。

  • 优点:减少请求数量
  • 缺点:main.js体积变得更大

修改输出资源的名称和路径

module.exports = {
  ...
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "static/js/main.js", // 将 js 文件输出到 static/js 目录中
  },
  module: {
    rules: [
      ...
      {
        test: /\.(png|jpe?g|gif|webp)$/,
        type: "asset",
        parser: {
          dataUrlCondition: {
            maxSize: 10 * 1024, // 小于10kb的图片会被base64处理
          },
        },
        generator: {
          // 将图片文件输出到 static/imgs 目录中
          // 将图片文件命名 [hash:8][ext][query]
          // [hash:8]: hash值取8位
          // [ext]: 使用之前的文件扩展名
          // [query]: 添加之前的query参数
          filename: "static/imgs/[hash:8][ext][query]",
        },
      },
    ],
  },
  ...
};

上述配置在原来的基础上,修改了:

  • main.js的输出目录;
  • 图片文件的输出目录以及名称。

修改了main.js的输出路径之后,需要修改/public/index.html中的<script>标签的路径。

<script src="../dist/static/js/main.js"></script>

自动清空上次打包的资源

如果想要每次打包自动覆盖上一次打包生成的资源,使用clean: true

module.exports = {
  entry: "./src/main.js",
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "static/js/main.js",
    clean: true, // 自动将上次打包目录资源清空
  },
  module: {
    rules: [
      ...
    ],
  },
  ...
};

处理字体图标资源

字体图标可以到阿里巴巴矢量图标库下载。

下载到本地后解压并添加到项目中的文件夹,这里只需要以下文件:

  • src/fonts/iconfont.ttf
  • src/fonts/iconfont.woff
  • src/fonts/iconfont.woff2
  • src/css/iconfont.css

将文件分开放置需要修改iconfont.css中对于字体文件的引用路径:

@font-face {
  font-family: "iconfont"; /* Project id 4000108 */
  src: url('../fonts/iconfont.woff2?t=1695445577878') format('woff2'),
       url('../fonts/iconfont.woff?t=1695445577878') format('woff'),
       url('../fonts/iconfont.ttf?t=1695445577878') format('truetype');
}

然后在/public/index.html中使用字体图标:

<div id="app"></div>
<i class="iconfont icon-wode3"></i>
<i class="iconfont icon-search"></i>
<i class="iconfont icon-rollback"></i>
<script src="../dist/static/js/main.js"></script>

配置webpack:

...
module.exports = {
  ...
  module: {
    rules: [
      ...
      {
        test: /\.(ttf|woff2?)$/,
        type: "asset/resource",
        generator: {
          filename: "static/media/[hash:8][ext][query]",
        },
      },
    ],
  },
  ...
};

type: "asset/resource"type: "asset"的区别:

  1. type: "asset/resource" 相当于file-loader, 将文件转化成 Webpack 能识别的资源,其他不做处理;
  2. type: "asset" 相当于url-loader, 将文件转化成 Webpack 能识别的资源,同时小于某个大小的资源会处理成 data URI 形式。

处理音频视频资源

音频和视频资源和字体图标做同样的处理,使用type: "asset/resource"

...
module.exports = {
  ...
  module: {
    rules: [
      ...
      {
        test: /\.(ttf|woff2?|map4|map3|avi)$/,
        type: "asset/resource",
        generator: {
          filename: "static/media/[hash:8][ext][query]",
        },
      },
    ],
  },
  ...
};

处理JS资源

webpack只能编译JS中的ES模块化语法,其它更高级的功能需要配置才能使用。

常用的工具有BabelEslint

  • Babel用于将高版本的JS语法转换成ES5语法,是针对兼容性的;
  • Eslint用于检查代码格式。

Eslint

Eslint可以用来检测JS和jsx语法。

使用Eslint需要写配置文件,标注各种rules,用于声明哪些代码规则需要检查。

在安装Eslint之前,先了解一下配置的写法:

  • 新建配置文件.eslintrc.*:位于项目根目录
    • .eslintrc
    • .eslintrc.js
    • .eslintrc.json
    • 上面三种文件命名的区别在于文件类型不同,配置的格式不一样。
  • 直接在package.json中的eslintConfig进行配置。

基本配置

module.exports = {
  // 解析选项
  parserOptions: {},
  // 具体检查规则
  rules: {},
  // 继承其他规则
  extends: [],
};
  1. parserOptions:解析选项

    parserOptions: {
      ecmaVersion: 6, // ES 语法版本
      sourceType: "module", // ES 模块化
      ecmaFeatures: { // ES 其他特性
        jsx: true // 如果是 React 项目,就需要开启 jsx 语法
      }
    }
    
  2. rules具体规则

    • off0:关闭
    • warn1:警告
    • error2:报错
    rules: {
      semi: "error", // 禁止使用分号
      'array-callback-return': 'warn', // 强制数组方法的回调函数中有 return 语句,否则警告
      'default-case': [
        'warn', // 要求 switch 语句中有 default 分支,否则警告
        { commentPattern: '^no default$' } // 允许在最后注释 no default, 就不会有警告了
      ],
      eqeqeq: [
        'warn', // 强制使用 === 和 !==,否则警告
        'smart' // https://eslint.bootcss.com/docs/rules/eqeqeq#smart 除了少数情况下不会有警告
      ],
    }
    
  3. extends继承

    直接引入已有的规则,常见的规则:

    • Eslint官方的规则:eslint:recommended
    • Vue-cli官方的规则:plugin:vue/essential
    • React-cli官方的规则:react-app

    如果本地的rules和继承的规则出现相同的选项,则本地的规则覆盖继承的规则。

安装Eslint

npm i -D eslint-webpack-plugin eslint

定义配置文件.eslintrc.js

module.exports = {
  // 继承 Eslint 规则
  extends: ["eslint:recommended"],
  env: {
    node: true, // 启用node中全局变量
    browser: true, // 启用浏览器中全局变量
  },
  parserOptions: {
    ecmaVersion: 6,
    sourceType: "module",
  },
  rules: {
    "no-var": 2, // 不能使用 var 定义变量
  },
};

配置webpack

...
const ESLintWebpackPlugin = require("eslint-webpack-plugin");

module.exports = {
  ...
  plugins: [
    new ESLintWebpackPlugin({
      // 指定检查文件的根目录
      context: path.resolve(__dirname, "src"),
    }),
  ],
  ...
};

Babel

Babel的配置文件可以是下面的文件名之一:

  • babel.config.js
  • babel.config.json
  • .babelrc
  • .babelrc.js
  • .babelrc.json

或者可以直接在package.json里的babel项进行配置。

babel.config.js为例,基础配置为:

module.exports = {
  // 预设
  presets: [],
};

预设可以理解为是Babel插件,扩展Babel的功能。

  • @babel/preset-env: 一个智能预设,允许您使用最新的 JavaScript;
  • @babel/preset-react:一个用来编译 React jsx 语法的预设;
  • @babel/preset-typescript:一个用来编译 TypeScript 语法的预设。

安装

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

定义配置文件babel.config.js

module.exports = {
  presets: ["@babel/preset-env"],
};

webpack配置

...
module.exports = {
  ...
  module: {
    rules: [
      ...
      {
        test: /\.js$/,
        exclude: /node_modules/, // 排除node_modules代码不编译
        loader: "babel-loader",
      },
    ],
  },
  ...
};

处理Html资源

之前没有处理html文件,所做的只是处理JS文件和其它资源,然后手动打开public/index.html。(/public/index.html需要手动引入打包后的dist/static/js/main.js文件)

安装

npm i html-webpack-plugin -D

配置

...
const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
  ...
  plugins: [
    ...
    new HtmlWebpackPlugin({
      // 以 public/index.html 为模板创建文件
      // 新的html文件有两个特点:1. 内容和源文件一致 2. 自动引入打包生成的js等资源
      template: path.resolve(__dirname, "public/index.html"),
    }),
  ],
  ...
};

修改/public/index.html

我们的目标是使用html-webpack-plugin完成html文件的打包,现在/public/index.html不需要手动引入dist/static/js/main.js文件了,插件会自动完成这个任务,并把html文件也一并打包到/dist文件夹中。

打包并检查结果

npm run start

可以看到index.html也被打包了:

以下打包之后的/dist/index.html,可以看到被自动添加了<script>标签:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>webpack-learning</title>
<script defer src="static/js/main.js"></script></head>
<body>
    <div id="app"></div>
    <i class="iconfont icon-wode3"></i>
    <i class="iconfont icon-search"></i>
    <i class="iconfont icon-rollback"></i>
</body>
</html>

开发服务器与自动化

使用自动化代替原先的手动输入打包指令的操作。

安装依赖

npm i webpack-dev-server -D

配置:(webpack.config.js文件)

...
module.exports = {
  ...
  // 开发服务器
  devServer: {
    host: "localhost", // 启动服务器域名
    port: "3000", // 启动服务器端口号
    open: true, // 是否自动打开浏览器
  },
  ...
};

修改package.json脚本

"scripts": {
    "start": "webpack --config webpack.config.js",
    "dev": "webpack serve --config webpack.config.js"
},

新加一个dev指令,用于启动本地服务。

运行指令

npm run dev

启动服务之后,会自动打开浏览器(可能会被电脑的安全软件拦截,点击允许就行)。

默认启动热加载功能,此时修改源代码,会自动更新。

:使用开发服务器的时候,代码不会被打包到/dist文件夹,而是编译打包在内存中。

生产模式介绍

开发完成代码之后,需要将代码打包给后端用于部署。

打包的配置需要考虑优化问题,主要从两个方面考虑:

  1. 优化代码运行性能
  2. 优化代码打包速度

通常会使用两个配置文件来对应开发模式和生产模式,方便随时切换。

在项目根目录下创建文件夹:config

然后新建两个配置文件:

  • 开发模式配置文件:/config/webpack.dev.js
  • 生产模式配置文件:/config/webpack.prod.js

在上文已经写好的webpack.config.js文件的基础上,稍作修改,拆分出两个模式的配置文件。

配置文件迁移的时候,由于目录变更,配置文件中的路径字符串需要做出相应的修改。同时,mode字段需要区分developmentproduction

开发模式需要注意

  • 没有输出,不需要指定输出路径,也不需要清空输出结果。
  • 需要启动开发服务器。

生产模式需要注意

  • 需要指定输出路径。
  • 关闭开发服务器。

更新package.json里的脚本

"scripts": {
    "start": "npm run dev",
    "dev": "webpack serve --config ./config/webpack.dev.js",
    "build": "webpack --config ./config/webpack.prod.js"
},
  • 启动开发模式使用:npm run startnpm run dev
  • 启动生产模式使用:npm run build

CSS处理

将css打包成单独文件

上文中的CSS处理是使用style-loader,这样做的结果是:样式会被打包到js文件中,最终再由js生成style标签。

这样做可能会导致白屏现象,因为目前流行的前端框架vue和react主要做的都是单页面应用,需要加载一个很大的js文件来渲染页面,如果样式也由js文件来负责的话,在js解析加载完成之前,页面内容一片空白。这样的页面用户体验不好。

因此,更好的解决方案是将CSS打包成单独的.css文件,然后使用<link>引入。

安装相关的插件

npm i -D mini-css-extract-plugin

配置webpack.build.js

...
// 引入插件
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

module.exports = {
  ...
  module: {
    // css相关的rules,将style-loader替换成新插件的loader
    rules: [
      {
          // 用来匹配 .css 结尾的文件
          test: /\.css$/,
          use: [MiniCssExtractPlugin.loader, 'css-loader']
      },
        {
            // 用来匹配 .less 结尾的文件
            test: /\.less$/,
            use: [MiniCssExtractPlugin.loader, 'css-loader', 'less-loader']
        },
      ...
    ],
  },
  plugins: [
    ...
    // 提取css成单独文件
    new MiniCssExtractPlugin({
      // 定义输出文件名和目录
      filename: "static/css/main.css",
    }),
  ],
  mode: "production",
};

主要步骤:

  1. 引入插件;
  2. 修改rules中的loader;
  3. 新增plugins,并指定输出的文件名。

运行打包指令

npm run build

效果:

  1. 输出了/dist/static/css/main.css文件:

  2. 输出的/dist/index.html中出现了<link>标签,并引入了打包后的css文件。

css兼容性处理

css兼容性处理可以使用postcss,拥有许多插件,可以按需配置,可以与解决JS兼容性问题的Babel进行类比。

安装

需要安装:

  • postcss:postcss本身
  • postcss-loader:为了webpack接入的loader
  • postcss-preset-env:postcss官方提供的预设配置
npm i -D postcss postcss-loader postcss-preset-env

配置webpack.prod.js

只需要在css-loader之前新增一个postcss-loader就可以:

rules: [
    {
        // 用来匹配 .css 结尾的文件
        test: /\.css$/,
        use: [
            MiniCssExtractPlugin.loader, 
            'css-loader',
            // 在css-loader之前使用postcss-loader
            {
                loader: 'postcss-loader',
                options: {
                    postcssOptions: {
                        plugins: ['postcss-preset-env']
                    }
                }
            }
        ]
    },
    {
        // 用来匹配 .less 结尾的文件
        test: /\.less$/,
        use: [
            MiniCssExtractPlugin.loader,
            'css-loader', 
            {
                loader: 'postcss-loader',
                options: {
                    postcssOptions: {
                        plugins: ['postcss-preset-env']
                    }
                }
            },
            'less-loader',
        ]
    },
]

对于其它css扩展语法,例如less,可以先使用less-loader转成css,然后使用postcss-loader处理其兼容性,最后才是使用css-loader处理成webpack可以处理的模块,以及打包用的MiniCssExtractPlugin.loader

兼容性控制

package.json文件中可以写入browserslist这个属性,指定兼容性的考虑程度。

这个属性是共用的,与兼容性问题相关的许多插件都会自动读取这个属性。

常用案例:

{
    "browserslist": ["last 2 version", "> 1%", "not dead"]
}

css压缩

减少打包后的体积。

安装

npm i -D css-minimizer-webpack-plugin

配置

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

module.exports = {
    ...
    plugins: [
        ...
        // css压缩
        new CssMinimizerPlugin()
    ],
    mode: 'production'
}

html和js压缩

生产环境下,html和js是默认压缩的,即打包后的代码都是单行的。

总结

5个核心概念

  1. entry(入口):webpack从这个文件开始进行打包,并打包其依赖文件;
  2. output(输出):决定了webpack打包后的文件的文件名、地址等;
  3. loader(加载器):webpack只能处理js文件,其它文件比如css文件、图片资源、jsx文件,需要使用loader才能解析;
  4. plugins(插件):扩展webpack的功能,每一个插件如何配置取决于插件的提供者如何设计;
  5. mode(模式):主要有两种模式,开发模式(development)和生产模式(production)。

2种模式

使用两个独立的配置文件。

  • 开发模式:需要代码编译、语法检查、热加载。
  • 生产模式:需要考虑性能与兼容性。

参考资料

热门相关:你好,霍少大人   二手市场:拯救性爱派对   重生成偏执霍少的小仙女   异能特工:军火皇后   紫府仙缘