4.核心概念—Loader—上-灵析社区

懒人学前端

一、概述

loader 概述

到目前为止,我们的案例都是都是介绍的如何打包 js 文件,对于工程中的其他类型资源,如 CSS、图片、字体等, webpack 会如何处理呢?在实际的项目开发中,我们经常会用到 Sass 或者 Less 来编写样式,我们使用 Typescript 增加静态类型检查,我们使用浏览器不支持的 ECMAScript 新特性,如何让 webpack 来对所有的编译进行统一管理呢?

本章我们会介绍 loader(预处理器),它赋予了 webpack 可以处理不同资源的能力,极大丰富了其可扩展性。

loader 作用

在 webpack 中,一切皆模块,我们可以使用 import、require 等方式 在JavaScript 模块中导入 JS、CSS、图片、字体等多种类型的静态资源,loader 用于对模块的源代码进行转换。loader 可以使我们在导入模块时预处理文件。loader 可以将文件从不同的语言(如 TypeScript)转换为 JavaScript。loader 甚至允许我们直接在 JavaScript 模块中 import CSS 文件!

loader 本质上是 node module 导出的一个函数,当资源需要被转换时,调用这个函数。下面我们通过自定义 loader,来看 loader 的使用方法。

src/index.js

const demoName = 'webpack loader'
console.log(demoName)

webpack 默认支持解析 js 文件,我们增加解析 js 的 loader 只为展示 loader 是如何工作的

src/js-loader.js

module.exports = function (source) {
  console.log(source)
  return `module.exports=${JSON.stringify(source)}`
}

webpack.config.js

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
  mode: 'development',
  entry: {
    index: './src/index.js'
  },
  output: {
    filename: '[name].js',
    clean: true
  },
  plugins: [
    new HtmlWebpackPlugin({
      title: 'loader',
    }),
  ],
  module: {
    rules: [
      {
        test: /\.js$/,
        use: "./src/js-loader.js"
      }
    ]
  }
};

执行构建命令,在控制台可以看到 js-loader 文件的 console.log 输出了 index.js 文件内容,从上面的 简易版 js-loader.js 文件中可以看出,loader 本身就是一个函数,在该函数中对接收的内容进行转换,然后返回转换后的结果。

loader 使用方式配置

在我们的应用中,有两种使用 loader 的方式,分别是 配置方式(推荐),内联方式。

配置方式(推荐)

module.rules 允许我们在 webpack 配置中指定多个 loader。 这种方式是展示 loader 的一种简明方式,并且有助于使代码变得简洁和易于维护。同时让我们对各个 loader 有个全局概览:

loader 从右到左(或从下到上)地取值(evaluate)/执行(execute)。在下面的示例中,从 css-loader 开始执行,最后以 style-loader 为结束。

webpack.config.js

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

src/index.js

import './index.css';

src/index.css

body {
    color: red;
    padding: 20px;
    text-align: center;
}

index.html

...
<body>
    <div>import css</div>
    <script src='./dist/index.js'></script>
</body>

webpack 无法处理 CSS 语法,此时我们执行打包命令 控制台会报 “请使用合适的loader来处理这个文件类型”

下面我们将 css-loader,style-loader 加到工程中,loader 都是一些第三方 npm 模块,webpack 本身不包含任何 loader, 所以使用前我们需要先安装这些 loader,在工程中执行以下命令安装。

npm install css-loader style-loader

安装成功后,在控制台执行打包命令,我们可以看到错误已经消失了,在浏览器打开index.html 文件我们可以看到,样式正常展示。

内联方式

loader 除了使用配置的方式,还有一种内联的用法,可以在 import 语句或任何 与 "import" 方法同等的引用方式 中指定 loader。使用 ! 将资源中的 loader 分开。每个部分都会相对于当前目录解析。

在上面的例子中,我们注释掉 webpack.config.js 中 module.rules 的配置,将引入方式改为内联方式。

src/index.js

import '!style-loader!css-loader!./index.css';

执行打包命令后,我们在浏览器中打开 index.html 文件,可以看到样式正常显示。我们在工程中尽可能使用 module.rules,因为这样可以减少源码中的代码量,并且可以在出错时,更快地调试和定位 loader 中的问题。

loader 特性

  • loader 支持链式调用。链中的每个 loader 会将转换应用在已处理过的资源上。一组链式的 loader 将按照相反的顺序执行。链中的第一个 loader 将其结果(也就是应用过转换后的资源)传递给下一个 loader,依此类推。最后,链中的最后一个 loader,返回 webpack 所期望的 JavaScript。
  • loader 可以是同步的,也可以是异步的。
  • loader 运行在 Node.js 中,并且能够执行任何操作。
  • loader 可以通过 options 对象配置(仍然支持使用 query 参数来设置选项,但是这种方式已被废弃)。
  • 除了常见的通过 package.json 的 main 来将一个 npm 模块导出为 loader,还可以在 module.rules 中使用 loader 字段直接引用一个模块。
  • 插件(plugin)可以为 loader 带来更多特性。
  • loader 能够产生额外的任意文件。

二、HTML-Loader

html-loader

html-loader 用于将 html 文件转换为字符串,支持压缩、导出、对内容预处理。下面让我们来看一个例子

使用

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <title>html-loader</title>
</head>
<body>
    <img src="./src/assets/card-mark.png" />
</body>
</html>

webpack.config.js

const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
  ...
  plugins: [
    new HtmlWebpackPlugin({
      // 使用 index.html 内容作为输出模版 
      template: "./index.html", 
    }),
  ],

在index.html 中我们使用 img 标签展示一张图片,图片为相对路径,在 src/assets 文件夹下增加名为 card-mark.png 的图片,在浏览器直接打开 index.html 我们可以看到,图片可以正常展示。


此时我们在控制台执行打包命令 npm run build,在 dist 文件夹中 输出了 index.html 文件,我们直接在浏览器打开 index.html 文件,此时图片无法打开。

图片打不开的原因为 src 地址使用了相对路径,dist 文件夹与 src 文件夹同目录,所以 src="./src/assets/card-mark.png" 这个地址无法找到图片,导致图片无法展示。此时我们就可以借助 html-loader 来帮我们解决这个问题。

安装 html-loader

npm install html-loader -D

安装成功后,将 html-loader 配置到 webpack.config.js 中

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

module.exports = {
  ...
  plugins: [
    new HtmlWebpackPlugin({
      template: "./index.html",
    }),
  ],
  module: {
    rules: [
      {
        test: /\.html$/,
        use: {
          loader: 'html-loader',
        }
      }
    ]
  }
};

此时在执行打包命令,可以看到在 dist 文件夹下除了index.html 文件和 index.js 文件,又多了一个 扩展名为 .png 的文件,我们打开 dist 文件夹下的 index.html 发现 src 的引用地址已修改,此时在浏览器中直接打开 dist 文件夹下的 index.html 发现图片可以正常显示。

配置项

html-loader 包含下面四个配置项

sources

sources 默认值为 true

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.html$/,
        loader: "html-loader",
        options: {
          // 设置为 false 则不会对可加载属性做任何处理
          sources: false,
        },
      },
    ],
  },
};

默认情况下,每个可加载属性(例如 img 图片导入)都将被导入( const img = require ('./image.png') 或 import img from "./image.png" )。 你可能需要为配置中的图片指定 loader(我们前面的例子,如果配置 html-loader 的参数 esModule: false,则需要使用 loader 对图片进行处理,否则打包报错)。

html-loader 支持处理的 可加载属性 包括:

  • audio 标签的 src 属性
  • img 标签的 src 属性
  • img 标签的 srcset 属性
  • 剩余标签和属性可查看文档

source 设置为 Object

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.html$/,
        loader: "html-loader",
       options: {
            sources: {
              list: [
                {
                  tag: "img",
                  attribute: "data-src",
                  type: "src",
                }
              ]
            },
          },
      },
    ],
  },
};

index.html

<body>
    <img data-src="./src/assets/card-mark.png" src="./src/assets/card-mark.png" />
</body>

执行打包命令,看下 dist 文件夹下的 index.html 文件,发现 html-loader 只对 img 标签的 data-src 属性做了转换。

sources 对象中 支持 list 和 urlFilter 属性,详情可查看文档

preprocessor

允许在处理之前对内容进行预处理。

webpack.config.js

const Handlebars = require("handlebars");
module.exports = {
  module: {
    rules: [
      {
        test: /\.html$/,
        use: {
          loader: 'html-loader',
          options: {
            preprocessor: (content, loaderContext) => {
              let result;
  
              try {
                result = Handlebars.compile(content)({
                  firstname: "Value",
                  lastname: "OtherValue",
                });
              } catch (error) {
                loaderContext.emitError(error);
  
                return content;
              }
              return result;
            }
          }
        }
       },
      },
    ],
  },
};

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <title>html-loader</title>
</head>
<body>
    <p>{{firstname}} {{lastname}}</p>  
    <img src="./src/assets/card-mark.png" />
</body>
</html>

在 webpack.config.js 中我们使用了 handlebars ,需要先安装 handlebars。

Handlebars 是一种简单的模板语言。

它使用模板和输入对象来生成 HTML 或其他文本格式。Handlebars 模板看起来像带有嵌入式 Handlebars 表达式的常规文本。

<p>{{firstname}} {{lastname}}</p> 

执行打包命令后,查看 dist 文件夹下的 index.html 文件,可以看到 p 标签内容已被替换。

minimize

告诉 html-loader 编译时需要压缩 HTML 字符串。

默认情况下,启用压缩的规则如下:

({
  caseSensitive: true,
  collapseWhitespace: true,
  conservativeCollapse: true,
  keepClosingSlash: true,
  minifyCSS: true,
  minifyJS: true,
  removeComments: true,
  removeRedundantAttributes: true,
  removeScriptTypeAttributes: true,
  removeStyleLinkTypeAttributes: true,
});

webpack.config.js


module.exports = {
  module: {
    rules: [
      {
        test: /\.html$/i,
        loader: "html-loader",
        options: {
          // boolean
          minimize: true,
          // 对象
          minimize: {
            removeComments: false,
            collapseWhitespace: false,
          },
        },
      },
    ],
  },
};

esModule

默认情况下, html-loader 生成使用 ES modules 语法的 JS 模块。 在某些情况下,使用 ES modules 会更好,例如在进行模块合并和 tree shaking 时。

你可以使用以下方法启用 CommonJS 模块语法:

webpack.config.js


module.exports = {
  module: {
    rules: [
      {
        test: /\.html$/i,
        loader: "html-loader",
        options: {
          esModule: false,
        },
      },
    ],
  },
};

总结

本节我们介绍了 html-loader 的使用方法和 html-loader 包含的 4 个参数 sources、preprocessor、minimize、esModule, 它们分别对应 html-loader 在项目实践中的 4 个重要功能:

  1. 将 HTML 中标签的可加载属性引入的文件作为模块导入
  2. 预处理 HTML,常用来支持模板引擎
  3. 压缩 HTML
  4. 默认导出 ES modules 便于模块合并和 tree shaking

三、URL-Loader

url-loader

当我们在文件中加载图片、字体等资源时,webpack 无法直接处理以上资源,在 webpack 5 之前需要使用相应 loader 来处理资源文件,url-loader 可以将一个文件转换为 base64 编码来代替访问地址,这样做的好处是可以减少一次网络请求,下面我们来看看如何使用 url-loader 及 url-loader 有哪些常用配置。

使用

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <title>url-loader</title>
</head>
<body>
</body>
</html>

index.js

index.js 文件中引入一张图片,创建image标签后,将导入的图片赋值给 image 标签的 src 属性,将 image 标签添加到页面中

import Back from './img/back.png';

function component() {
  var element = document.createElement('img'); 
  element.src=Back
  return element;
}

document.body.appendChild(component());

安装 url-loader

npm install url-loader -D

安装成功后,将 url-loader 配置到 webpack.config.js 中

module.exports = {
  ...
   module: {
    rules: [
      {
        test: /\.(png|jpg)$/,
        use: [
          {
            loader: 'url-loader'
          }
        ]
      }
    ]
  }
};

此时在执行打包命令,此时在浏览器中直接打开 dist 文件夹下的 index.html 发现页面中展示一张图片。

配置项

url-loader 包含下面 3 个配置项

limit

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.(png|jpg)$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 2048 // 2Kb
            }
          }
        ]
      }
    ]
  }
};

一般情况下,当资源文件大小小于 2Kb 时,我们需要将资源路径转换为 base64 的格式将资源打包到 bundle 中,这样可以减少一次网络请求,当一个页面中引入多个资源文件时可以明显减少请求次数,但这种方式带来了另外一个问题,如果资源文件体积较大,就会导致 bundle 的体积增大,体积大的情况下网络请求时间变长,会导致页面白屏时间变长,非常影响用户体验。所以在处理资源文件时,一般会加上 limit 配置,文件资源体积超过配置的大小后,更改资源文件的处理方式,默认使用 file-loader 来处理。

上面的配置在执行打包命令时会报 “Cannot find module 'file-loader'” 的错误,所以在使用 limit 配置时,我们先下载安装 file-loader。

安装 file-loader

npm install file-loader -D

再次执行打包命令,在 dist 文件夹下输出了一个扩展名为 .png 的图片,让我们来对比下增加 limit 配置前和增加 limit 配置后 dist 文件夹和 index.js 文件的变化。

增加 limit 配置前

dist 文件夹

index.js

增加 limit 配置后

dist 文件夹

index.js

mimetype

设置文件的转换类型。如果未指定,将使用文件扩展名来查找MIME 类型。

webpack.config.js


 rules: [
      {
        test: /\.(png|jpg)$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              mimetype: 'image/jpg'
            }
          }
        ]
      }
    ]

fallback

指定当目标文件的大小等于或超过限制选项中设置的限制时,使用的替代加载 loader,默认为 file-loader。


module: {
    rules: [
      {
        test: /\.(png|jpg)$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 2048,
              fallback: 'responsive-loader'
            }
          }
        ]
      }
    ]
  }

总结

本节我们介绍了 url-loader 的使用方法和 url-loader 包含的 3 个参数 limit、minitype、fallback, url-loader 设置了 limit 参数后,超过设置的限制大小后,默认使用 file-loader 加载资源文件,所以 file-loader 的可配置参数在 url-loader 中也可配置生效,剩余可配置参数在 file-loader 中继续总结。

四、File-Loader

file-loader

在 webpack 5 之前处理图片、字体等资源,除了使用 url-loader 之外还经常使用 file-loader,file-loader 的处理方式和 url-loader 有些不同,url-loader 通过 limit 参数判断如果没有超过配置大小,则将文件转做 base64 编码,直接嵌入到 CSS/JS/HTML 代码中。而 file-loader 并不会对文件内容进行任何转换,只是复制一份文件内容,并根据配置为他生成一个唯一的文件名, 下面让我们梳理下 file-loader 如何使用及有哪些可配置参数。我们继续使用 url-loader 的例子, 只是对个别配置做些修改。

使用

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <title>file-loader</title>
</head>
<body>
</body>
</html>

index.js

index.js 文件中引入一张图片,创建 image 标签后,将导入的图片赋值给 image 标签的 src 属性,将 image 标签添加到页面中。

import Back from './img/back.png';

function component() {
  var element = document.createElement('img'); 
  element.src=Back
  return element;
}

document.body.appendChild(component());

安装 file-loader

npm install file-loader -D

安装成功后,将 file-loader 配置到 webpack.config.js 中

module.exports = {
  ...
   module: {
    rules: [
      {
        test: /\.(png|jpg)$/,
        use: [
          {
            loader: 'file-loader'
          }
        ]
      }
    ]
  }
};

执行打包命令,此时在浏览器中直接打开 dist 文件夹下的 index.html 发现页面中展示一张图片,此时我们在 dist 目录下可以看到一张扩展名为 .png 的图片,直接点击图片打开,可以看到与我们引入的图片一致。

配置项

file-loader 包含下面几个配置项

name

可以使用查询参数名称为您的文件配置一个自定义的文件名模板。默认情况下,不配置 name 属性生成的文件的文件名就是文件内容的 MD5 哈希值与原始扩展名。name 属性支持传入字符串或函数配置。

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.(png|jpg)$/,
        use: [
          {
            loader: 'file-loader',
            options: {
              name: [name].[ext]
            }
          }
        ]
      }
    ]
  }
};

name 参数可以传入以下常用占位符

常见的打包命名方式是:assets/[name]-[hash].[ext],即将所有 file-loader 处理的图片按照 name 传入的文件名称 + hash 值.扩展名的方式打包到 assets 目录下。其中

  1. [ext] 表示是原文件的扩展名,如 back.png 就是指 png
  2. [name] 表示原文件的文件名。如 back.png 就是指 back,但一般生产环境不推荐直接使用 [name],一般和 [hash] 一起使用,这样可以防止命名冲突。
  3. [path] 相对于 context 的路径,context 默认是 webpack.config.js 的路径
  4. [hash:6]可以控制 hash 值的长度,6 表示长度为 6,默认是 32

context

修改打包文件生成路径,其实影响的 是 path 占位符,context 需要和 path 占位符同时配置才会影响文件生成路径。

webpack.config.js

{
  loader: 'file-loader',
  options: {
    name: '[path][name].[ext]',
    context: __dirname + '/../'
  }
}

打包后,当前项目根文件夹和图片所在路径形成了打包文件的新路径,如果不设置 context 则打包路径相对于 webpack.config.js 的 context 的路径。

publicPath

publicPath 一般会用 webpack 本身配置的,和那个效果也一样,但假如你想单独配置,就用这个。设置 publicPath 后,文件的请求地址会被打包进 js 文件。

webpack.config.js

{
  loader: 'file-loader',
  options: {
    name: '[name].[ext]',
    outputPath: 'https://www.abc.cn/img/'
  }
}

outputPath

outputPath 在文件前增加路径,也就是增加文件夹。

webpack.config.js

{
  loader: 'file-loader',
  options: {
    name: '[name].[ext]',
    outputPath: 'images/'
  }
}

总结

本节我们介绍了 file-loader 的使用方法和 file-loader 包含的几个常用参数配置。file-loader 的可配置选项在 url-loader 中配置也可生效(limit 生效的情况下),在 webpack5 以前对于资源文件的处理一般使用这两种插件,webpack5 提供了一种模块Asset Modules,它允许人们在不配置额外加载器的情况下使用资源文件(字体、图标等)

阅读量:2007

点赞量:0

收藏量:0