# webpack配置工程师之路

什么是webpack?

我的理解是:最早期的前端开发,什么工具都没有,如果B.html用到了A.html的一部分代码,需要从A.html复制几百行粘贴到B.html,非常恶心。

当前端开发越来越规范以后,模块化开发也成为了主流,A.html里面的那几百行就可以提取为模块供其他地方使用。

以vue.js开发举例,你写的vue文件需要解析成浏览器可以直接运行的js、css代码,这个解析的工具就可以是webpack。

webpack配置相当繁杂,所以有网友开玩笑自称是webpack配置工程师。

本笔记来自玩转Webpack专栏,这里是相关PDF (opens new window) ,国内请访问码云仓库 (opens new window)

安装webpack:

npm i webpack webpack-cli -D

webpack Github主页 (opens new window)

# 初识webpack:

配置文件

webpack 默认配置: webpack.config.js 可以通过webpack -- config指定配置文件

配置组成:

//webpack.config.js
module.exports = {
  // 1 打包的入口文件
  entry: "./src/index.js",
  // 2 打包的输出
  output: "./dist/main.js",
  /* 打包的输出也可以这样,需要 const path = require("path");
  output: {
    path: path.join(__dirname, "dist"),
    filename: "bundle.js",
  },
  */
  // 3 环境
  mode: "production",
  module: {
    rules: [
      // 4 Loader 配置
      { test: /\.txt$/, use: "raw-loader" },
    ],
  },
  plugins: [
    // 5 插件配置
    new HtmlwebpackPlugin({
      template: "./src/index.html",
    }),
  ],
};

通过npm run build运行:

{
  "name": "hello-world",
  "version": "1.0.0",
  "description": "Hello world",
  "main": "index.js",
  "scripts": {
    "build": "webpack"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

# 多入口配置

module.exports = {
  // 多入口配置
  entry: {
    app: "./src/app.js",
    search: "./src/search.js",
  },
  output: {
    filename: "[name].js",
    path: _dirname + "/dist",
  },
};

# Loaders

webpack默认只支持JS和JSON两种文件类型,通过Loaders去支持其它文件类型并且把它们转化成有效的模块,并且可以添加到依赖图中。

本身是一个函数,接受源文件作为参数,返回转换的结果。

常见的Loaders及作用:

名称 作用
babel-loader 转换ES6、ES7等JS新特性语法
css-loader 支持.css文件的加载和解析
less-loader 将less文件转换成css
ts-loader 将TS转换成JS
file-loader 进行图片、字体等的打包
raw-loader 将文件以字符串的形式导入
thread-loader 多进程打包JS和CSS

Loaders的用法

const path = require("path");
module.exports = {
  output: {
    filename: "bundle.js",
  },
  module: {
    rules: [
      {
        //test指定匹配规则
        test: /.txt$/,
        //use指定使用的loader名称
        use: "raw-loader",
      },
    ],
  },
};

# Plugin

常见的Plugins及功能:

名称 描述
CommonsChunkPlugin 将chunks相同的模块代码提取成公共js
CleanWebpackPlugin 清理构建目录
ExtractTextWebpackPlugin 将CSS从bunlde文件里提取成一个独立的CSS文件
CopyWebpackPlugin 将文件或者文件夹拷贝到构建的输出目录
HtmlWebpackPlugin 创建html文件去承载输出的bundle
UglifyjsWebpackPlugin 压缩JS
ZipWebpackPlugin 将打包出的资源生成一个zip包

# Mode的内置函数功能

Mode的内置函数功能

选项 描述
deve lopment 设置process.env.NODE_ENV 的值为development。
开启NamedChunksPlugin F NamedModulesPlugin
production 设置 process.env.NODE_ENV 的值为production。
开启FlagDependencyUsagePlugin , FlagIncludedChunksPlugin,ModuleConcatenationPlugin,NoEmitOnErrorsPlugin,Occurrence0rderPlugin , SideEffectsFlagPlugin和TerserPlugin
none 不开启任何优化选项

# 解析ES6

使用babel-loader

babel的配置文件是: .babelrc

需要安装的依赖是:

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

示例:

const path = require("path");
module.exports = {
  entry: "./src/index.js",
  output: {
    path: path.join(__dirname, "dist"),
    filename: "bundle.js",
  },
  module: {
    rules: [
      {
        //使用babel-loader解析ES语法的JS
        test: /.js$/,
        use: "babel-loader",
      },
    ],
  },
};

资源解析:增加ES6的babel preset配置

{
  presets: ["@babel/preset-env"],
  plugins: ["@babel/proposal-class-properties"],
};

# 解析CSS

css- -loader用于加载.css 文件,并且转换成commonjs对象 style- loader将样式通过<style>标签插入到head中

const path = require("path");
module.exports = {
  entry: "./src/index.js",
  output: {
    filename: "bundle.js",
    path: path.resolve(__dirname, "dist"),
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ["style-loader", "css-loader"],
      },
    ],
  },
};

webpack中的文件监听

文件监听是在发现源码发生变化时,自动重新构建出新的输出文件。

webpack开启监听模式,有两种方式:

  • 启动webpack命令时,带上--watch参数

  • 在配置webpack.config.js中设置watch:true

文件监听的原理分析 轮询判断文件的最后编辑时间是否变化 某个文件发生了变化,并不会立刻告诉监听者,而是先缓存起来,等 aggregate Timeout

module.export = {
  //默认false,也就是不开启
  watch: true,
  //只有开启监听模式时,watchOptions才有意义
  wathcOptions: {
    //默认为空,不监听的文件或者文件夹,支持正则匹配
    ignored: /node_ modules/,
    //监听到变化发生后会等300ms再去执行,默认300ms
    aggregateTimeout: 300,
    //判断文件是否发生变化是通过不停询问系统指定文件有没有变化实现的,默认每秒问1000次
    poll: 1000,
  },
};

# 热更新的原理分析

Webpack Compile:将JS编译成Bundle

HMR Server:将热更新的文件输出给HMR Rumtime

Bundle server:提供文件在浏览器的访问

HMR Rumtime:会被注入到浏览器,更新文件的变化

bundle.js:构建输出的文件

图:

# CSS的文件指纹设置

设置 MiniCssExtractPlugin 的 filename, 使用 [contenthash]

module.exports = {
  entry: {
    app: "./src/app.js",
    search: "./src/search.js",
  },
  output: {
    filename: "[name]_[chunkhash:8].js",
    path: _dirname + "/dist",
  },
  plugins: [
    // 保证输出的CSS唯一性
    new MiniCssExtractPlugin({
      filename: "[name]_[chunkhash:8].css",
    }),
  ],
};

# JS文件的压缩

内置了 uglifyjs-webpack-plugin

# CSS 文件的压缩

使用 optimize-css-assets-webpack-plugin 同时使用 cssnano

module.exports = {
  entry: {
    app: "./src/app.js",
    search: "./src/search.js",
  },
  output: {
    filename: "[name][chunkhash:8].js",
    path: dirname + "/dist",
  },
  plugins: [
    new OptimizeCSSAssetsPlugin({
      assetNameRegExp: /\.css$/g,
      cssProcessor: require("cssnano"),
    }),
  ],
};

# html 文件的压缩

修改html-webpack-plugin,设置压缩参数

module.exports = {
  entry: {
    app: "./src/app.js",
    search: "./src/search.is",
  },
  output: {
    filename: "name]chunkhash:8].js",
    path: __dirname + "/dist",
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: path.join(__dirname, "src/search.html"),
      filename: "search.html",
      chunks: ["search"],
      inject: true,
      minify: {
        html5: true,
        collapseWhitespace: true,
        preserveLineBreaks: false,
        minifyCSS: true,
        minifyJS: true,
        removeComments: false,
      },
    }),
  ],
};

# PostCss 插件 autoprefixer 自动补齐 CSS3 前缀

使用 autoprefixer 插件 根据 Can I Use 规则( https://caniuse.com/)

module.exports = {
  module: {
    rules: [
      {
        // 使用 autoprefixer 插件
        test: /\.less$/,
        use: [
          "style-loader",
          "css-loader",
          "less-loader",
          {
            loader: "postcss-loader",
            options: {
              plugins: () => [
                require("autoprefixer")({
                  browsers: ["last 2 version", "> 1%", "iOS 7"],
                }),
              ],
            },
          },
        ],
      },
    ],
  },
};

# CSS 媒体查询实现响应式布局

缺陷:需要写多套适配样式代码

@media screen and (max-width: 980px) {
  header {
    width: 900px;
  }
}
@media screen and (max-width: 480px) {
  header {
    width: 400px;
  }
}
@media screen and (max-width: 350px) {
  header {
    width: 300px;
  }
}

# 参考链接

https://github.com/cpselvis/geektime-webpack-course

# 3到4迁移指南:

inline和hot的区别 (opens new window)

// 1. 不会刷新浏览器
$ webpack-dev-server
//2. 刷新浏览器
$ webpack-dev-server --inline
//3. 重新加载改变的部分,不会刷新页面
$ webpack-dev-server --hot
//4. 重新加载改变的部分,HRM失败则刷新页面
$ webpack-dev-server  --inline --hot

https://webpack.js.org/migrate/4/

https://v4.webpack.js.org/loaders/

https://github.com/webpack/webpack-dev-server/blob/master/migration-v4.md

Sass-loader和less-loader首次支持webpack5的版本: https://stackoverflow.com/a/66111894/4493393

style-loader v3 also dropped support for Webpack 4.

npm install vue-loader@14.2.2

"less": "^4.1.2", 不行

Cannot find module 'webpack/bin/config-yargs'

For webpack-cli 3.x:

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

For webpack-cli 4.x:

"scripts": {   "start:dev": "webpack serve" }

[webpack-cli] Error: Unknown option '--inline'

工程目录/node_modules/html-webpack-plugin/lib/compiler.js:81 var outputName = compilation.mainTemplate.applyPluginsWaterfall('asset-path', outputOptions.filename, {

TypeError: compilation.mainTemplate.applyPluginsWaterfall is not a function

npm i html-webpack-plugin@3

https://stackoverflow.com/a/52020551/4493393

上次更新: 3/16/2022, 2:29:37 PM