Webpack(二):核心特性

上一篇的最后提出了对模块化打包方案或工具的诉求:

  • 能够将散落的模块打包到一起;
  • 能够编译代码中的新特性;
  • 能够支持不同种类的前端资源模块。

目前,前端领域有一些工具能够很好的满足以上这 3 个需求,其中最为主流的就是 Webpack、Parcel 和 Rollup。

如何使用 Webpack 实现模块化打包?

  • Webpack 作为一个模块打包工具,本身就可以解决模块化代码打包的问题,将零散的 JavaScript 代码打包到一个 JS 文件中。
  • 对于有环境兼容问题的代码,Webpack 可以在打包过程中通过 Loader 机制对其实现编译转换,然后再进行打包。
  • 对于不同类型的前端模块类型,Webpack 支持在 JavaScript 中以模块化的方式载入任意类型的资源文件,例如,我们可以通过 Webpack 实现在 JavaScript 中加载 CSS 文件,被加载的 CSS 文件将会通过 style 标签的方式工作。
  • Webpack 还具备代码拆分的能力,它能够将应用中所有的模块按照我们的需要分块打包
🌰
└─ webpack_test
   ├── src
   │   ├── heading.js
   │   └── index.js
   └── index.html
// ./src/heading.js
export default () => {
  const element = document.createElement('h2')
  element.textContent = 'Hello webpack'
  element.addEventListener('click', () => alert('Hello webpack'))
  return element
}
// ./src/index.js
import createHeading from './heading.js'
const heading = createHeading()
document.body.append(heading)
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Webpack - 快速上手</title>
</head>
<body>
  <script type="module" src="src/index.js"></script>
</body>
</html>
1. 安装 Webpack 的核心模块以及它的 CLI 模块
npm init --yes   #先初始化一个package.json文件,用来管理npm依赖版本
npm i webpack webpack-cli --save-dev

webpack 是 Webpack 的核心模块,webpack-cli 是 Webpack 的 CLI 程序,用来在命令行中调用 Webpack。

安装完成之后,webpack-cli 所提供的 CLI 程序就会出现在 node_modules/.bin 目录当中,我们可以通过 npx 快速找到 CLI 并运行它,具体操作如下:

npx webpack --version
# v4.43.0

npx 是 npm 5.2 以后新增的一个命令,可以用来更方便的执行远程模块或者项目 node_modules 中的 CLI 程序。

2. 运行 Webpack

有了 Webpack 后,就可以直接运行 webpack 命令来打包 JS 模块代码:

npx webpack

这个命令在执行的过程中,Webpack 会自动从 src/index.js 文件开始打包,然后根据代码中的模块导入操作,自动将所有用到的模块代码打包到一起。

完成之后,控制台会提示:顺着 index.js 有两个 JS 文件被打包到了一起。与之对应的就是项目的根目录下多出了一个 dist 目录,我们的打包结果就存放在这个目录下的 main.js 文件中。

修改 inde.html 中的代码:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Webpack - 快速上手</title>
</head>
<body>
  <script src="dist/main.js"></script>
</body>
</html>

成功使用。

对于 Webpack 最基本的使用,总结下来就是:先安装 webpack 相关的 npm 包,然后使用 webpack-cli 所提供的命令行工具进行打包。

配置 Webpack 的打包过程

🌰:需要它的打包入口是 src/main.js

那就需要通过配置文件的方式修改 Webpack 的默认配置,在项目的根目录下添加一个 webpack.config.js,具体结构如下:

 └─ 02-configuation
    ├── src
    │ ├── heading.js
    │ └── main.js
    ├── index.html
    ├── package.json
+   └── webpack.config.js ···················· Webpack 配置文件

webpack.config.js 是一个运行在 Node.js 环境中的 JS 文件,也就是说我们需要按照 CommonJS 的方式编写代码,这个文件可以导出一个对象,我们可以通过所导出对象的属性完成相应的配置选项。

添加一个 entry 属性,这个属性的作用就是指定 Webpack 打包的入口文件路径。我们将其设置为 src/main.js,具体代码如下所示:

// ./webpack.config.js
module.exports = {
  entry: './src/main.js'
}

配置完成之后,回到命令行终端重新运行打包命令,此时 Webpack 就会从 src/main.js 文件开始打包。

还可以通过 output 属性设置输出文件的位置

output 属性的值必须是一个对象,通过这个对象的 filename 指定输出文件的文件名称,path 指定输出的目录。

具体代码如下所示:

// ./webpack.config.js
const path = require('path')

module.exports = {
  entry: './src/main.js',
  output: {
    filename: 'bundle.js',
    path: path.join(__dirname, 'output')
  }
}
Webpack 工作模式

Webpack 4 新增了一个工作模式的用法,这种用法大大简化了 Webpack 配置的复杂程度。你可以把它理解为针对不同环境的几组预设配置:

  • production 模式下,启动内置优化插件,自动优化打包结果,打包速度偏慢;
  • development 模式下,自动优化打包速度,添加一些调试过程中的辅助插件;
  • none 模式下,运行最原始的打包,不做任何额外处理。

修改 Webpack 工作模式的方式有两种:

  • 通过 CLI --mode 参数传入;
  • 通过配置文件设置 mode 属性。

官方文档



如何通过 Loader 实现特殊资源加载?

在这里插入图片描述
🌰:通过 Webpack 打包项目中的一个 CSS 文件。

$ npm install css-loader --save-dev 
// ./src/webpack.config.js
module.exports = {
  entry: './src/main.css',
  output: {
    filename: 'bundle.js'
  },
  module: {
    rules: [
      {
        test: /\.css$/, // 根据打包过程中所遇到文件路径匹配是否使用这个 loader
        use: 'css-loader' // 指定具体的 loader
      }
    ]
  }
}

在配置对象的 module 属性中添加一个 rules 数组。这个数组就是我们针对资源模块的加载规则配置,其中的每个规则对象都需要设置两个属性:

  • test :它是一个正则表达式,用来匹配打包过程中所遇到文件路径,这里我们是以 .css 结尾;
  • use :它用来指定匹配到的文件需要使用的 loader,这里用到的是 css-loader

配置完成过后,回到命令行终端重新运行打包命令,打包过程就不会再出现错误了,因为这时 CSS 文件会交给 css-loader 处理过后再由 Webpack 打包。

不过此时 main.css 模块并没有工作。只需要再额外添加一个 style-loader,样式就可以正常工作了。

$ npm install style-loader --save-dev 
module: {
    rules: [
      {
        test: /\.css$/,
        // 对同一个模块使用多个 loader,注意顺序
        use: [
          'style-loader',
          'css-loader'
        ]
      }
    ]
  }


如何利用插件机制横向扩展 Webpack 的构建能力

clean-webpack-plugin 自动清除输出目录的插件
$ npm install clean-webpack-plugin --save-dev

安装过后,在 Webpack 的配置文件中导入 clean-webpack-plugin 插件,这个插件模块导出了一个叫作 CleanWebpackPlugin 的成员,我们先把它解构出来,具体代码如下:

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

回到配置对象中,添加一个 plugins 属性,这个属性就是专门用来配置插件的地方,它是一个数组,添加一个插件就是在这个数组中添加一个元素。

绝大多数插件模块导出的都是一个类型,CleanWebpackPlugin 也不例外,使用它,就是通过这个类型创建一个实例,放入 plugins 数组中,具体代码如下:

// ./webpack.config.js
const { CleanWebpackPlugin } = require('clean-webpack-plugin')

module.exports = {
  entry: './src/main.js',
  output: {
    filename: 'bundle.js'
  },
  plugins: [
    new CleanWebpackPlugin()
  ]
}

测试 clean-webpack-plugin 插件的效果:回到命令行终端,再次运行 Webpack 打包,此时之前的打包结果就不会存在了,dist 目录中存放的就都是我们本次打包的结果。

html-webpack-plugin 用于生成HTML的插件
$ npm install html-webpack-plugin --save-dev

安装完成过后,回到配置文件,载入这个模块。

html-webpack-plugin 插件默认导出的就是插件类型,不需要再解构内部成员。

具体如下:

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

有了这个类型过后,回到配置对象的 plugins 属性中,同样需要添加一下这个类型的实例对象,完成这个插件的使用,具体配置代码如下:

// ./webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')

module.exports = {
  entry: './src/main.js',
  output: {
    filename: 'bundle.js'
  },
  plugins: [
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin()
  ]
}
copy-webpack-plugin 用于复制文件的插件

安装 copy-webpack-plugin 插件,安装完成后,回到配置文件中,导入这个插件类型。然后同样在 plugins 属性中添加一个这个类型的实例,具体代码如下:

// ./webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')

module.exports = {
  entry: './src/main.js',
  output: {
    filename: 'bundle.js'
  },
  plugins: [
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      title: 'Webpack Plugin Sample',
      template: './src/index.html'
    }),
    new CopyWebpackPlugin([
      'public' // 需要拷贝的目录或者路径通配符
    ])
  ]
}

这个插件类型的构造函数需要传入一个字符串数组,用于指定需要拷贝的文件路径。它可以是一个通配符,也可以是一个目录或者文件的相对路径。我们这里传入的是 public 目录,表示将这个目录下所有文件全部拷贝到输出目录中。还可以在这个数组中继续添加其它路径,这样它在工作时可以同时拷贝。

配置完成以后回到命令行终端,再次运行 Webpack,此时 public 目录下的文件就会同时拷贝到输出目录中。


名称 链接
file-loader https://webpack.js.org/loaders/file-loader
url-loader https://webpack.js.org/loaders/url-loader
babel-loader https://webpack.js.org/loaders/babel-loader
style-loader https://webpack.js.org/loaders/style-loader
css-loader https://webpack.js.org/loaders/css-loader
sass-loader https://webpack.js.org/loaders/sass-loader
postcss-loader https://webpack.js.org/loaders/postcss-loader
eslint-loader https://github.com/webpack-contrib/eslint-loader
vue-loader https://github.com/vuejs/vue-loader

Webpack 为每一个工作环节都预留了合适的钩子,我们在扩展时只需要找到合适的时机去做合适的事情就可以了。



探索 Webpack 运行机制与核心工作原理

工作过程

在这里插入图片描述
Webpack 启动后,会根据配置,找到项目中的某个指定文件(一般这个文件都会是一个 JS 文件)作为入口。然后顺着入口文件中的代码,根据代码中出现的 import(ES Modules)或者是 require(CommonJS)之类的语句,解析推断出来这个文件所依赖的资源模块,然后再分别去解析每个资源模块的依赖,周而复始,最后形成整个项目中所有用到的文件之间的依赖关系树。

有了这个依赖关系树过后, Webpack 会遍历(递归)这个依赖树,找到每个节点对应的资源文件,然后根据配置选项中的 Loader 配置,交给对应的 Loader 去加载这个模块,最后将加载的结果放入 bundle.js(打包结果)中,从而实现整个项目的打包。

工作原理

Webpack 核心工作过程中的关键环节,明确“查阅”源码的思路:

  • Webpack CLI 启动打包流程;
  • 载入 Webpack 核心模块,创建 Compiler 对象
  • 使用 Compiler 对象开始编译整个项目;
  • 从入口文件开始,解析模块依赖,形成依赖关系树
  • 递归依赖树,将每个模块交给对应的 Loader 处理
  • 合并 Loader 处理完的结果,将打包结果输出到 dist 目录。



🔗 相关链接:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章