基于 webpack 5 实现自定义 loader

2023-10-29

前面的话

基于 webpack 5 创建自定义 同步、异步loader,在此基础上实现一个简易的渲染 markdown 的 loader 和 合成雪碧图的 loader。

代码地址: 自定义loader

准备工作

我们先创建一个 webpack 项目。

初始化项目

新建一个文件夹 create-loader,在该目录执行以下命令来完成初始化工作。

npm init -y

安装依赖

安装 webpack 、webpack-cli 以及webpack-dev-server。前两者打包必备;后者是一个提供热更新的开发服务器,对开发阶段友好。

pnpm add webpack webpack-cli webpack-dev-server --save-dev

创建入口文件

创建 src 目录,并创建 index.js 入口文件。

// index.js
document.write('hello wp')

创建 webpack 配置文件 webpack.config.js

虽然 webpack v4 + 开箱即用,可以无需配置文件就可以打包,但 webpack 会默认打包入口文件为 src/index.js 文件,打包产物为 dist/main.js ,并且开启生产环境的压缩和优化。

但大多数的项目还是需要一些复杂的配置,配置文件 webpack.config.js 文件还是很有必要的 。 webpack 在打包时会自动识别这个文件,根据里面的配置来进行打包。

const path = require('path')

module.exports = {
  mode: 'development', // 以什么模式进行打包
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'), // 打包后的代码放在dist目录下
    filename: 'bundle.js', // 文件名为 bundle.js
  },
  devServer: {
    static: './dist'
  }
}

Tips: 如果我们想更改为指定的配置文件 prod.config.js 来打包,可以使用--config 标志来修改。

"scripts": {
  "build": "webpack --config prod.config.js"
}

添加 npm script

我们可以在 package.json 文件中创建快捷方试来启动开发和打包。

{
// ...省略
"main": "src/index.js", // 修改入口文件
 "scripts": {
    "dev": "webpack serve --open",
    "build": "webpack"
  },
  // ...省略
} 

这样可以通过 npm run dev 来启动项目:
在这里插入图片描述使用 npm run build 来打包项目:

在这里插入图片描述

可以看到生成了一个 dist 目录,里面有一个 bundle.js 文件。

创建 index.html 文件

上面打包的产物只有一个js 文件,我们想要使用浏览器访问里面的内容就要手动创建一个 html 文件,并引入 bundle.js 脚本文件,使用浏览器打开即可。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <script src="./bundle.js"></script>
</body>
</html>

以上的步骤简单的完成了一个基于 webpack 打包的最简单例子。

实现简单自定义的 loader

为什么需要 loader

webpack 本身只能识别 js 和 json 文件,对于 css、ts 等其他文件就需要 loader 对齐进行处理,转换为 webpack 能识别的模板。

loader 是什么

loader 是一个导出为函数的 JavaScript 模块,用于对模块的源码进行转换。形如:

moudle.exports = (source) => {
// 按照自己的转换要求进行处理
	return source
}

loader 原则

  • 单一: 一个loader 只做一件事。
  • 链式调用:从右往左依次调用。
  • 模块化:保证输出的是模块化。
  • 无状态: 每一个 loader 的运行相对独立,不与其他 loader
  • loader-utils 工具库: 提供了很多工具

更多原则参考官方文档 loader 原则

实现一个简单的同步loader

在上面例子的基础上我们来编写一个简单的字符串替换 loader、

创建同步 loader

在 src 下创建 xq-loader 目录,创建 my-loader.js 文件。

// my-loader.js
module.exports = function (source) {
  // 在这里按照你的需求处理 source
  // 可以通过 this.getOptions 或者 this.query 来获取参数
  const options = this.getOptions()
  console.log(options)
  return source.replace('wp', 'xiaoqi') 
}

// 或者 使用this.callback 
module.exports = function (source) {
  // 在这里按照你的需求处理 source
  // 可以通过 this.getOptions 或者 this.query 来获取参数
  const options = this.getOptions()
  const result = source.replace('wp', 'xiaoqi')
  this.callback(null, result)
}

这里编写的是一个简单的同步loader,我们可以通过 return 一个表示已转换模块的单一值。如果情况比较复杂,也可以通过 this.callback(err,values…) 这个回调函数来返回转换后的结果。

this.callback(
// 转换异常时,抛出错误
err: Error || null,
// 转换后的结果
content: string | Buffer,
// 用于把转换后的内容得出原内容的 Source Map,方便调试
sourceMap?: SourceMap,  
)

使用 loader

在 webpack.config.js 中配置 loader 有两种方式。一种是 path.resolve ,另一种是 ResolveLoader。

  • 使用 path.resolve 指定 loader 的文件路径

    // webpack.config.js
    {
      // ...省略
      module: {
        rules: [
          {
            test: /\.js$/,
            use: [
              {
                loader: path.resolve(__dirname, './src/xq-loader/my-loader.js'),
                options: {
                  name: 'xiaoqi',
                },
              },
            ],
          },
        ],
      }
    }
    
  • 使用 ResolveLoader

    {
     module: {
        rules: [
          {
            test: /\.js$/,
            use: ['my-loader'],
          },
        ],
      },
      resolveLoader: {
        // webpack 将会从这些目录中依次搜索 loader,
        modules: ['node_modules', './src/xq-loader'],
      },
    }
    

运行 npm run dev 可以看到 wp 被替换为 xiaoqi 。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8VU9wwcW-1659181612266)(/Users/wanmian/Library/Application Support/typora-user-images/image-20220703103708300.png)]

实现一个异步 loader

在某些耗时久的场景下,比如处理网络请求的结果,我们可以使用 异步 loader ,这样不会阻塞整个构建。

创建 async.txt 文件

在 src 目录下创建 async.txt 文件,随便写点内容。
在这里插入图片描述

创建异步 loader

在 src 下创建 xq-loader 目录,创建 my-async-loader.js 文件。该 loader 的功能为读取 async.txt 中的内容并返回。

// my-async-loader.js
const fs = require('fs')
const path = require('path')

module.exports = function (source) {
  // 通过 this.async 来返回一个异步函数,第一个参数 Error, 第二个参数是处理的结果。
  const callback = this.async()
  fs.readFile(path.join(__dirname, '../async.txt'), 'utf-8', (err, data) => {
    const html = `module.exports = ${JSON.stringify(data)}`
    callback(null, html)
  })
}

添加 loader 配置

在 webpack.config.js 文件中的 module.rules 下添加 my-async-loader。

// webpack.config.js
// ...
{
  test: /\.txt$/,
  use: {
  loader: 'my-async-loader',
  },
},
// ...

引入 txt 文件

在 index.js 入口文件中引入。

// index.js
import txt from './async.txt'
document.write('hello wp')
document.write(`</br>异步loader: ${txt}`)

运行 || 打包

npm run dev 可以看到 async.txt 文件下的内容被打印出来。
在这里插入图片描述

实现一个渲染 markdown 的 loader

简易版 mark-loader,借助 markdown-it 库的能力。

创建 md 文件

首先我们在 src 下创建一个 md 文件。
在这里插入图片描述

编写 mark-loader

在 src 下创建 xq-loader 目录,创建 mark-loader.js 文件。

// mark-loader.js
const MarkdownIt = require('markdown-it')
module.exports = function (source) {
  const options = this.getOptions()
  const md = new MarkdownIt({
    html: true,
    ...options,
  })

  let html = md.render(source)
  // webpack 无法直接去解析html模板,所以要返回一段 js 代码
  html = `module.exports = ${JSON.stringify(html)}`
  this.callback(null, html)
}

添加配置 loader

// webpack.config.js
{
  test: /\.md$/,
  use: [
    {
    loader: 'mark-loader',
    },
  ],
},

引入 md 文件

创建一个 div ,将生成的 html 插入其中。

import txt from './async.txt'
import md from './mk.md'

document.write('hello wp')
document.write(`</br>异步loader: ${txt}`)

const div = document.createElement('div')
div.innerHTML = `${md}`
document.body.appendChild(div)

运行 || 打包

npm run dev 运行,可以看到 md 文件以html 的形式渲染出来。
在这里插入图片描述

实现一个生成雪碧图的 loader

简易版 sprite-loader,借助 spritesmith 库的能力。

创建 css 文件

首先我们在 src 下创建一个 css 文件,以 ?__sprite 来标志是需要合成的图片。
在这里插入图片描述

创建 sprite-loader

找到需要合成的图片,组合生成一个数组,再使用 spritesmith 的能力将多张图合并成一张图。

const path = require('path')
const fse = require('fse')
const Spritesmith = require('spritesmith')

module.exports = function (source) {
  const callback = this.async()
  // 匹配 url(开头 ?__sprite 结尾的
  const imgs = source.match(/url\((\S*)\?__sprite/g)
  const matchImgs = []
  for (let i = 0; i < imgs.length; i++) {
    // 解析出图片路径
    const img = imgs[i].match(/url\((\S*)\?__sprite/)
    matchImgs.push(path.join('./src', img[1]))
  }
  Spritesmith.run({ src: matchImgs }, (err, result) => {
    // 将合成的图片写入到 src/images/sprite.jpg 中
    fse.writeFileSync(path.join(process.cwd(), 'src/images/sprite.jpg'), result.image)
    // 替换原引入为合成图片的路径
    source = source.replace(/url\((\S*)\?__sprite/g, () => {
      return `url('./images/sprite.jpg'`
    })
    callback(null, source)
  })
}

添加 loader 配置

由于 webpack 没法识别 css 文件,这里先用 sprite-loader 合成图片;再调用 css-loader,它会帮我们对 @importurl() 进行处理,就像 js 解析 import/require() 一样;最后调用 style-loader 帮我们将 css 代码以 <style>的形式插入到 DOM 中。

// webpack.config.js
{
    test: /\.css$/,
    use: ['style-loader', 'css-loader', 'sprite-loader'],
},

引入 css 文件

在 index.js 中引入 css 文件,给上述的 div 加上类名 img2。

import './index.css'
import txt from './async.txt'
import md from './mk.md'
// ...
const div = document.createElement('div')
div.className = 'img2'
div.innerHTML = `${md}`
document.body.appendChild(div)

运行 || 打包

可以看到背景图生效了,并且在 dist 下会生成一个图片文件。
在这里插入图片描述

在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

基于 webpack 5 实现自定义 loader 的相关文章

随机推荐

  • [Ctf show web]萌新计划9

    system exec highlight 等于or 题目要求我们利用 system exec highlight才能执行eval preg match a b 函数可以返回 a匹配次数 它的值将是 0 次 不匹配 或 1 次 因为 pre
  • ※机器学习函数调用/绘图/读取文件/分析常用代码总结

    机器学习函数调用代码 决策树 from sklearn tree import DecisionTreeRegressor 随机森林 from sklearn ensemble import RandomForestClassifier 线
  • [数据可视化]使用matplotlib对CSV文件数据进行绘图

    数据可视化 使用matplotlib对CSV文件数据进行绘图 使用CSV格式的锡特卡天气数据 绘制2018年1月1日的天气数据 先放成果 代码部分包括以下内容 分析CSV文件头 打印文件头及其位置 提取并读取数据 绘制温度图表 模块date
  • redis 配置文件详解

    Redis 配置文件 当配置中需要配置内存大小时 可以使用 1k 5GB 4M 等类似的格式 其转换方式如下 不区分大小写 1k gt 1000 bytes 1kb gt 1024 bytes 1m gt 1000000 bytes 1mb
  • Qt篇——QTcpSocket设置连接超时时间

    QTcpSocket默认是没有函数接口设置超时时间的 使用以下方法可以给socket设置超时时间 QNetworkConfigurationManager manager QNetworkConfiguration config manag
  • 机器学习单变量线性回归

    1 模型概述 给出一些带有标签的数据 即带有 正确答案 的数据 用y ax b 的形式去拟合数据 线性 单变量 大致过程如下 给出训练集 带有标签的数据 通过学习算法选择不错的参数 theta0 theta1 得到假设函数h 从x到y 的映
  • 数据库之分库分表-垂直?水平?

    一 数据库瓶颈 不管是IO瓶颈 还是CPU瓶颈 最终都会导致数据库的活跃连接数增加 进而逼近甚至达到数据库可承载活跃连接数的阈值 在业务Service来看就是 可用数据库连接少甚至无连接可用 接下来就可以想象了吧 并发量 吞吐量 崩溃 1
  • 小智ai:ChatGPT3主要功能这些功能可以说明什么问题?

    ChatGPT3在自然语言处理领域中的强大能力 尤其是在文本生成和语言模型训练方面 它可以模拟人类的语言能力 理解人类的意图并生成自然的回复和文本 ChatGPT3的语言翻译和语音转文本功能也使得跨语言交流和语音交互变得更加容易 它可以自动
  • QQ取色器功能

    一 使用方法 QQ截图功能具有取色器功能 二 截取颜色三原色 1 截图 Ctral ALT A 2 找到取色目标 3 按 C 键 4 CTRL V 5 得到颜色三原色 255 255 255 三 截取十六进制颜色 1 截图 Ctral AL
  • 第三方yum源仓库搭建

    作用 第三方软件是yum仓库里没有的软件 如果从网上下载得到了软件 用yum还是不能安装 因为会有依赖性 解决依赖性非常麻烦 但是如果搭建了第三方软件仓库 yum就会自己解决这个问题 下载 浏览器搜索 sourceforge 源码安装包 r
  • 爬虫 第五讲 多线程爬虫

    文章目录 爬虫 第五讲 多线程爬虫 一 多线程 1 多线程基本介绍 2 主线程和子线程的执行关系 3 查看线程数量 4 验证子线程的执行与创建 5 继承Thread类创建线程 6 线程间的通信 多线程共享全局变量 7 线程间的资源竞争 二
  • 什么是Service, 以及Service 模板

    Service本质就是一个驻留Process 驻留Prcess至少有一个驻留线程 这个线程处在waiting的状态 相对于Runable 控制流停在某个点上 等待外部事件驱动 或者是自己的timer驱动 Windows Service 是一
  • 十行代码,我用Python做一个迷你版的美图秀秀!

    美图秀秀相信大家都不陌生 大家只要操作美图秀秀 就可以P掉图片中脸上的一些瑕疵 让人变得更加的美丽 今天小编就带领大家来借助Python和Flask来实现一个美图秀秀的网页设计 大家只需要通过网页上传需要美颜的图片 然后就可以从网页下载美颜
  • linux-shell是什么

    由于安全 复杂 繁琐等原因 用户不能直接解除内核 需要一个程序来接收用户的操作 进行处理后再传给内核 这一过程不仅做到了简化 同时也保护了内核 说白了Shell就是用来解释用户命令的 linuxshell可以看成一种脚本语言 高级语言能做到
  • 华为OD机试真题-匿名信C++实现【2023.Q1】

    题目内容 电视剧 分界线 里面有一个片段 男主为了向警察透露案件细节 且不暴露自己 于是将报刊上的字减下来 剪拼成匿名信 现在有一名举报人 希望借鉴这种手段 使用英文报刊完成举报操作 但为了增加文章的混淆度 只需满足每个单词中字母数量一致即
  • 取消springsecurity默认的登录验证

    取消springsecurity默认的登录验证 问题描述 解决方法一 方法二 问题描述 springboot 2 x 访问swagger ui html时 会自动跳转到springsecurity的login页 自定义过滤路径的拦截器无效
  • 详解FreeRTOS中的软件定时器

    软件定时器用于让某个任务定时执行 或者周期性执行 比如设定某个时间后执行某个函数 或者每隔一段时间执行某个函数 由软件定时器执行的函数称为软件定时器的回调函数 参考资料 Mastering the FreeRTOS Real Time Ke
  • 公司企业怎么做一个网站?

    怎么做一个网站对于一些实体的公司企业来说是个需要了解的问题 由于实体公司企业大部分情景下都是线下面谈业务 所以他们一开始并没有搭建自己的公司企业网站 而到了现在逐渐发展的阶段 就开始需要公司企业网站来开拓更多的客户资源或是提高公司企业的曝光
  • 这是一套基于THINKPHP6+SCUI+VUE2.6开发的CRM客户管理系统

    这是一套基于THINKPHP6 SCUI VUE2 6开发的CRM客户管理系统 演示 http v2 antsys cn 用户名admin 密码123456
  • 基于 webpack 5 实现自定义 loader

    前面的话 基于 webpack 5 创建自定义 同步 异步loader 在此基础上实现一个简易的渲染 markdown 的 loader 和 合成雪碧图的 loader 代码地址 自定义loader 准备工作 我们先创建一个 webpack