浅谈一下 webpack 以及 loader 和 plugin 您所在的位置:网站首页 etl的主要功能概括 浅谈一下 webpack 以及 loader 和 plugin

浅谈一下 webpack 以及 loader 和 plugin

2023-07-11 03:29| 来源: 网络整理| 查看: 265

话说,前端练习时长也快两年了,但是关于 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 的主要功能包括:

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

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

插件系统: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 有两个属性:

test 属性,识别出哪些文件会被转换。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 转化成 csspostcss-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/' // 输出文件的目录}} // 只有 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 插件:

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

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

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

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

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 在构建过程中的特定时间点触发的函数。插件可以根据需求选择合适的钩子函数,并在这些函数中执行自定义的逻辑。例如,在构建开始前可以使用 beforeRun 或 run 钩子,在构建完成后可以使用 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 钩子中获取构建输出文件的路径和大小,并将其输出到控制台。

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

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

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

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

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

end 🎉



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有