loader主要是帮助webpac将不同类型的文件转换为webpack可识别的模块。
分类:enforce属性
pre 前置loader,normal 普通loader,inline:内联loader,post:后置loader
如果不写默认是 normal类型
执行顺序:pre > normal > inline > post
相同等级的,从后往前执行。
//普通loader,执行顺序:loader3,loader2,loader1
module:{{test:/\.js$/,loader:"loader1"},{test:/\.js$/,loader:"loader2"},{test:/\.js$/,loader:"loader3"},}//不同级别loader,执行顺序:loader1,loader2,loader3
module:{{enforce:"pre",test:/\.js$/,loader:"loader1"},{test:/\.js$/,loader:"loader2"},{enforce:"post",test:/\.js$/,loader:"loader3"},}
使用loader的方式:
配置方式:如上述直接在webpack.config.js文件配置,加上enforce配置。(pre normal post)
内联方式:在每个import语句中显示指定loader(inline)
inline loader用法:
这里使用css-loader,style-loader来处理这个css资源,!是为了隔开各个loader
import Styles from 'style-loader!css-loader?modules!./styles.css'
inline loader可以通过添加不同的前缀,跳过其他类型的loader:
! :表示跳过normal loader,即使在module里配置了style-loader,css-loader,如是normal类型的,也不会执行的。
import Styles from '!style-loader!css-loader?modules!./styles.css'
- ! :表示跳过pre和normal loader,即使在module里配置了style-loader,css-loader,如是pre或normal类型的,也不会执行的。
import Styles from '-!style-loader!css-loader?modules!./styles.css'
! ! :表示跳过pre和normal和post loader,即使在module里配置了style-loader,css-loader,如是pre或normal或post类型的,也不会执行的。
import Styles from '!!style-loader!css-loader?modules!./styles.css'
但是一般不会选择使用inline,因为不好复用。
实际使用:
新创建一个项目:
npm init -y
//webpack.config.js
const path = require("path")
const HtmlWebpackPlugin=reuqire("html-webpack-plugin")
module.exports={entry:"./src/main.js",output:{path:path.resolve(__dirname,"./dist"),filename:"js/[name].js",clean:true},module:{rules:[]},plugins:[new HtmlWebpackPlugin({template:path.resolve(__dirname,"public/index.html")})],mode:"development"
}
//index.html
<!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>myLoader</title>
</head>
<body><div id="app"></div>
</body>
</html>
//main.js
console.log("hello main");
下载依赖:
npm i webpack webpack-cli html-webpack-plugin -D
尝试打包:npx webpack成功
新建loaders文件夹,建立test-loader.js
loader是一个函数,当webpack解析资源时,会调用响应的loader,传入内容loader会作为参数收到数据并且返回出去,
content 文件内容
map SourceMap相关
meta 别的loader传来的数据
module.exports=function(content,map,meta){console.log(content);return content
}
webpack.config.js配置:
rules:[{test:/\.js$/,loader:"./loaders/test-loader.js"}]
loader的分类
同步loader:
// 同步loader
// 简单写法,只有一个loader
// module.exports=function(content){
// return content
// }
//多个loader传递,倾向于这个
module.exports=function(context,map,meta){//第一个参数:表示是否有错误,有错就是具体内容,无错就是null//第二个参数:传递的内容//第三个参数:source-map,继续传递//第四个参数:meta给其他loader的参数,可以自己写,也可以传上一个下来的this.callback(null,context,map,meta)
}
异步loader:异步代码一定要放在异步loader中放在同步中会有问题。
// 异步loader,会等待异步做完后再做其他的事
module.exports=function(context,map,meta){const callback = this.async();setTimeout(()=>{console.log("test2");callback(null,context,map,meta)},1000)
}
在webpack配置:
rules:[// {// test:/\.js$/,// loader:"./loaders/test-loader.js"// },{test:/\.js$/,use:["./loaders/test/test1.js","./loaders/test/test2.js"]},]
raw loader:可以写异步或者同步,但是要在最后加一句:module.exports.raw = true
//接收的content数据是buffer类型的
module.exports=function(context,map,meta){const callback = this.async();setTimeout(()=>{console.log("test2");callback(null,context,map,meta)},1000)
}
module.exports.raw = true
pitch loader:在输出的对象里加上pitch方法,这个方法会在loader执行之前先执行,优先级大于loader,会先把每个loader的pitch方法执行后,在按照顺序执行loader
pitch的执行顺序取决于loader的顺序,同级的话,正向顺序执行,如果loader存在不同的优先级,那么pitch也根据优先级的反向顺序执行。
例如:
全部为normal,从左向右,从上到下执行;
存在pre,normal,post,则从优先级低的开始执行,post=>normal=>pre
pitch全部执行结束后,执行loader
如果在pitch方法中加入了返回值,那么之后的所有pitch以及loader全部都不执行,直接跳到前一个pitch方法对应的loader中继续加载loader。
module.exports=function(context,map,meta){console.log("loader1");this.callback(null,context,map,meta)
}
module.exports.pitch=function(){console.log("pitch1");
}
loader API
说几个常用的,具体的参考:Loader Interface | webpack 中文文档 | webpack 中文文档 | webpack 中文网
自定义clean-log-loader:
module.exports=function(content,map,meta){return content.replace(/console\.log\(.*\);?/g,"")
}
rules:[// {// test:/\.js$/,// loader:"./loaders/test-loader.js"// },// {// test:/\.js$/,// use:[// // "./loaders/test/test1.js","./loaders/test/test2.js"// // "./loaders/test/test3.js"// "./loaders/test/test4.js","./loaders/test/test5.js","./loaders/test/test6.js"// ]// },// {// enforce:"pre",// test:/\.js$/,// loader:"./loaders/test/test4.js"// },// {// enforce:"post",// test:/\.js$/,// loader:"./loaders/test/test6.js"// },// {// test:/\.js$/,// loader:"./loaders/test/test5.js"// },{test:/\.js$/,loader:"./loaders/clean-log-loader.js"},]
自定义banner-loader,添加作者:
module.exports=function(context,map,meta){//获取传入的options选项//schema要符合JSONschema的验证规则let schema = {"type":"object","properties":{"author":{ //author属性"type":"string"}},"additionalProperties":false //是否允许添加其他的属性}let options = this.getOptions(schema)const prefix =`/**Author:${options.author}*/`return prefix+context
}
rules:[{test:/\.js$/,loader:"./loaders/clean-log-loader.js"},{test:/\.js$/,loader:"./loaders/banner-loader.js",options:{author:"大熊",age:18}},]
自定义babel-loader:
下载依赖:
npm i @babel/core @babel/preset-env -D
//babel-loader
const babel = require("@babel/core")
module.exports=function(context,map,meta){let schema = {"type":"object","properties":{"presets":{"type":"array"}},additionalProperties:true}const callback = this.async()let options = this.getOptions(schema)babel.transform(context,options,function(err,result){if(err) callback(err)else callback(null,result.code,)})return context
}
{test:/\.js$/,loader:"./loaders/babel-loader.js",options:{presets:["@babel/preset-env"],}},
然后打包发现箭头函数转为普通函数。
自定义file-loader:主要针对图片,字体等文件,转为二进制的流。
下载工具:
npm i loader-utils -D
const loaderUtils = require("loader-utils")
module.exports=function(content){//1.根据文件内容生成带hash值的文件名const interpolateName = loaderUtils.interpolateName(this,//上下文this"[hash].[ext][query]",//生成文件名称格式content// 处理内容)//interpolateName = `images/${interpolateName}`可以设置输出的路径//2.将文件输出this.emitFile(interpolateName,content)// 3.返回:module.exports = "文件路径(文件名)"return `module.exports = "${interpolateName}"`
}
module.exports.raw=true
{test:/\.(png|jpe?g|gif)$/,loader:"./loaders/file-loader.js",type:"javascript/auto" //阻止webpack默认处理图片资源,只用file-loader处理},
自定义style loader
module.exports=function(context,map,meta){// 不能直接会用style-loader,只能处理样式,不能处理引入的其他资源//借助css-loader解决样式中引用其他资源的问题,但是css暴露的是js代码,style-loader需要执行js代码得到返回值,再创建style标签,插入到页面上,不好操作,于是styleloader使用pitch的方式
}
module.exports.pitch = function(remainningRequest){ // remainningRequest剩余要处理的数据// 1.将remainningRequest改为相对路径(后面处理只能使用相对路径)const relativePath = remainningRequest.split("!").map(absolutePath=>{// 返回一个相对路径return this.utils.contextify(this.context,absolutePath) //this.context指当前loader所在的目录}).join("!")// 2.在这里会使用css-loader处理资源,引入css-loader处理后的资源,创建tyle并引入。// 添加!!:加载完pitch方法以后不再执行其他的loader,包括pre,normal,post// 引入 styke使用内联的用法const script = `import style from "!!${relativePath}"const styleEl = document.createElement('style')styleEl.innerHTML= styledocument.head.appendChild(styleEl)`// 终止后面的loader执行return script
}
{test:/\.css$/,use:["./loaders/style-loader.js","css-loader"]},