Webpack

Webpack的基础用法

Webpack5针对不同的模块打包的学习总结

Yixuan Lang
2021-08-29
8 min

# 🍲 Webpack 学习记录(不同类型模块的打包)

上篇文章主要了解一下什么是 webpack,以及 webpack 的核心概念,并对其基本配置也有了一定的了解。该篇文章主要总结了 webpack 针对不同类型模块的打包。

在 webpack 中,webpack 本身做的事情仅仅只是分析出各个模块之间的依赖关系,然后形成资源列表,最后打包到指定的文件中。在 webpack 看来,任何的文件都是模块,但是 webpack 只支持对 js 和 json 文件打包。像是 css、sass、png 等这些类型的文件,webpack 需要借助对应的 loader 对其进行解析,同时 plugin 插件也可以为 loader 带来更多的特性。下面我们来看一下 webpack 是如何对各类模块进行打包的把~👇

webpack打包

# 一、打包 CSS


# 1. Webpack 打包 CSS

webpack 默认只会处理 JS 文件,这时可以使用相应的 loader 来处理 CSS 文件,这里打包 CSS 需要借助两个 loader 分别是:

  • css-loader:将 CSS 文件转换成一个 JS 模块(将 CSS 输出到打包后的 JS 文件中)
  • style-loader:把包含 CSS 内容的 JS 代码,挂载到页面的 style 标签中

# 安装

npm i css-loader styly-loader -D

# 配置

// webpack.config.js
module.exports = {
  ...
  entry: './src/main.js',
  ...
  module: {
    rules: [
      {
        test: /.css$/, // 匹配以css结尾的文件
        use: [
          'style-loader',  // 将JS中的样式,挂载到style标签中
          'css-loader'  // css-loader按照Common JS规范,讲样式文件输出到JS中
        ]
      }
    ]
  }
}

注意:loader 的执行顺序是先下后上,就是 use 数组会逆序执行 loader 们

# 2. Webpack 打包 less

处理 less 文件时,需要先借助 less-loader 将 less 转换成 css,scss 等预编译语言也是一样的逻辑

npm i less less-loader -D
module: {
  rules: [
    {
      test: /\.less$/,
      use: [
        "style-loader",
        "css-loader",
        "less-loader", // 将less先转换成css
      ],
    },
  ];
}

这里需要注意在 use 中插入 loader 的顺序,必须先让 less-loader 执行,将其解析成 css 后再进行后续的操作

image-20211014225418343

# 3. 将 CSS 打包成独立的文件

当处理完 css 和 less 时,不希望样式通过 style 的方式引入到 html 文件中,而是希望其抽离成一个单独的样式文件。这时需要借助mini-css-extract-plugin插件来完成,该插件可以替换style-loader

npm i mini-css-extract-plugin -D
// webpack.consig.js
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module:{
  rules:[
    {
      test: /\.css$/,
      use:[
        MiniCssExtractPlugin.loader, //替换style-loader,将css打包成独立的文件
        'css-loader'      // 加载处理css文件
      ]
    }
  ]
},
plugins:[
  // 实例化插件
  new MiniCssExtractPlugin({
     filename:'index.css'  //重命名输出的css文件,也可不写默认
  })
]

# 4. 处理 CSS 兼容性问题

CSS 在不同浏览器上的兼容性不一致,对于一些比较新的 CSS 属性需要针对不同的浏览器添加相应的样式前缀。在 webpack 中可以使用postcss-loader中的autoprefixer插件来帮助我们完成这个工作。

【扩展】什么是 PostCSS:

  • PostCSS 是一个通过 JavaScript 来转换样式的工具
  • 这个工具可以帮助我们进行一些 CSS 的转换和适配,比如自动添加浏览器前缀、css 样式的重置
npm i postcss-loader autoprefixer -D

之后可以在webpack.config.js文件中引入 postcss-loader

module:{
  rules:[
    {
      test: /\.css$/,
      use:[
        // 第3步:将css打包成独立的文件
        MiniCssExtractPlugin.loader,
        // 第2步:将css输出到打包后的js中
        'css-loader',
        // 第1步:通过postcss-loader给样式属性添加浏览器前缀
        'postcss-loader',
      ]
    }
  ]
},

因为我们只是想要使用postcss-loader中的autoprefixer插件来帮助我们添加浏览器前缀,所以可以新建一个postcss.config.js文件来引入这个插件(基于 Common.js 规范)

module.exports = {
  plugins: [require("autoprefixer")],
};

对于上述的配置可以指定需要兼容的浏览器,可以直接在 package.json 文件中直接指定 browserslist,也可以在根目录下新建一个.browserslistrc 文件,这里我直接在 package.json 文件中指定了

"browserslist": [
  "last 1 version",  // 匹配到浏览器的最后一个版本
  "> 1%"  // 代表全球超过1%使用的浏览器
]

我们还可以使用另一个插件postcss-preset-env来帮助我们完成上面的工作,不需要借助 autoprefixer

postcss-preset-env: 可以帮助我们将一些现代的 CSS 特性,转成大多数浏览器认识的 CSS,并且会根据目标浏览器或者运行时环境添加所需的 polyfill; 也包括会自动帮助我们添加 autoprefixer(所以相当于已经内置了 autoprefixer),比 autoprefixer 插件的功能更加的强大。

npm i postcss-preset-env -D

# 5. 校验 CSS 代码格式

在 webpack 中对 CSS 代码进行格式校验的时候,可以引入三个包来帮助我们进行校验

  • stylelint:对 CSS 代码格式进行校验 👉 https://stylelint.io/
  • stylelint-config-standard:进行 CSS 代码校验的规则集(一些大公司遵循的代码规范) 👉 https://github.com/stylelint/stylelint-config-standard
  • stylelint-webpack-plugin:允许在 webpack 中使用 stylelint 对 CSS 代码进行格式校验
npm i stylelint stylelint-config-standard stylelint-webpack-plugin -D

在 webpack.config.js 中引入 stylelint-webpack-plugin 插件

// 引入校验css代码格式的插件
const StylelintPlugin = require("stylelint-webpack-plugin");
module.exports = {
  // 配置插件
  plugins: [
    new StylelintPlugin({
      // 指定需要进行格式校验的文件
      files: ["./src/css/*.{css, less, sass, scss}"],
    }),
  ],
};

在 package.json 文件中配置 stylelint-config-standard

"stylelint": {
   "extends": "stylelint-config-standard",
    // 可以指定rules属性来对规则集进行扩展
    "rules": {}
 }

指定规则的配置有三种方式,按照加载的先后顺序,依次是:

  • 在 package.json 中的 stylelint 属性指定规则
  • 在.stylelintrc 中指定规则
  • 在 stylelint.config.js 中指定规则

# 6. 压缩 CSS

当我们希望对打包后的 CSS 进行压缩可以使用 optimize-css-assets-webpack-plugin -D 插件

npm i optimize-css-assets-webpack-plugin -D

之后在 webpack.config.js 中的中引入并且进行初始化后,打包出来的 CSS 就是压缩版的啦

// 引入压缩CSS的插件
const OptimizeCssAssetsPlugin = require("optimize-css-assets-webpack-plugin");
module.exports = {
  // 配置插件
  plugins: [new OptimizeCssAssetsPlugin()],
};

# 二、打包 HTML


打包完成后我们会手动创建一个 html 文件引入打包后的资源来查看效果,这时可以使用html-webpack-plugin插件来帮助我们简化 HTML 文件的创建,以便为你的 webpack 包提供服务。

html-webpack-plugin:

  • 自动生成一个 HTML 文件(用于服务器访问),并在 HTML 中加载所有的打包资源
  • 指定 HTML 模板、设置 HTML 变量、压缩 HTML
npm i html-webpack-plugin -D

下载后在 webpack.config.js 中引用该插件,在实例化插件的时候,可以指定打包出来的 HTML 文件名称、指定引用的 HTML 模板以及定义 HTML 中使用的变量等操作

const HtmlWebpackConfig = require('html-webpack-plugin')
module.exports = {
  // 配置插件
  plugins: [
  	// 配置html
    new HtmlWebpackConfig({
      // 指定打包出来的html文件名称
      filename: 'index.html',
      // 指定生成的HTML模板
      template: './src/index.html',
      // 指定HTML中使用的变量
      title: '首页'
  ]
}

详细配置 👉 https://webpack.docschina.org/plugins/html-webpack-plugin/#configuration

# 三、打包 JavaScript


# 1. Webpack 编译 JS

虽然 webpack 可以直接对 JS 进行打包,但是当我们使用一些 ES6+的新特性时,并不能对其进行转译。如果需要将 ES6+转换成 ES5,从而保证 JS 在低版本的浏览器上也可以兼容。这时可以借助babel-loader来帮助我们将 ES6+的语法转换成 ES5。为了使对 JS 的转译更加的灵活与强大还可以引入@babel/core@babel/preset-env

  • @babel/core:包含了一些 JS 转译的核心插件,其作用是是将 JS 代码分析成 ast,方便各个插件分析语法进行相应的处理。

image-20211015150744026

  • @babel/preset-env:主要功能有两个:(1) 将尚未被大部分浏览器支援的 JavaScript 语法转换成能被浏览器支援的语法。 (2) 让较旧的浏览器也能支持大部分浏览器能支持的语法 👉 详细讲解
npm i babel-loader @babel/core @babel/preset-env -D

babel-loader 详细使用

module: {
  rules: [
    {
      test: /\.m?js$/,
      exclude: /node_modules/,
      use: {
        loader: "babel-loader",
        options: {
          presets: [["@babel/preset-env", { targets: "defaults" }]],
        },
      },
    },
  ];
}

经过上面的配置已经可以对 ES6+的大部分语法进行转译了,但是@babel/preset-env只能够转译基本的语法,比如像是 promise 这个新语法就没有办法进行转译。这时可以借助@babel/polyfill包,这个包可以帮助我们转译所有的 JS 新语法,但是会导致打包出来的文件大了许多,可以配合core-js(按需转译新语法)一起使用

# 2. 校验 JS 的代码格式

JS 和 CSS 一样,在开发过程中也都需要进行代码格式的校验。一般会引入下面这几个包对 JS 的代码格式进行校验

  • eslint:校验 JS 代码格式的工具 👉 https://eslint.org/
  • eslint-config-airbnb-base:最流行的 JS 代码格式规范 👉 https://www.npmjs.com/package/eslint-config-airbnb-base
  • eslint-webpack-plugin:webpack 的 eslint 插件 👉 https://webpack.docschina.org/plugins/eslint-webpack-plugin/#root
  • eslint-plugin-import:用于在 package.json 中读取
npm i eslint eslint-config-airbnb-base eslint-webpack-plugin eslint-plugin-import -D

在 webpack.config.js 中引入并实例化eslint-webpack-plugin

// js代码格式校验
const ESlintPlugin = require("eslint-webpack-plugin");
module.expoers = {
  plugins: [
    // js代码格式校验配置
    new ESlintPlugin({
      // 自动解决一些常规的代码格式错误
      fix: true,
    }),
  ],
};

JS 代码的校验规则基于之前下载的eslint-config-airbnb-base,可以在 package.json 文件中对其进行扩展

"eslintConfig": {
  "extends": "airbnb-base"
}

# 四、打包图片资源


大多数的资源加载器都类似 css-loader,都是将资源模块转化为 js 代码的实现方式去工作。但是还有一些我们经常用到的资源文件,例如项目当中的图片、字体,这些文件没有办法通过 js 的方式表示。对于这一类的资源文件,我们需要用到文件资源加载器,也就是 file-loader。

  1. file-loader:其作用就是帮助我们处理 import/require()方式引入的一个文件资源,并且会将它放到我们输出的文件夹中。
npm i file-loader -D

默认打包出来的文件名称是一个随机的哈希值,比较难与我们原本的图片文件对应起来。可以在file-laoder的配置项中指定打包后的图片名称,以及设置输出的文件夹

// webpack.config.js
module: {
  rules: [
    {
      test: /\.(png|jpe?g|gif|svg)/,
      use: {
        loader: "file-loader",
        options: {
          name: "[name].[hash:8].[ext]", // [name]图片原来的名称 | [ext]图片原来的后缀名
          outputPath: "img", // 打包输出的文件夹
        },
      },
    },
  ];
}

除了file-loader还可以使用url-loader来帮助我们打包图片类型的资源。

  1. url-loader:相当于file-loader的升级版本,我们可以指定图片大小的限制,小于该限制的图片不会被单独单包,而是转换成 base64 字符串打包在 JS 文件中。这样图片就会和 JS 文件一起加载,减少了图片的请求次数
npm i url-loader -D

在 webpack 中引入该 loader,并配置其图片大小的限制

module: {
  rules: [
    {
      test: /\.(png|gif|jpe?g)$/i,
      use: {
        loader: "url-loader",
        options: {
          // 指定图片大小的限制,小于该限制的图片,会被编译成base64格式
          limit: 10 * 1024, // 10kb
          name: "img/[name].[ext]",
        },
      },
    },
  ];
}

【问题优化】每次引入图片的时候,需要在入口文件中以模块的方式引入之后,再挂载到 dom 节点上。有没有什么更好的方式让图片直接在 html 模板中以标签的模式直接引入,之后打包后也可以有正确的路径显示图片

【问题解决】可以使用html-loader

  1. html-loader:将 HTML 导出成字符串,负责引入 img,从而能被 url-loader 进行处理
npm i html-loader -D
module: {
  rules: [
    {
      test: /\.(htm|html)$/i,
      use: {
        // html-loader会采用url-loader加载图片,但是url-loader和html-loader使用的默认加载规范不一样
        loader: "html-loader",
        options: {
          // 如果webapck4:只需要在url-loader配置中配置esModule为false
          // 如果webpack5: 在url-loader和html-loader中都需要进行配置
          esModule: false,
        },
      },
    },
  ];
}

# 五、资源模块(Asset Module)


在 webpack5 之前,加载资源文件(字体、图片、图标、HTML...)需要使用一些 loader,比如 raw-loader 、url-loader、file-loader。 在 webpack5 开始,我们可以直接使用资源模块类型(asset module type),来替代上面的这些 loader,而无需配置额外的 loader

资源模块类型(asset module type),通过添加 4 种新的模块类型,来替换所有这些 loader

  • Webpack4:
    • raw-loader:将文件导入为字符串
    • file-loader:将文件发送到输出目录
    • url-loader:将文件发送到输出目录,或转为 Data URL 内联到 bundle 中
  • Webpack5:
    • asset/resource:发送一个单独的文件并导出 URL(相当于之前的 file-loader)
    • asset/inline:导出一个资源的 data URL(之前通过使用 url-laoder 实现)
    • asset/source:导出资源的源代码(之前通过使用 raw-loader 实现)
    • asset:再导出一个 data URL 和发送一个单独的文件之间自动选择(之前通过使用 url-loader,并且配置资源体 积限制实现)

举个 🌰, 以 webpack4 和 webpack5 分别处理字体资源文件举例

// webpack4中处理字体资源文件
module: {
  rules: [
    {
      test: /\.(eot|svg|ttf|woff|woff2)$/i,
      use: {
        loader: "file-loader",
        options: {
          name: "fonts/[name].[ext]",
        },
      },
    },
  ];
}
// webpack5中处理字体资源文件
module: {
  rules: [
    {
      test: /\.(eot|svg|ttf|woff|woff2)$/i,
      use: {
        loader: "file-loader",
        options: {
          name: "fonts/[name].[ext]",
        },
      },
    },
  ];
}