如何编写一个 Webpack Loader的实现


Posted in Javascript onOctober 18, 2020

前言

在平时自己由零搭建项目时,虽然基础配置都比较熟悉,比如配置 file-loader, url-loader, css-loader 等,配置不难,但究竟是怎么起作用的呢,今天就来说说如何编写一个 Webpack Loader。

Loader 作用

按我自己的简单理解,loader 通常指打包的方案,即按什么方式来处理打包,打包的时候它可以拿到模块源代码,经过特定 loader 的转换后返回新的结果。

比如 sass-loader 可以把 SCSS 代码转换成 CSS 代码

编写 Loader

保持功能单一

我们项目中可能会配置很多,但要记住,要保持一个 Loader 的功能单一,避免做多种功能,只需完成一种功能转换即可。

所以如 less 文件转换成 css 文件,也不是一步到位,而是 less-loader, css-loader, style-loader 几个 loader 的链式调用才能完成转换。

模块

因为 Webpack 本身是运行在 Node.js 之上的,一个 loader 其实就是一个 node 模块,这个模块导出的是一个函数,即:

module.exports = function (source) {
 // source 为 compiler 传递给 Loader 的一个文件的原内容
 // 处理...
 return source // 需要返回处理后的内容
}

这个导出的函数的工作就是获得处理前的原内容,对原内容执行处理后,返回处理后的内容。

替换字符串的 loader

比如我们打包时,想要替换源文件的字符串,这时可以考虑使用 Loader,因为 loader 就是获得源文件内容然后对其进行处理,再返回。

比如 src 目录下有三个文件:

src/msg1.js

export const msg1 = '学习框架'

src/msg2.js

export const msg2 = '深入理解JS'

src/index.js

import { msg1 } from './msg1'
import { msg2 } from './msg2'

function print() {
 console.log(`输出:${msg1}, ${msg2}`)
}

print()

做的事情则是把 msg1 和 msg2 两个文件导入,然后输出两个字符串。

我们要做的事也很简单,把"框架"转为"React 框架", "JS"转为"JavaScript"。

新建 src/loaders/replaceLoader.js文件,

module.exports = function (source) {
 const handleContent = source.replace('框架', 'React框架').replace('JS', 'JavaScript')
 return handleContent
}

就这样,loader 写完了!!!

上面我们讲到,source 是源文件内容,如果打印的话,则是:

如何编写一个 Webpack Loader的实现

使用 Loader

接下来,我们要来使用它,在根目录下新建文件 webpack.config.js

const path = require('path')

module.exports = {
 mode: 'production',
 entry: './src/index.js',
 module: {
  rules: [
   {
    test: /\.js$/,
    use: './src/loaders/replaceLoader.js',
   },
  ],
 },
 output: {
  path: path.resolve(__dirname, 'dist'),
  filename: '[name].js',
 },
}

执行npx webpack, 查看打包结果dist/main.js

(()=>{"use strict";console.log("输出:学习React框架, 深入理解JavaScript")})();

替换成功!

需要注意的是,use里面填写的 loader 是去node_modules目录里面找的,由于我们是自定义的 loader,所以不能直接写use: 'replaceLoader',但直接写路径的方式未免难看点,我们可以通过 webpack 来配置:

module.exports = {
 resolveLoader: {
  modules: ['node_modules', './src/loaders'], // node_modules找不到,就去./src/loaders找
 },
 module: {
  rules: [
   {
    test: /\.js$/,
    use: 'replaceLoader',
   },
  ],
 },
}

获取 loader 的 options

写完之后,让我们来想想,其实就是写一个功能函数嘛。

当然,这只是最简单的例子,如果 loader 可以传入参数呢,比如:

module: {
 rules: [
  {
   test: /\.js$/,
   use: {
    loader: 'replaceLoader',
    options: {
     params: 'replaceString',
    },
   },
  },
 ],
},

这个时候可以使用this.query来获取,通过this.query.params就能拿到,这里需要注意的是,this 上下文是有用的,所以这个 loader 导出函数不能是箭头函数。

但 webpack 更推荐loader-utils模块来获取,它提供了许多有用的工具,最常用的一种工具是获取传递给 loader 的选项。

首先要安装

npm i -D loader-utils

修改src/loaders/replaceLoader.js

const { getOptions } = require('loader-utils')

module.exports = function (source) {
 console.log(getOptions(this)) // { params: 'replaceString' }
 console.log(this.query.params) // replaceString
 const handleContent = source.replace('框架', 'React框架').replace('JS', 'JavaScript')
 return handleContent
}

这里需要注意的是,getOptions(this)参数传入的是 this,也就是说

打印结果:

{ params: 'replaceString' }
{ params: 'replaceString' }
{ params: 'replaceString' }

this.callback()

上面都是返回原来内容转换后的内容,但有些场景下还需要返回其他东西比如 sourceMap

module.exports = function (source) {
 // 告诉 Webpack 返回的结果
 this.callback(null, source, sourceMaps)
}

另外也不需要 return 了,所以也可使用此 API 替代 return

const { getOptions } = require('loader-utils')

module.exports = function (source) {
 const handleContent = source.replace('框架', 'React框架').replace('JS', 'JavaScript')
 this.callback(null, handleContent)
}

自定义 loader 应用场景

在所有 function 外面加一层 try catch 代码块捕获错误,避免手动繁琐添加。
实现中英文替换:可以将文字用占位符如{{ title }}包裹,检测到占位符则根据环境变量替换为中英文。

到此这篇关于如何编写一个 Webpack Loader的实现的文章就介绍到这了,更多相关Webpack Loader实现内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Javascript 相关文章推荐
网页开发中的容易忽略的问题 javascript HTML中的table
Apr 15 Javascript
setTimeout与setInterval在不同浏览器下的差异
Jan 24 Javascript
jquery插件实现鼠标经过图片右侧显示大图的效果(类似淘宝)
Feb 04 Javascript
JS注册/移除事件处理程序(ExtJS应用程序设计实战)
May 07 Javascript
js判断为空Null与字符串为空简写方法
Feb 24 Javascript
一起学写js Calender日历控件
Apr 14 Javascript
JS组件Bootstrap实现弹出框效果代码
Apr 26 Javascript
Angularjs在初始化未完毕时出现闪烁问题的解决方法分析
Aug 05 Javascript
详解Javascript ES6中的箭头函数(Arrow Functions)
Aug 24 Javascript
bootstrap响应式表格实例详解
May 15 Javascript
Vue-cli3.X使用px2 rem遇到的问题及解决方法
Aug 08 Javascript
three.js利用卷积法如何实现物体描边效果
Nov 27 Javascript
JavaScript中EventBus实现对象之间通信
Oct 18 #Javascript
vuex刷新后数据丢失的解决方法
Oct 18 #Javascript
JavaScript大数相加相乘的实现方法实例
Oct 18 #Javascript
vue任意关系组件通信与跨组件监听状态vue-communication
Oct 18 #Javascript
vscode+gulp轻松开发小程序的完整步骤
Oct 18 #Javascript
详解VUE中的插值( Interpolation)语法
Oct 18 #Javascript
vue自定义树状结构图的实现方法
Oct 18 #Javascript
You might like
php操作MongoDB类实例
2015/06/17 PHP
jQuery '行 4954 错误: 不支持该属性或方法' 的问题解决方法
2011/01/19 Javascript
js工具方法弹出蒙版
2013/05/08 Javascript
javascript 用函数语句和表达式定义函数的区别详解
2014/01/06 Javascript
javascript根据像素点取位置示例
2014/01/27 Javascript
js的正则test,match,exec详细解析
2014/01/29 Javascript
JavaScript继承基础讲解(原型链、借用构造函数、混合模式、原型式继承、寄生式继承、寄生组合式继承)
2014/08/16 Javascript
jQuery获取checkboxlist的value值的方法
2015/09/27 Javascript
jQuery实现的点赞随机数字显示动画效果(附在线演示与demo源码下载)
2015/12/31 Javascript
JS中的二叉树遍历详解
2016/03/18 Javascript
利用node.js搭建简单web服务器的方法教程
2017/02/20 Javascript
node下使用UglifyJS压缩合并JS文件的方法
2018/03/07 Javascript
vue用递归组件写树形控件的实例代码
2018/07/19 Javascript
原生JS与CSS实现软件卸载对话框功能
2019/12/05 Javascript
javascript 代码是如何被压缩的示例代码
2020/05/06 Javascript
微信小程序开发(一):服务器获取数据列表渲染操作示例
2020/06/01 Javascript
[39:19]完美世界DOTA2联赛PWL S2 SZ vs LBZS 第二场 11.26
2020/11/30 DOTA
使用python实现递归版汉诺塔示例(汉诺塔递归算法)
2014/04/08 Python
在Docker上部署Python的Flask框架的教程
2015/04/08 Python
简单讲解Python中的数字类型及基本的数学计算
2016/03/11 Python
pandas中去除指定字符的实例
2018/05/18 Python
Python之虚拟环境virtualenv,pipreqs生成项目依赖第三方包的方法
2019/07/23 Python
全面解析HTML5中的标准属性与自定义属性
2016/02/18 HTML / CSS
会计与审计专业大专生求职信
2013/10/03 职场文书
中英文自我评价语句
2013/12/20 职场文书
点菜员岗位职责范本
2014/02/14 职场文书
金融事务专业毕业生求职信
2014/02/23 职场文书
暑假家长评语大全
2014/04/17 职场文书
信息管理与信息系统专业求职信
2014/06/21 职场文书
常务副县长“三严三实”对照检查材料思想汇报
2014/10/05 职场文书
先进教育工作者事迹材料
2014/12/23 职场文书
运动会广播稿300字
2015/08/19 职场文书
职业生涯规划书之大学四年
2019/08/07 职场文书
2019新员工试用期转正申请书3篇
2019/08/13 职场文书
pytorch 运行一段时间后出现GPU OOM的问题
2021/06/02 Python
php png失真的原因及解决办法
2021/10/24 PHP