浅谈一下 webpack 以及 loader 和 plugin

编程入门 行业动态 更新时间:2024-10-06 10:29:56

<a href=https://www.elefans.com/category/jswz/34/1769825.html style=浅谈一下 webpack 以及 loader 和 plugin"/>

浅谈一下 webpack 以及 loader 和 plugin

话说,前端练习时长也快两年了,但是关于 webpack 的东西好像也没怎么研究过 😅

🚩一是没有这方面的需求:回想一下,关于 webpack 的配置相关工作,也就只有自己配置过一次 loader「使用 svg-sprite-loader、svgo-loader 优化 svg symbols」,还是摸着石头过河;

🚩二是大部分的配置工作脚手架都已经做好了,这很可能导致一个问题,就是别人问你 webpack 相关的知识的时候,阿巴阿巴阿巴... 🤕️

确实,大多数情况下,前端开发人员可能不需要深入了解 webpack,但了解 webpack 的基本概念和用法对于前端开发仍然是很有益的。话不多说,开搞!🤓️

1. webpack

先让我们 👀 一下 webpack 官网 的解释:

本质上,webpack 是一个用于现代 JavaScript 应用程序的 静态模块打包工具。当 webpack 处理应用程序时,它会在内部从一个或多个入口点构建一个 依赖图(dependency graph),然后将你项目中所需的每一个模块组合成一个或多个 bundles,它们均为静态资源,用于展示你的内容。

怎么感觉所有框架或工具的官网定义都不是那么的通俗易懂?🤔

通俗点讲,就是当我们开发应用时,无论你用什么框架也好,都会在项目内部有一个入口文件,比如 Vue 和 React 项目的 `main.ts` 文件,其他模块的代码一般会分散在多个文件中,这些文件可能包含不同的功能、库或模块。为了能在浏览器中运行这些代码,我们需要将它们打包成一个或多个文件,比如我们平时打包出来的 `dist` 或 `build` 目录,这就是 webpack 的作用 🤷‍♂️

webpack 的主要功能包括:

  1. 模块打包:webpack 将应用程序的各个模块作为输入,通过解析模块之间的依赖关系,将它们打包成一个或多个静态资源文件。➡️ `pnpm run build`

  2. 资源转换:webpack 支持加载各种类型的文件,并且可以通过加载器(Loaders)对它们进行转换。比如,可以使用 Babel-loader 将 ES6/ES7 的 JavaScript 代码转换为浏览器可识别的 ES5 代码。➡️ loader 加载器

  3. 插件系统:webpack 提供了丰富的插件系统,开发者可以使用插件来扩展和定制打包过程。比如,可以使用 UglifyJS 插件来压缩 JavaScript 代码,或者使用 HtmlWebpackPlugin 插件生成 HTML 文件。➡️ plugin 插件

此外,webpack 还提供了许多优化功能,如代码压缩、代码拆分、懒加载等,以优化应用性能 🐂🍺

2. loader

loader,顾名思义,加载器。

webpack 只能理解 JavaScript 和 JSON 文件,这是 webpack 开箱即用自带的能力。loader 让 webpack 拥有能够去处理其他类型的文件都能力,并将它们转换为有效模块,以供应用程序使用,以及被添加到依赖图中。比如刚才提到的,可以使用 Babel-loader 将 ES6/ES7 的 JavaScript 代码转换为浏览器可识别的 ES5 代码。

🚩一句话概括:loader 就是协助 webpack 打包处理特定的文件模块。

在更高层面,在 webpack 的配置中,loader 有两个属性:

  1. test 属性,识别出哪些文件会被转换。
  2. use 属性,定义出在进行转换时,应该使用哪个 loader。
// webpack.config.js
const path = require('path');module.exports = {output: {filename: 'my-first-webpack.bundle.js',},module: {rules: [{ test: /\.txt$/, use: 'raw-loader' }],},
};

以上配置中,对一个单独的 module 对象定义了 rules 属性,里面包含两个必须属性:test 和 use。这告诉 webpack 编译器(compiler) 如下信息:

“嘿,webpack 编译器,当你碰到「在 require()/import 语句中被解析为 '.txt' 的路径」时,在你对它打包之前,先 use(使用) raw-loader 转换一下。” 🐒

是不是还挺简单的?去看文档!🙄️

下面简单看一下 webpack 常见的 loader 🤔

🚩 2.1 babel-loader

作用:将高级 JS 语法转化成低级语法 → 才能运行在 IE

webpack 只能打包处理部分高级 JS 语法,对于无法处理的需借助 babel-loader 打包,比如:

class Person {// 通过 static 关键字,为 Person 类定义了一个静态属性 info// webpack 无法打包处理“静态属性”这个高级语法static info = 'person info'
}// 安装 babel-loader 相关的包
npm i babel-loader @babel/core @babel/plugin-proposal-class-properties// 在 webpack.config.js 的 module -> rules 数组中,添加 loader 规则:
module: [rules: [{test: /.js$/,            // 匹配的文件类型exclude: /node_modules/, // 排除项use: {                   // 对应要调用的loaderloader: "babel-loader",options: { // 参数项// 声明一个babel插件,此插件用来转化class中的高级语法plugins:['@babel/plugin-proposal-class-properties']}}}]
]

🚩 2.2 ts-loader

作用:把 TS 转变成 JS,并提示类型错误

// 安装
// npm install ts-loader typescript --save-dev// 在 webpack.config.js 的 module -> rules 数组中,添加 loader 规则:
module: {rules: [// `.ts/.cts/.mts/.tsx` extension files will be handled by `ts-loader`{ test: /\.([cm]?ts|tsx)$/, loader: "ts-loader" }]
}

🚩 2.3 less/sass-loader、postcss-loader、css-loader、style-loader

  • less/sass-loader: 将 less/sass 转化成 css
  • postcss-loader: 优化 css (如:加前缀) → 最好放 css-loader 之前
  • css-loader: 将 css 转化成 JS 字符串
  • style-loader: 将 JS 字符串转化成 style 标签
// 安装 css 相关的 loader 的包
npm install style-loader css-loader less/sass-loader less/sass -D// 在 webpack.config.js 的 module -> rules 数组中,添加 loader 规则:
module: {rules: [{test : /.css$/,use : ['style-loader', 'css-loader', 'postcss-loader', 'less/sass-loader']} // 多个 loader 的调用顺序是:从后往前调用]
}

🤔 多个 loader 的调用顺序是从后往前的?其实不然,官方文档 有这样一段描述:

loader 总是 从右到左被调用。有些情况下,loader 只关心 request 后面的 元数据(metadata),并且忽略前一个 loader 的结果。在实际(从右到左)执行 loader 之前,会先 从左到右 调用 loader 上的 pitch 方法。

🚩 2.4 url-loader、file-loader

  • file-loader:一个简单的文件加载器,它会将源文件复制到输出目录,并返回文件的最终路径。它通常用于处理像图片、字体等文件类型,可以将这些文件复制到输出目录,并根据需要生成正确的 URL 地址供应用程序使用。

例如,在 webpack 配置中使用 file-loader 处理图片文件:

// 安装相关的 loader 的包
npm i file-loader// 在 webpack.config.js 的 module -> rules 数组中,添加 loader 规则:
module: {rules: [{test: /\.(png|jpg|gif)$/,use: [{loader: 'file-loader',options: {name: '[name].[hash].[ext]', // 输出文件名的格式outputPath: 'images/' // 输出文件的目录}}]}]
}

这个配置会将匹配到的图片文件复制到输出目录中的 images/ 目录,并生成一个对应的文件名。

  • url-loader:基于 file-loader 的封装,并增加了一些额外的功能。它可以根据文件大小将文件转换为 Data URL 或将其保留为文件,并返回相应的 URL 地址。这样做的好处是,对于小文件,可以将其转换为 Data URL,避免额外的网络请求,而对于大文件,则可以保留为文件。
// 安装相关的 loader 的包
npm i url-loader// 在 webpack.config.js 的 module -> rules 数组中,添加 loader 规则:
module: {rules: [{test : /.jpg|png|gif$/,use : { // 带参数项的 loader 可以通过对象的方式进行配置loader: "url-loader",options: {limit: 10240, // limit 指定图片的大小,单位是字节(byte)name: '[name].[hash].[ext]', // 输出文件名的格式outputPath: 'images/' // 输出文件的目录}} // 只有 <= limit大小的图片,才会被转为 base64格式的图片}   // 配了 url-loader 在配置里面就不要再给图片配 file-loader 了]     // 因为 url-loader 默认会使用 file-loader 来处理图片的路径关系的
}

🚩 2.5 svg-sprite-loader、svgo-loader

  • svg-sprite-loader:官方解释是:一个用于创建 svg 雪碧图的 Webpack 加载器。这个加载器现在已经被 JetBrains 公司收录和维护了。通俗的讲:svg-sprite-loader 会把你引入的 svg 塞到一个个 symbol 中,合成一个大的 svg,最后将这个大的 svg 放入 body 中。symbol 的 id 如果不特别指定,就是你的文件名。
  • svgo-loader:是基于 SVG Optimizer 的一个加载器,而 SVG Optimizer 是一个基于node.js 的工具,用于优化 SVG 矢量图形文件,它可以删除和修改SVG元素,折叠内容,移动属性等。
// 安装相关的 loader 的包
npm i svg-sprite-loader svgo-loader --dev// 在 webpack.config.js 的 module -> rules 数组中,添加 loader 规则:
module: {rules: [{test : /\.svg$/,use : [{ loader: 'svg-sprite-loader', options: {} },{ loader: 'svgo-loader', options: {plugins: [{name: 'removeAttrs', // 必须指定name!params: {attrs: 'fill'}}]}]}]
}

ps:对这个 loader 感兴趣的话可以参考 使用 svg-sprite-loader、svgo-loader 优化 svg symbols

3. plugin

plugin,顾名思义,插件。

通过安装和配置第三方插件,可以扩展 webpack 的能力,从而让 webpack 用起来更方便。

loader 用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。包括:打包优化,资源管理,注入环境变量。

🚩一句话概括:plugin 是用于扩展和定制 webpack  功能的工具。没用过浏览器插件吗?🤷‍♂️

想要使用一个插件,你只需要 require() 它,然后把它添加到 plugins 数组中。多数插件可以通过选项(option)自定义。你也可以在一个配置文件中因为不同目的而多次使用同一个插件,这时需要通过使用 new 操作符来创建一个插件实例。

// webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack'); // 用于访问内置插件module.exports = {module: {rules: [{ test: /\.txt$/, use: 'raw-loader' }],},plugins: [new HtmlWebpackPlugin({ template: './src/index.html' })],
};

在上面的示例中,html-webpack-plugin 为应用程序生成一个 HTML 文件,并自动将生成的所有 bundle 注入到此文件中。

是不是还挺简单的?去看文档!🙄️

下面简单看一下 webpack 常见的 plugin 🤔

一些常用的 Webpack 插件:

  1. HtmlWebpackPlugin:用于生成 HTML 文件,并将打包生成的资源文件自动注入到 HTML 文件中。

  2. MiniCssExtractPlugin:用于将 CSS 代码提取为独立的文件,而不是内联到 JavaScript 文件中。

  3. CleanWebpackPlugin:用于清理输出目录中的旧文件,以便在每次构建之前保持输出目录的干净。

  4. OptimizeCSSAssetsPlugin:用于优化和压缩 CSS 代码。

  5. DefinePlugin:用于定义全局常量,可以在应用程序的代码中直接使用。

可以根据官网给出的步骤配置插件:

const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');module.exports = {entry: './src/index.js',output: {path: 'dist',filename: 'bundle.js'},module: { rules: [ /* 添加 Loader 的规则 */ ] },plugins: [new HtmlWebpackPlugin({template: './src/index.html'}),new MiniCssExtractPlugin({filename: 'styles.css'}),new CleanWebpackPlugin()]
};

4. loader vs plugin

如果看完上面的解释,还是不知道 webpack 的 loader 和 plugin 的区别的话,那我们举个 🌰

假如你是一名厨师,你有一些食材(模块文件)需要处理,并且需要一些工具来做完这道菜。

🚩 Loader 就像你的各种厨房工具。例如,切菜刀、搅拌器、炉灶等,这些工具帮助你对食材进行加工和转换,以便制作出美味的菜肴。在 webpack 中 loader 的作用也是一样的,它们负责将不同类型的文件进行处理和转换,比如:将 ES6 代码转换为 ES5 代码,将 CSS 文件转换为浏览器可识别的样式等。

🚩 Plugin 则像你的特殊调料和烹饪技巧。假设你想给菜肴增添特殊的风味或实现特定的效果。你可能会使用辣椒酱增加辣味,柠檬汁增添酸味,或者使用烘烤技巧让菜表面金黄酥脆。在 webpack 中 plugin 的作用也是一样的,它们可以在构建过程中监听事件,并执行一些特殊的操作。例如,你可以使用 HtmlWebpackPlugin 生成一个带有引入资源的 HTML 文件,使用 UglifyJSPlugin 压缩和混淆 JavaScript 代码,或者使用 ExtractTextPlugin 将 CSS 提取为独立的文件。

总结来说,loader 是用于处理和转换文件的工具,类似于厨房中的各种工具,而 plugin 则是用于扩展和定制构建过程的工具,类似于特殊的调料和烹饪技巧。它们共同协作,使得 Webpack 能够处理各种文件类型、进行模块化开发,并通过插件机制进行灵活的定制和优化。🎉

5. 自己写一个 plugin

Webpack 插件就是一个 JavaScript 对象,通过扩展或修改 webpack 的功能来实现特定的构建需求。它可以在 webpack 的构建过程中干预并做出相应的处理。基本的 webpack 插件结构如下:

class MyPlugin {constructor(options) {// 在构造函数中接收插件的配置选项this.options = options;}apply(compiler) {// 在 apply 方法中定义插件的逻辑// compiler 对象代表了完整的 webpack 环境配置// 可以通过 compiler 对象来访问 webpack 的各种钩子函数// 注册钩子函数,以在 webpack 构建过程中执行特定操作compiler.hooks.someHook.tap('MyPlugin', () => {// 在这里执行你的插件逻辑});}
}

👆这是一个最基本的 webpack 插件结构示例,webpack 插件的结构包括一个 apply 方法和一些钩子函数。apply 方法在插件被应用时被调用,接受一个 compiler 参数,该参数代表了完整的 webpack 环境配置。通过 compiler 对象,插件可以访问 webpack 的各种钩子函数并注册自己的逻辑。

钩子函数是 webpack 在构建过程中的特定时间点触发的函数。插件可以根据需求选择合适的钩子函数,并在这些函数中执行自定义的逻辑。例如,在构建开始前可以使用 beforeRunrun 钩子,在构建完成后可以使用 done 钩子。

下面是一些常用的 webpack 钩子函数:

  • beforeRun:在 webpack 构建启动之前执行。
  • run:在开始构建之前执行。
  • beforeCompile:在编译之前执行。
  • compile:在开始编译之前执行。
  • compilation:在每次新的编译创建之前执行。
  • emit:在生成资源并输出到输出目录之前执行。
  • afterEmit:在资源输出到输出目录之后执行。
  • done:在构建完成时执行。
  • 不止这些钩子吧?去看文档!🙄

插件可以使用这些钩子函数来执行各种任务,如修改、添加、删除资源,生成额外的文件,提取公共代码,优化输出等等。

OK,举个 🌰

假如我们比较关心项目在构建完成后产出的文件的路径和大小,go!

const fs = require('fs');class MyPlugin {constructor(options) {this.options = options;}apply(compiler) { // 构建时只会执行一次compiler.hooks.done.tap('MyPlugin', (stats) => {const outputPath = statspilation.outputOptions.path;const outputFileName = statspilation.outputOptions.filename;const filePath = `${outputPath}/${outputFileName}`;const fileSize = fs.statSync(filePath).size;console.log(`Built file: ${filePath}`);console.log(`File size: ${fileSize} bytes`);});}
}

在上述示例中,我们在 webpack 构建完成后的 done 钩子中获取构建输出文件的路径和大小,并将其输出到控制台。

🚩 为什么说 apply 只会执行一次?🤔

我在实践手写这个插件的时候,为了调试,在 apply 内部写了一个日志 log,我发现日志仅在第一次编译的时候可以执行,在热更新的时候,日志并没有打印,这是怎么回事?

还是刚才那个例子,apply 方法相当于你炒菜的点火动作,具体到了什么时机以及我们需要具体做什么,是需要提前注册具体的逻辑的。这里为了方便记忆,你也可以类比成 dom 的 addEventListener,具体的注册动作只发生一次,但是监听到事件之后是每次都会执行的!

最后,要使用此插件,还需要在 webpack 配置文件中引入并实例化它

const MyPlugin = require('./path/to/MyPlugin');module.exports = {// ...其他配置项plugins: [new MyPlugin({// 插件的配置选项})]
};

end 🎉

更多推荐

浅谈一下 webpack 以及 loader 和 plugin

本文发布于:2024-02-14 03:03:08,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1761799.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:浅谈   webpack   plugin   loader

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!