浅谈一下 webpack 以及 loader 和 plugin

news2024/11/16 17:45:05

话说,前端练习时长也快两年了,但是关于 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: {                   // 对应要调用的loader
				loader: "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 的调用顺序是:从后往前调用
	]
}

🚩 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 = stats.compilation.outputOptions.path;
      const outputFileName = stats.compilation.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 钩子中获取构建输出文件的路径和大小,并将其输出到控制台。

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

const MyPlugin = require('./path/to/MyPlugin');

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

end 🎉

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/737883.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

5G工业路由器赋能无人驾驶技术发展,无线车联网应用方案

随着无人驾驶技术进入大众视野&#xff0c;5G技术已广泛应用在各行各业中&#xff0c;无人驾驶也成了汽车自动化发展的核心趋势。无人驾驶技术需要满足低时延、高效率、大带宽、稳定性等硬性要求&#xff0c;工业路由器IR2730采用5G网络快速接入&#xff0c;拥有广泛连接能力&a…

appium 笔记

配置相关信息查看 appium.io 初始化&#xff1a; desired_caps {} desired_caps["automationName"] "UiAutomator2" desired_caps["platformName"] "Android" # 操作系统类型 desired_caps["platformVersion"] "5…

[入门必看]数据结构6.2:图的存储及基本操作

[入门必看]数据结构6.2&#xff1a;图的存储及基本操作 第六章 图6.2 图的存储及基本操作知识总览6.2.1 邻接矩阵法6.2.2 邻接表法6.2.36.2.4 十字链表、邻接多重表6.2.5 图的基本操作 6.2.1 邻接矩阵法图的存储——邻接矩阵法邻接矩阵法存储带权图&#xff08;网&#xff09;邻…

OpenAI再建顶级团队,重金招聘核心岗成员,阻止超级AI的威胁!

“AI确实可能会杀死人类。”这并不是哪家自媒体为博人眼球而发表的谣言&#xff0c;而是ChatGPT之父、OpenAI CEO山姆奥尔特曼在今年3月与MIT研究科学家Lex Fridman对话时亲口所说的一句话。 如今&#xff0c;这位全世界最受瞩目的老板决定采取行动&#xff0c;以阻止自己的预…

【算法】AcWing算法基础课笔记 第一章 基础算法 Part 1

&#x1f680;Write In Front&#x1f680; &#x1f4dd;个人主页&#xff1a;令夏二十三 &#x1f381;欢迎各位→点赞&#x1f44d; 收藏⭐️ 留言&#x1f4dd; &#x1f4e3;系列专栏&#xff1a;AcWing &#x1f4ac;总结&#xff1a;希望你看完之后&#xff0c;能对你…

Python程序设计基础:列表与元组(一)

文章目录 一、列表的表示与访问1、列表的表示2、列表的访问 二、列表的元素操作1、元素的修改2、元素的增加3、元素的删除4、其他操作 三、列表操作1、列表的遍历2、列表的排序3、列表的切片4、列表的扩充5、列表的复制6、列表的删除 一、列表的表示与访问 1、列表的表示 列表…

自营外卖配送平台的商家如何对接第三方美饿的订单

自营外卖跑腿平台对接第三方美饿的好处 单说美团饿了么自身的流量优势&#xff0c;很多商家不能忽视&#xff0c;但是美团饿了么的高额配送成本与抽成&#xff0c;同样也不能忽视。很多商家希望选择自配送或者其他更划算的配送方式来节省成本。这时&#xff0c;区域性的自建外…

【Java】单例模式

单例模式 设计模式概述单例模式实现思路饿汉式懒汉式饿汉式 vs 懒汉式 设计模式概述 设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格、以及解决问题的思考方式。设计模式免去我们自己再思考和摸索。就像是经典的棋谱&#xff0c;不同的棋局&#xff0c;我…

AttributeError: module ‘collections‘ has no attribute ‘Iterable‘

vitables 出现 AttributeError: module collections has no attribute Iterable错误修改如下图&#xff1a;

PCIE转PMC载板

描述 这是一款被动适配卡&#xff0c;它支持通过要转换为 PCIe x4 通道边缘连接器的 AMC 接头&#xff0c;来选择 TI EVM&#xff1b;因此它可被插入一个台式计算机或使用 PCIe 接头的任何位置。选定的 TI EVM 支持 DSP 上的本地 PCIe。此卡作为适配器&#xff0c;需要可与 AMC…

Linux与Windows:操作系统的比较与技巧分享

&#x1f337;&#x1f341; 博主 libin9iOak带您 Go to New World.✨&#x1f341; &#x1f984; 个人主页——libin9iOak的博客&#x1f390; &#x1f433; 《面试题大全》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33…

Java 动态规划 Leetcode 62. 不同路径

代码展示&#xff1a; class Solution {public int uniquePaths(int m, int n) {//定义dp数组//二维数组多增加一行一列&#xff0c;方便对数组进行初始化int[][]dpnew int[m1][n1];//初始化dp[0][1]1;//填充数组for(int i1;i<m;i){for(int j1;j<n;j){dp[i][j]dp[i-1][j…

@Around踩坑记录

Around踩坑记录 先上结论&#xff1a; 如果你在定义切面的时候打算使用环绕通知➕定义注解的方式&#xff0c;那么在进行*Around*("(annotation(costTrace))") 类似这样的定义的时候&#xff0c;”costTrace“一定要与你定义切面中入参的参数名字一致&#xff0c;而…

【嵌入式Qt开发入门】Qt如何使用多线程——初识QThread

我们写的一个应用程序&#xff0c;应用程序跑起来后一般情况下只有一个线程&#xff0c;但是可能也有特殊情况。比如我们前几篇的例程都跑起来后只有一个线程&#xff0c;就是程序的主线程。线程内的操作都是顺序执行的。但试着想一下&#xff0c;我们的程序顺序执行&#xff0…

模拟日志数据

模拟日志数据。。 日志模拟器主要模拟站点的用户信息和用户行为日志数据 使用shell调用java类实现数据的生产 ! 所需文件如下: datagen.jar genlog.sh 注意: 存储数据的文件夹要预先创建好 Bash mkdir -p /tmp/doit/user_data mkdir -p /tmp/doit/log_data 将用来模拟生成…

【安全】Xsslabs(1~13)基于白盒测试浅析

目录 环境 关卡 level 1 level 2 level 3 level 4 level 5 level 6 level 7 level 8 扩展 level 9 level 10 level 11 level 12 level 13 总结 环境 PHP&#xff1a;php7.3.4nts 中间件&#xff1a;Nginx1.15.11 工具&#xff1a;Hackbar 关卡 level …

超长上下文处理:基于Transformer上下文处理常见方法梳理

原文链接&#xff1a;芝士AI吃鱼 目前已经采用多种方法来增加Transformer的上下文长度&#xff0c;主要侧重于缓解注意力计算的二次复杂度。 例如&#xff0c;Transformer-XL通过缓存先前的上下文&#xff0c;并允许随着层数的增加线性扩展上下文。Longformer采用了一种注意力…

【GLBCXX_3.4.21 not found】问题解决

问题描述 rootlocalhost:/home# rar -h rar&#xff1a;/lib64/libstdc.so.6 ,version "GLBCXX_3.4.21" not found 问题排查过程 下载相应的GCC源码&#xff0c;找到适合自己版本&#xff0c;这里选择是的是7.3.0 gcc源码下载地址&#xff1a;http://ftp.gnu.org/g…

fastadmin数据内容admin_id权限

/*** 是否开启数据限制* 支持auth/personal* 表示按权限判断/仅限个人* 默认为禁用,若启用请务必保证表中存在admin_id字段*/protected $dataLimit true;/*** 数据限制字段*/protected $dataLimitField admin_id;

第一次上传GitHub代码------教程、记录和踩雷

刚录用的一篇论文&#xff0c;编辑要求上传GitHub repo。 对于上传代码&#xff0c;本人是一无所知&#xff0c;写这篇博客记录一下 一. 创建代码仓库 点击右上角&#xff0c;选择New repository&#xff0c;来创建仓库&#xff1a; 根据提示&#xff0c;填写仓库的名称、描述…