Loading...

# hello Webpack

本节主要介绍 Webpack 相关知识,聊聊 Webpack 的由来,以及为什么要使用 Webpack,通过两大利器:Loader 与 Plugins 进行讲解。

要想快速知道 Webpack 是什么,最好的方式就是通过官网去了解它。通过官方介绍,我们可以知道:webpack 是一个现代 JavaScript 应用程序的静态模块打包器 (module bundler)。

image.png

在最初,Webpack 并不被人熟知,它刚出现时,主打的优势是 Code Splitting,我们现在从官网也能看到对它的定义:

Code Splitting : 代码分离指将代码分成不同的包 / 块,然后可以按需加载,而不是加载包含所有内容的单个包。

什么时候 Webpack 才受人关注?2014 年,Instagram 的前端团队在一次大会上分享其内部前端页面加载性能优化,提到最重要的一点就是用到了 Webpack 的 Code Splitting

这简直就是为 Webpack 好友助力了一波,之后形成了一个热潮。Webpack 的风口来了,很多公司纷纷使用 Webpack,并贡献了无数的 Plugin、Loader,你一刀,我一刀,明天 Webpack 就出道,果不其然,短短时间内,Webpack 被推上了高潮。

大家都用,我需要用吗?如果说你的应用程序非常小,没有什么静态资源,只需要一个 JS 文件就可以满足需求,这时使用 Webpack 并不是一个好的选择。至于你用与不用,得靠你自身评估~

# 两大利器

得益于 Webpack 扩展性强,插件机制完善,官方提供了许多的 Loader、Plugin,接下来通过问题,配合简单明了的 demo,给大家讲解这两大利器,在此之前,我们先全局安装一下 Webpack。

npm install webpack --save --dev
npm install webpack-cli --save --dev

# Loader 模块打包方案

官方对 Loader 的介绍是:Webpack 可以使用 Loader 来预处理文件。这允许你打包除 JS 之外的任何静态资源。

在我看来, Loader 就是一种模块打包方案 ,怎么理解?给大家科普一个知识点:Webpack 默认是知道如何打包 js 类型文件,但对于其他类型文件,它是不知道如何处理,我们得告诉它,对这种类型文件,打包的方案是什么。接下来,我们通过例子,帮助小伙伴们理解为什么我说它是一种方案。

我们新建一个 demo 文件夹,创建一个 index.js 文件,文件结构是这样的

├── demo
│ └── index.js
└──...

此时我们在 index.js 中写下这行代码

// index.js
const str = 'hello webpack';
console.log(str);

执行一下 npx webpack ./index.js ,意思就是对我们的 index.js 文件打包。

我们在终端可以看到,在不配置任何东西情况下,Webpack 也能够打包 JS 类型文件,这说明 Webpack 默认对 JS 文件是有一套打包方案的

img

接下来,我们将代码改成这样,引入我们的图片

// index.js
import myPdkAvatar from './cover.jpg';
const str = 'hello webpack';
console.log(str);

同上,执行 npx webpack index.js ,此时会报错。对 jpg 类型的文件打包失败了

img

Webpack 很友好,它会告诉你,你需要一个 loader 去处理此文件类型。

img

官方提供了一种专门处理此类型的方案:file-loader,我们安装一下这个 loader

npm install --save-dev file-loader

接着新增一个文件 webpack.config.js ,此时的文件结构是这样的

├── demo
│ ├── index.js
│ └── webpack.config.js
└──...

我们在配置文件中,添加一下对于 jpg 这种类型文件的处理方案

// webpack.config.js
module.exports = {
  module: {
    rules: [
      {
        test: /\.jpg$/,
        use: [
          {
            loader: 'file-loader',
            options: {
              name: '[name]_[hash].[ext]',
              outputPath: 'images/',
            },
          },
        ],
      },
    ],
  },
};

解读一下这段代码,意思就是:当遇到模块 (module) 时,进行规则 (rules) 匹配,如果匹配到 /\.jpg$/ 类型的文件,就采用 file-loader 方案进行打包,并且配置了参数: nameoutputPath ,意味着打包后的文件名是按照 [文件名]_[哈希值].[源类型] 规则命名,并且输出在 images/ 目录下

理解了这段代码含义之后,我们再来打包,看看结果如何,执行 npx webpack ./index.js

image.png

打包正常!我们再看看打包之后的 dist 文件下,是不是真的有个 images/ 目录存放着打包后的图片?

img

如我们所想,现在回过头细品,Loader 就是一种模块打包方案是不是也有点道理?下面写几行代码,大家细品细品

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

# Plugins 打包更加便捷

继续以上边的 Loader demo 为例子,回顾一下我们现在 demo 的文件目录结构

├── demo
│ ├── index.js
│ └── webpack.config.js
└──

我们先来执行一下 npx webpack index.js ,来看看 dist 目录下有哪些文件

image.png

通过官网可知,在我们未配置 output 属性时,它的默认值是 ./dist/main.js,其他生成文件默认放置在 ./dist 文件夹中。

因为我们都用的默认配置,所以打包生成的文件夹名就叫 dist,bundle 默认名称就是 main.js

接下来我们手动创建一个 HTML,加载打包后的 js 文件,如何加载呢?通过 script 加载打包后的 main.js

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>webpack plugins demo</title>
  </head>
  <body>
    <div id="root"></div>
    <!-- 在这里加载打包好之后的 main.js 文件 -->
    <script src="./dist/main.js"></script>
  </body>
</html>

然后运行此页面,通过控制台,可以看到会打印出: hello webpack

假设现在有一种场景,需要通过 hash 进行命名输出的 bundle。我们来修改一下 webpack.config.js

module.exports = {
  // 1. webpack 执行构建的第一步将从 entry 开始,这里我们的入口文件为 index.js
  entry: './index.js',
  // 2. 经过一系列处理得到最终的代码,然后输出结果
  output: {
    // 这里将输出的结果代码文件自定义配置文件名
    filename: '[webpack]_[hash].bundle.js',
  },
  // ...
};

执行 npx webpack ./index.js ,来看看打包之后的文件命名格式是否如我们预期

image.png

没毛病,这时候我们 HTML 加载该怎么办?手动修改成正确的文件地址

<script src="./dist/[webpack]_04638f451cfea56abd38.bundle.js"></script>

如果我们将 index.js 文件中的内容修改(👇 下面添加一行代码)

// index.js
import myPdkAvatar from './cover.jpg';
const str = 'hello webpack';
console.log(str);
// 新添加的代码
console.log('new add code ......');

然后把 dist 目录删除,再打包一次,看看文件 hash 是否一致?

Entrypoint main = [webpack]_b8a2cb97e0f716e38925.bundle.js

通过对比,我们发现,每次修改,重新打包生成的 bundle 文件名哈希值都不一样。等价于每次打包都需要手动修改 HTML 中的文件引用

太原始太麻烦了,低效率!为此,Webpack 提供了 Plugins 插件能力,让 Webpack 变得更加灵活。

官方提供了很多 Plugins,让我们的打包更加便捷,上面的问题,我们可以通过 HtmlWebpackPlugin 插件进行简化 HTML 文件的创建,这对于在文件名中包含每次会随着编译而发生变化哈希的 webpack bundle 尤其有用!

多说无益,上手试试,先根据文档,安装一下插件,看看它能实现怎样的效果

npm install --save-dev html-webpack-plugin

安装好之后,我们来修改 webpack.config.js 内容,将这个插件引入

const path = require('path');
  // 👇 引入此插件
const HtmlWebpackPlugin = require("html-webpack-plugin");
// webpack.config.js
module.exports = {
  // 1. webpack 执行构建的第一步将从 entry 开始,这里我们的入口文件为 index.js
  entry: "./index.js",
  // 2. 经过一系列处理得到最终的代码,然后输出结果
  output: {
    // 这里将输出的结果代码文件自定义配置文件名
    path: path.resolve(__dirname, 'dist'),
    filename: "[webpack]_[hash].bundle.js",
  },
  // 👇 使用此插件
  plugins: [new HtmlWebpackPlugin()],
  module: {
    rules: [
      {
        test: /\.jpg$/,
        use: [
          {
            loader: "file-loader",
            options: {
              name: "[name]_[hash].[ext]",
              outputPath: "images/",
            },
          },
        ],
      },
    ],
  },
};

执行一下 npx webpack ./index.js ,打包的出来的文件有哪些?

image.png

image.png

HtmlWebpackPlugin 会在打包结束后,自动帮我们生成一个 HTML 文件,同时把打包后的 bundle 自动引入。当我们内容修改,重新打包,生成的 HTML 也会随着每次编译导致哈希变化的 bundle 自动引入。

是不是很完美呢?不,我们采用火眼金睛瞧一瞧由 HtmlWebpackPlugin 生成的 HTML 文件,你会发现好像有些问题?是不是 body 下少了一些 DOM 节点(比如 Vue、React 都会有一个 id 为 app 的 DOM 元素),怎么办?这是该插件默认生成的,有没有办法生成我想要的 DOM 结构呢?

HtmlWebpackPlugin 提供了一个配置参数 template ,它允许你自定义 HTML 文件,以此文件为模版,生成一份一样的 HTML 并为你自动引入打包后的 bundle。

我们来动手实现一下,首先定义一份 “别具一格” 的 HTML 模版。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>我是 HtmlWebpackPlugin 的模版</title>
    <style>
      * {
        margin: 0;
      }
    </style>
  </head>
  <body>
    <div id="root">
      <div id="pdk">PDK Demo</div>
    </div>
  </body>
</html>

然后通过修改 webpack.config.js 配置,采用此模版为基础

const path = require('path');
  // 👇 引入此插件
const HtmlWebpackPlugin = require("html-webpack-plugin");
// webpack.config.js
module.exports = {
  // 1. webpack 执行构建的第一步将从 entry 开始,这里我们的入口文件为 index.js
  entry: "./index.js",
  // 2. 经过一系列处理得到最终的代码,然后输出结果
  output: {
    // 这里将输出的结果代码文件自定义配置文件名
    path: path.resolve(__dirname, 'dist'),
    filename: "[webpack]_[hash].bundle.js",
  },
  // 👇 使用此插件
  plugins: [new HtmlWebpackPlugin({
    template: './index.html',
  })],
  module: {
    rules: [
      {
        test: /\.jpg$/,
        use: [
          {
            loader: "file-loader",
            options: {
              name: "[name]_[hash].[ext]",
              outputPath: "images/",
            },
          },
        ],
      },
    ],
  },
};

最后我们来瞧瞧,是否打包后自动生成的 HTML 文件结构跟我们的模版一致?

image.png

事实证明,确实一模一样。

官方还有很多精巧有用的 Plugins 插件,几乎每个插件目的都是出于让你的打包构建更加便捷。小伙伴们要善于使用搜索引擎去寻找所需的插件工具(官方插件或第三方 Plugins 插件)及解决问题的方法。

# 总结

  • Loader 就是一种模块打包方案,换言之,它是一名具备文件类型转换的翻译员
  • Plugins 用于扩展 Webpack 的功能,使得 webpack 变得极其灵活。
  • Plugins 可以在 Webpack 运行到某个时刻,帮你做一些事情。其实 Plugins 很像生命周期函数,在 Webpack 运行到某个生命周期去做些事情。

如上述例子中,HtmlWebpackPlugin 就是在 Webpack 打包过程结束的生命周期时刻,去做了一些事情 —— 自动生成 HTML 文件,引入打包后的 bundle。

在比如 clean-webpack-plugin 第三方的插件,它其实就是在 Webpack 打包之前的生命周期时刻,去做了一些事情 —— 删除我们打包的目录

这两个 Plugins 相信项目中都会用到,回去翻一翻项目的配置,结合文档,在细品细品。

更新于 阅读次数

请我喝[茶]~( ̄▽ ̄)~*

jluyeyu 微信支付

微信支付

jluyeyu 支付宝

支付宝