前端最常用的打包工具webpack(二)-引入资源

这是重新学习webpack的第二篇文章,之前我也写过一篇关于webpack的文章。问题是过去快半年了,我已经将webpack忘得差不多,回头一看那篇文章完全是一头雾水,也没有办法,那个时候我才刚刚开始写前端方面的博客,对于知识梳理、文字表达都有一些欠缺,啪啪啪一下扔出来几千字,全是重点,没有任何过渡性的语言,看个一会就没什么兴趣。

综上所述,本次决定给webpack写一个比较详细的系列文章,目的是如果我半年后又忘记了webpack的具体内容,再来翻阅文章,不会像这次一样一头雾水。


前景提要:前端最常用的打包工具webpack(一)-初识篇

在前一篇文章中,我们已经可以打包js文件,但是在一个工程项目中,我们不仅有js文件,还有其它类型的文件,比如.css.scss文件,而这些文件跟js文件不同,如果使用webpack的默认打包,则无法进行识别这些文件,从而报错。

我们在项目根目录下创建一个src文件夹,然后在里面创建一个style.css文件,写上如下代码

* {
    padding: 0;
    margin: 0;
    box-sizing: border-box;
}

接着我们在main.js中引入:

import "./src/style.css";  // 引入样式文件

function add(x, y) {
  return x + y;
}

console.log(add(1, 2));

这种引入方式在使用前端框架时会经常用到,那么这个时候我们再运行一次打包指令:

webpack ./main.js -o ./build/ --mode=development

image-20210206160001409

可以看到报错了,提示模块解析失败,因为默认情况下,webpack是不认识.css这一类的文件,所以也就无法将它打包到项目中,那么如何才能将它打包呢?

接下来我们就要说到webpack的配置文件webpack.config.js


1. 配置文件

在项目的根目录下创建一个webpack.config.js文件。

module.exports = {};

注意:因为webpack是使用Nodejs写的,所以需要使用CommonJS语法进行导出,而不能使用我们常用的es6 modules(export default {};)形式进行导出。

webpack ./main.js -o ./build/ --mode=development这一段在终端中运行的代码,如果通过webpack.config.js配置来实现的话就需要写成下面这个样子:

const { resolve } = require("path");

module.exports = {
  // 入口文件
  entry: "./main.js",
  // 输出文件
  output: {
    // 输出路径
    path: resolve(__dirname, "build"),
  },
  // 输出模式
  mode: "development",
};

然后在终端中运行:

webpack

即可以根据配置webpack.config.js文件来打包。

说到这里你是否觉得下面这种方式比起上面终端中要敲webpack ./main.js -o ./build/ --mode=development这一坨要简便多了?

其实webpack ./main.js -o ./build/ --mode=development存在是有它的道理的,这个我们放在后面进行讲解为什么会存在这种形式的打包方式,并且它还是一个非常常用的打包方式。

2. 打包CSS

在终端下载两个关于处理CSS的loader:

  • style-loader:创建style标签,将js中的样式资源进行插入,添加到head中生效。
  • css-loader:将css文件编译成CommonJS模块加载到js中,里面内容是样式字符串。
npm install style-loader css-loader --save-dev

然后将下面的配置写入webpack.config.js文件。

const { resolve } = require("path");

module.exports = {
  // 入口文件
  entry: "./main.js",
  // 输出文件
  output: {
    // 输出路径
    path: resolve(__dirname, "build"),
  },
  module: {
    rules: [
      // 详细的loader配置
      {
        // 用正则表达式匹配文件类型,下面就是匹配以.css结尾的文件,并且将它们使用下面的loader解析。
        test: /\.css$/,
        use: [
          // 创建style标签,将js中的样式资源进行插入,添加到head中生效
          "style-loader",
          // 将css文件编程CommonJs模块加载到js中,里面内容是样式字符串
          "css-loader",
        ],
      },
    ],
  },
  // 输出模式
  mode: "development",
};

可以看到上面的配置文件多了一个module对象,并且在其下面有一个rules数组,在这个rules数组中就是配置loader相关的东西。

  • test:用正则表达式匹配文件类型。
  • use:需要使用的loader。

use下面的loader执行顺序是从右到左(从下到上)。这个非常重要,如果写反了可能会导致无法正确进行打包。

这个时候我们再运行webpack打包一次,显示打包成功,然后再到打包后的文件中就可以看到最终的输出结果。

3. 打包SASS

负责将sass转换为css的两个loader是:sass-loadersass

现在大部分项目已经使用sass替代node-sass了,因为node-sass之前依赖Python,并且它还有一部分文件来自于GitHub,因为国内网络环境的原因,导致经常下载node-sass时出错,后来官方推出了编译速度更快的sass替代node-sass

下载两个loader:

npm install sass-loader sass --save-dev

配置代码:

{
  test: /\.(scss|sass)$/,
  use: [
    "style-loader",
    "css-loader",
    "sass-loader",
  ],
},

简单的说就是将.scss.sass文件转换为css,然后将css编译为webpack支持的文件类型。

4. HTML

HTML文件有些许的不同,因为HTML是不用通过webpack进行编译,而是直接输出.html文件,然后通过浏览器进行解析。

所以针对HTML文件,我们不需要使用loader,但是要使用一个插件来默认创建一个HTML文件,然后引入打包后的js文件。

  • html-webpack-plugin:默认会创建一个空的HTML,自动引入打包输出的所有资源。

首先需要进行安装该插件:

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

module对象下面新增:

// 引入html-webpack-plugin插件
const HtmlWebpackPlugin = require("html-webpack-plugin");

// 在module对象下面新增
plugins: [new HtmlWebpackPlugin()],

这个时候再运行打包指令webpack

image-20210206211439805

可以看到在build目录下输出了一个index.html文件,并且引入了main.js

同时我们也可以自行配置HTML文件模板样式。

在项目的根目录下创建一个public文件夹,在下面创建一个index.html文件。

image-20210206211727766

重新配置一下webpack。

plugins: [
  new HtmlWebpackPlugin({
    template: resolve(__dirname, "./public/index.html"),
  }),
],

image-20210206212049725

可以看到,HTML文件已经应用了我们给出的模板,并且也引入了main.js文件。

5. 处理图片资源

引入依赖:

npm install url-loader file-loader --save-dev

url-loader依赖file-loader,设置配置如下:

{
  test: /\.(jpg|png|gif)$/,
  use: [
    {
      // url-loader依赖file-loader。
      loader: "url-loader",
      options: {
        // 图片大小小于8kb,就会被编译成base64打包在项目中
        // 优点:减少请求数量(减轻服务器压力)。
        // 缺点:图片体积会更大。
        // 一般只将8~12kb的图片进行处理
        limit: 8 * 1024,
      },
    },
  ],
},

根据上面的配置,小于8kb的图片就会被编译成为base64。


然而,如果在HTML中有引入图片文件,例如:

image-20210206215837990

打包后引入图片的地址会原模原样的进行输出,但是打包后的目录结构发生了变化,根本就找不到这个文件夹,所以引入也是无效的,那么这种情况如何处理呢?

处理方式:引入html-loader

{
  test: /\.html$/,
  loader: "html-loader",
},

再次尝试进行打包,发现输出的文件已经产生了变化

image-20210206220310222

如果觉得打包后的文件名太长,可以设置url-loadername属性。

{
  test: /\.(jpg|png|gif)$/,
  use: [
    {
      // url-loader依赖file-loader。
      loader: "url-loader",
      options: {
        limit: 8 * 1024,
        // 设置名称长度,保留hash前10位,ext表示原有后缀名
        name: "[hash:10].[ext]",
      },
    },
  ],
},

6. 其它资源

如果有一些不希望webpack打包的资源,我们可以将它排除掉,直接放到项目中去。

{
  // 需要排除的文件类型
  exclude: /\.(html|css|js|scss|sass|png|jpg|gif)/,
  loader: "file-loader",
},

7. 最后

经过本次学习,我已经成功明白如何将除了js外的其它文件通过webpack进行打包,那么本篇文章就到这里,下一篇文章就介绍如何在开发环境将项目跑在webpack创建的服务器上,并且处理css和js文件的兼容性问题。