当前位置:

webpack 5 实现自定义 loader

访客 2024-04-23 699 0

前面的话

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

代码地址:自定义loader

准备工作

我们先创建一个webpack项目。

初始化项目

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

npminit-y

安装依赖

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

pnpmaddwebpackwebpack-cliwebpack-dev-server--save-dev

创建入口文件

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

//index.jsdocument.write('hellowp')

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

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

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

constpath=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--configprod.config.js"}

添加npmscript

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

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

这样可以通过npmrundev来启动项目:
使用npmrunbuild来打包项目:

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

创建index.html文件

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

<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metahttp-equiv="X-UA-Compatible"content="IE=edge"><metaname="viewport"content="width=device-width,initial-scale=1.0"><title>Document</title></head><body><scriptsrc="./bundle.js"></script></body></html>

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

实现简单自定义的loader

为什么需要loader

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

loader是什么

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

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

loader原则

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

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

实现一个简单的同步loader

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

创建同步loader

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

//my-loader.jsmodule.exports=function(source){//在这里按照你的需求处理source//可以通过this.getOptions或者this.query来获取参数constoptions=this.getOptions()console.log(options)returnsource.replace('wp','xiaoqi')}//或者使用this.callbackmodule.exports=function(source){//在这里按照你的需求处理source//可以通过this.getOptions或者this.query来获取参数constoptions=this.getOptions()constresult=source.replace('wp','xiaoqi')this.callback(null,result)}

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

this.callback(//转换异常时,抛出错误err:Error||null,//转换后的结果content:string|Buffer,//用于把转换后的内容得出原内容的SourceMap,方便调试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'],},}

运行npmrundev可以看到wp被替换为xiaoqi。

实现一个异步loader

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

创建async.txt文件

在src目录下创建async.txt文件,随便写点内容。

创建异步loader

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

//my-async-loader.jsconstfs=require('fs')constpath=require('path')module.exports=function(source){//通过this.async来返回一个异步函数,第一个参数Error,第二个参数是处理的结果。constcallback=this.async()fs.readFile(path.join(__dirname,'../async.txt'),'utf-8',(err,data)=>{consthtml=`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.jsimporttxtfrom'./async.txt'document.write('hellowp')document.write(`</br>异步loader:${txt}`)
运行||打包

npmrundev可以看到async.txt文件下的内容被打印出来。

实现一个渲染markdown的loader

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

创建md文件

首先我们在src下创建一个md文件。

编写mark-loader

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

//mark-loader.jsconstMarkdownIt=require('markdown-it')module.exports=function(source){constoptions=this.getOptions()constmd=newMarkdownIt({html:true,...options,})lethtml=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插入其中。

importtxtfrom'./async.txt'importmdfrom'./mk.md'document.write('hellowp')document.write(`</br>异步loader:${txt}`)constdiv=document.createElement('div')div.innerHTML=`${md}`document.body.appendChild(div)
运行||打包

npmrundev运行,可以看到md文件以html的形式渲染出来。

实现一个生成雪碧图的loader

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

创建css文件

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

创建sprite-loader

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

constpath=require('path')constfse=require('fse')constSpritesmith=require('spritesmith')module.exports=function(source){constcallback=this.async()//匹配url(开头?__sprite结尾的constimgs=source.match(/url\((\S*)\?__sprite/g)constmatchImgs=[]for(leti=0;i<imgs.length;i){//解析出图片路径constimg=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'importtxtfrom'./async.txt'importmdfrom'./mk.md'//...constdiv=document.createElement('div')div.className='img2'div.innerHTML=`${md}`document.body.appendChild(div)
运行||打包

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

发表评论

  • 评论列表
还没有人评论,快来抢沙发吧~