01 - webpack 基本使用

110 阅读10分钟

Webpack 是什么

        在上一篇文章中介绍了构建工具是如何产生构建工具前世今生,而 webpack 是当前使用人数最多,生态环境最好的构建工具。

        webpack 以一个或多个文件作为入口进行打包,最终将整个项目所有文件以一个或多个文件的形式进行输出,输出的是编译好的文件,可直接在浏览器中运行。

Webpack 基本使用

项目构建

        首先,我们介绍一下 webpack 的基本使用,在之后的文章中对相关模块进行详细的介绍。

        创建项目并下载依赖 webpack 及 webpack-cli。

mkdir webpack-demo

cd webpack-demo

npm install -y

npm install webpack webpack-cli -D

        这里需要说明一下 webpack 与 webpack-cli 的区别,两者概念并不相同。

  • webpack: 实际执行打包的工具,将多个文件(如JavaScript、CSS、图像等)打包成单(多)个文件。
  • webpack-cli: 与Webpack进行交互的命令行工具,它提供了一些命令,以便于在终端中运行Webpack.

        接着创建用于测试打包的代码:

截屏2023-02-25 17.21.46.png

        向 package.json 中的 script 添加 命令,用于执行 webpack 打包:

image.png

        终端中执行命令,可以看到 index.js 文件成功被打包,可以证明项目构建是成功的。

截屏2023-02-25 17.25.52.png

        你可能会有疑问,这打包后的文件和我直接写的代码有啥需求,我直接引入不就好了,为啥还要用webpack打包。别急,webpack 强大的功能将在后面马上介绍到。

webpack 基本概念

        由于我们使用的是 webpack5,所以并不需要做配置就可以将 index.js 文件进行打包,这是因为 webpack5 有了默认的配置。在实际项目中,往往是需要配置指定配置文件的,从而完成所需。

        我们在项目跟目录中创建 webpack.config.js 文件,webpack 打包时,会先读取该文件中的配置。

// webpack 是基于 node.js 运行的,所以采用的是 commonJS 规范

module.exports = {
  // 入口
  entry: '',
  // 出口
  output: {},
  // 模块
  module: {
    rules: []
  },
  // 插件
  plugins: [],
  // 模式
  mode: '',
}
  • entry: 指定 webpack 开始进行打包的入口文件
  • output: 指定 webpack 将打包后的文件输出位置以及文件命名等
  • loader: webpack 只能识别 js、json,对于其他资源,如 vue、react 等,都需要对应的 loader 进行转换,将其转换为 webpack 可识别等资源
  • plugin: 用于扩展 webpack 的功能
  • mode: 模式,开发模式、生产模式。

        根据上面的概念,我们可以设置 webpack 的配置文件如下:

const path = require('path');其实

module.exports = {
  // 入口
  entry: './src/index.js',
  // 出口
  output: {
    // 编译后文件的存放目录
    path: path.resolve(__dirname, 'dist'),
    // 编译后文件的明明
    filename: 'bundle.js'
  },
  // 模块
  module: {
    rules: []
  },
  // 插件
  plugins: [],
  // 模式
  mode: 'development',
}

处理样式资源

处理 css 资源

        webpack 作为静态资源打包文件,其是可以对 CSS 进行打包的,但是 webpack 并不认识css 资源,需要借助 loader 才能够进行解析。

截屏2023-02-25 18.17.48.png

        终端执行命令,进行打包。此时打包失败,同时 webpack 也告诉了我们,我们需要一个 loader 处理 css 的文件。

image.png

        处理 css 资源,需要借助 css-loader 以及 style-loader。同时,需要在 webpack 的配置文件中,指定 css 资源需要使用对应的 loader 处理。

npm install css-loader style-loader -D
// webpack.config.js
module.exports = {
  ...
  // 模块
  module: {
    rules: [
      {
        // 匹配到 css 文件
        test: /\.css$/,
        // 先使用 css-loader 处理 css 文件,然后将处理后的结果再使用 style-loader 处理
        use: ['style-loader', 'css-loader']
      }
    ]
  },
}

        此时,再次执行打包命令,可以看到打包成功。

image.png

        为了验证打包后,css 是否生效,我们在dist目录下创建一个 html 文件,并引入打包后的文件。

image.png

        可以看到样式成功渲染到网页中,需要注意的是 webpack中 loader 是从右向左进行执行的。对于上述的配置来说,先执行 css-loader,再执行 style-loader,不能将位置放反。

        我们简单介绍下,css-loader 以及 style-loader 是如何进行工作的。

  • css-loader:会将CSS文件中的样式代码进行解析和转换,并将每个样式声明转换成一个JavaScript对象,如下所示:
{
  color: 'red',
  fontSize: '20px',
  // ...
}
  • style-loader: 将css-loader输出的JavaScript对象转换成一个style标签,并将其插入到HTML文档中。

image.png

处理 less 资源

        为了提升开发效率,css 预处理 less、sass 以及后处理器 postcss 在项目中被大规模应用。对于 less、sass 等资源,同样需要借助对应的 loader 才能进行解析后,才能被 webpack 处理。这里我们演示一下webpack 是如何进行 less 资源的处理,处理 less 资源使用 lessless-loader 依赖。

npm install less less-loader -D 

截屏2023-02-25 18.45.25.png

        这里我们猜测一下,less 资源的loader 应该怎么写?

        首先,需要将 less 资源转换为 css 资源,然后使用 css-loader 将 css 样式转换为 js 对象,最后再使用 style-loader 将对应的 js 对象转换为 style 标签并插入 html 中。 webpack 中 loader 的执行顺序是从右向左执行,所以对应的配置如下:


module.exports = {
  ...
  // 模块
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      },
      {
        test: /\.less$/,
        use: ['style-loader', 'css-loader', 'less-loader']
      }
    ]
  },
}

image.png

        你可能会有这样的疑问,为啥要安装 less-loader 和 less,只安装 less-loader 不可以嘛?这里,我们简单介绍一下 less-loader 的工作原理。

  1. 读取 Less 文件内容。

  2. 将 Less 代码编译为 CSS 代码。这一步需要使用 less 库进行编译(这也就是我们安装 less 库的原因)。

  3. 将编译后的 CSS 代码输出到 Webpack 中。

  4. Webpack 将 CSS 代码处理为 CSS 模块,并将其添加到打包后的输出文件中。

        在如上的内容中,我们介绍了如何使用 webpack 处理 css 以及 less 资源,如果我们想要处理 sass、postcss 的样式资源,采用同样的原理,借助对应的 loader 进行处理,这里不做过多的赘述了。

处理图片资源

        处理图片资源的步骤同处理 css 资源,即借助对应的 loader 解析。在 webpack4 中,处理图片资源使用 file-loaderurl-loader 进行处理。

        在 webpack5 中,已经内置了这两个 loader,只需进行简单配置即可。


module.exports = {
  ...
  // 模块
  module: {
    rules: [
      ...
      {
        test: /\.(png|jep?g|gif|webp|svg)/,
        // asset 表示静态资源,webpack 会使用自动使用 file-loader、url-loader 进行处理
        type: 'asset',
      }
    ]
  },
  // 插件
  plugins: [],
  // 模式
  mode: 'development',
}

截屏2023-02-25 19.05.14.png

        可以看到,打包后dist 目录多了一个hash值命名的图片,并且打开html后,对应的图片资源在页面中成功被渲染。

image.png

        实际开发中,对于一些比较小的图片资源,我们采用 base64 的方式进行渲染,从而减少请求次数。webpack 同样提供了该功能,只需做如下配置:

  {
    test: /\.(png|jep?g|gif|webp|svg)/,
    type: 'asset',
    parser: {
      dataUrlCondition: {
        // 小于 20 kb 的图片,采用 base64 的方式进行引入
        maxSize: 20 * 1024
      }
    }
  }

image.png

        由于打包后的静态资源与其他代码处于同一级目录,并且文件命名不可读,我们可通过如下配置修改文件存储目录及设置文件命名。

      {
        test: /\.(png|jep?g|gif|webp|svg)/,
        type: 'asset',
        parser: {
          dataUrlCondition: {
            // 小于 20 kb 的图片,采用 base64 的方式进行引入
            maxSize: 10 * 1024
          }
        },
        generator: {
          // 静态资源存储在 static/images 目录
          // 文件命名为 文件名-hash取6位.扩展名
          filename: 'static/images/[name]-[hash:6][ext]'
        }
      }

        重新打包后,静态资源被输出至指定目录下,并且文件命名符合我们预期。但是为啥会有两个图片文件?webpack 并不会自动清除上一次构建产生的资源。

image.png

处理 JS 资源

        webpack 虽然能够处理 JS 资源,但是其对于 JS 资源的处理非常有限,并不能进行代码兼容性处理。

        项目开发中,我们经常用到 ES6-ES10 之间的语法,但是低版本的浏览器可能并不能支持这些语法,此时就需要进行代码降级,我们使用 babel 进行代码降级。

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

        项目的根目录中创建一个 .babelrc 文件来指定配置转换规则

{
  "presets": ["@babel/preset-env"]
}

        指定处理 js 资源的 loader:

  {
    test: /\.js/,
    exclude: /node_modules/,  // 排除 node_modules 下的js 资源
    loader: 'babel-loader',
  }

截屏2023-02-25 19.35.17.png

处理 TS 资源

        处理 TypeScript 需要借助 ts-loader,处理步骤如下所示:

npm install typescript ts-loader -D

        项目跟目录下创建 tsconfig.json 文件:

// 用于指定 ts 编译时相关的配置
{
  "compilerOptions": {
    "target": "es2015",
    "module": "es2015",
    "strict": true
  }
}

        配置 webpack.config.js 处理 ts 资源:

module.exports = {
  ...
  // 模块
  module: {
    rules: [
      {
        test: /\.ts/,
        exclude: /node_modules/,  // 排除 node_modules 下的js 资源
        loader: 'ts-loader',
      }
    ]
  },
  resolve: {
    extensions: ['.ts']
  },
}

        webpack 处理 ts 资源时,还另外将“.ts”添加到resolve.extensions数组中,告诉Webpack在尝试解析导入语句时也要查找TypeScript文件。如果没有将“.ts”添加到resolve.extensions中,Webpack就无法正确解析TypeScript模块,这会导致编译错误或运行时错误。

image.png

处理 HTML 资源

        在上面的演示中,我们手动创建了一个 html 文件,并将打包后的资源进行手动引入。webpack 为我们提供 html-webpack-plugin 插件,该插件可以自动生成 html 文件(或手动指定 html 文件),然后自动引入打包后的资源。

        使用步骤如下:

npm install html-webpack-plugin -D
const HTMLWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  ...
  // 插件
  plugins: [
    new HTMLWebpackPlugin(),
  ],
}

截屏2023-02-25 19.56.06.png

        html-webpack-plugin使用详细场景

自动清除上一次构建

        webpack 并不会自动清除上一次的构建产物,每次手动删除右比较麻烦,我们可以通过 clean-webpack-plugin 来实现该功能。

npm install clean-webpack-plugin -D
const HTMLWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');

module.exports = {
  ...
  // 插件
  plugins: [
    new HTMLWebpackPlugin(),
    new CleanWebpackPlugin()
  ],
}

开发服务器

        通过上述的介绍,我们已经了解了 webpack 如何处理常见的资源,并且处理这一资源相对来说效率提升了很多。但是我们每改一行代码,都需要重新编译,然后再刷新页面,还是比较麻烦。

        为此,webpack 提供了开发服务器,每修改一次代码,都能够进行自动编译以及模块热加载。同时,在本地开发中,往往需要访问线上的服务器,此时会存在跨域问题,而 webpack-dev-server 则可以解决跨域,极大提高开发效率。

npm install webpack-dev-server -D
// package.json
  "scripts": {
    "build": "webpack",
    "dev": "webpack server --open"
  },
module.exports = {
  ...
  devServer: {
    port: 8888
  },
}

image.png

截屏2023-02-25 20.12.57.png

生产模式

        生产模式下,webpack 会进行 JS 代码的压缩从而提高效率。除此之外,我们还需要进行其他配置,进一步提高效率。

截屏2023-02-25 20.20.13.png

        可以看到 JS 在生产环境下被压缩为一行了。

image.png

CSS 单独打包

        开发环境下,CSS 文件是被打包到 JS 文件中的。这会导致如下问题:

1、JS 文件过大,网络请求时间长,会出现闪屏现象

2、项目结构不清晰

        我们可以通过 mini-css-extract-plugin 插件,将 css 资源单独打包成一个文件。使用 MiniCssExtractPlugin 时,需要将所有的 style-loader 替换为 MiniCssExtractPlugin.loader

npm install  mini-css-extract-plugin -D

image.png

CSS 兼容性处理

        有些 css 新语法在旧的浏览器中可能不支持,或者需要增加特定前缀才能支持。此时我们需要使用 CSS 后处理器,postcss 进行处理。

npm install postcss-loader postcss postcss-preset-env -D

image.png

截屏2023-02-25 20.37.24.png

image.png

CSS 压缩

        CSS 压缩使用 css-minimizer-webpack-plugin 插件。

npm install css-minimizer-webpack-plugin -D

image.png

image.png

总结

        如上便是 webpack 基本使用的内容,我们了解了常见资源的处理。目前而言,项目中我们主要是用 Vue 以及 React,由于这两种框架都提供了基于 webpack 的脚手架,所以未做介绍,但是处理原理依然和上面一致。

        最后,我们了解到 webpack 分为生产模式和开发模式,生产模式下,我们需要进行一定的优化,提升页面到效率。你可能会问,开发环境下为什么不做这些优化?有如下原因

1、代码压缩后,可读性差,有时我们可以需要看编译后的代码。

2、代码进行优化会增加耗时,开发阶段我们更看重看法效率。