# webpack-code
**Repository Path**: yan_guoping/webpack-code
## Basic Information
- **Project Name**: webpack-code
- **Description**: No description available
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2024-03-21
- **Last Updated**: 2024-03-21
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
## webpack加载器和插件开发
### 加载器概述
为什么要使用loader,因为使用import和require进行模块加载的时候,这两种语法都是JavaScript进行模块化开发的语法,而我们导入的图片和css并不是JavaScript,在导入的时候解析器(v8引擎)无法识别这种语法就会报错。如果想正常执行内容,就需要对一个内容转换,转换成JavaScript所识别的语法。
* ##### 基本概念
> loader 用于对模块的源代码进行转换。
* ##### 使用方法
* 安装
* 在项目中通过npm或yarn命令进行安装,之后在webpack.config.js进行配置,当我们执行webpack命令的时候,一旦遇到了import和require的时候,就会把它进行处理,把它处理成JavaScript认识的语法格式。
```sh
yarn init -y
yarn add webpack webpack-cli
mkdir src
touch src/index.js || type nul>src/index.js
touch src/index.css || type nul>src/index.css
touch webpack.config.js || type nul>webpack.config.js
```
index.css
```css
body{
background-color: red;
}
```
index.js
```js
import "./index.css";
```
webpack.config.js
```js
const path = require("path");
module.exports = {
entry: "./src/index.js", //entry可以是相对路径,也可以是绝对路径
//output只能是绝对路径
output: {
path: path.resolve(__dirname, "dist"), //__dirname是指项目所在的路径,指定路径
filename: "bundle.js", //指定文件名
},
};
```
直接使用webpack打包,会报错,因为解析不了,建议我们安装loader
```sh
yarn add css-loader
```
webpack.config.js
```js
{
module:{
//test要处理的文件类型,use 指定loader
rules: [{ test: /.css$/, use: "css-loader" }],
}
}
```
可以从上面看到,webpack已经将css输出成一个对象模块了,此时index.html肯定是不认识的
style-loader将css对象注入到html中,请使用style-loader吧
```
yarn add style-loader
```
因为要使用两个loader,use要改成数组的形式,注意:loader的执行顺序是先后到前
```js
{
module: {
//test要处理的文件类型,use 指定loader
rules: [{ test: /.css$/, use: ["style-loader", "css-loader"] }],
},
}
```
然后我们可以测试一下,在
```html
内容是输出来了,但还是报错,报错我们导出的内容不对,原因是我们在进行模块的import或者require的时候,会要求我们读取的内容必须是一个js模块形式的文件(比如说必须是commonjs或者es模块的形式)。
hello-loader.js
```js
module.exports = function (content) {
//要求模块导出的内容,要求是一个字符串,而且格式为ESM或者是commonjs的格式
console.log(content, "content");
//ESM的格式 xxx可以字符串,也可以是对象或函数 如果是字符串,记得用引号包裹起来
//export default xxx
//commonjs的格式
//module.exports = xxx
// const result = `
// export default '${content}'
// `;
// 当然,上面我们export 字符串的时候,不建议手动加上引号,还是使用JSON.stringify
const result = `
export default ${JSON.stringify(content)}
`;
return result;
};
```
当然,上面我们export 字符串的时候,不建议手动加上引号,还是使用JSON.stringify
* ##### 使用[loader runner](https://www.npmjs.com/package/loader-runner)进行调试
loader runner允许你在不安装webpack的情况下运行loaders
* 作用:
* 作为webpack的依赖包
* 进行loader的开发和调试
* 使用:
webpack内部也是使用loader runner
```sh
yarn add loader-runner
```
之后可以不用安装webpack,执行webpack来运行调试你自己写的loader了
```sh
mkdir src
mkdir loaders
touch src/index.js
touch loaders/my-loader.js
touch run-loaders.js
```
run-loaders.js
```js
const { runLoaders } = require("loader-runner");
const path = require("path");
const fs = require("fs");
//接收两个参数,第一个参数是配置项,第二个参数是一个回调函数,
//回调函数的第一个参数是err,当我们loader内部发生错误的时候,可以查看到
//回调函数的第一个参数是result,是如果没有错误输出的结果
runLoaders(
{
//导入的资源路径,绝对路径
resource: path.resolve(__dirname, "./src/index.js"),
//loaders所在的位置,绝对路径,数组
loaders: [path.resolve(__dirname, "./loaders/my-loader.js")],
//context:loader执行时的上下文 this
context: {
minimize: true,
},
//readResource:在读取被转换文件的时候,需要以什么函数来进行读取
readResource: fs.readFile.bind(fs),
},
(err, result) => {
if (err) {
console.log(err, "err in loader");
return;
}
console.log(result, "result");
}
);
```
cacheable:true 被缓存,当我们这个函数输入没有变化的时候,下次就不会再处理,直接将内容返回
```js
module.exports = function (content) {
console.log("=====[my-loader]this", this);
return content;
};
```
* ##### loader上下文(this上的属性和方法)
* this.context
* this.data
* this.cacheable
* this.callback
* this.async
* this.emitFile
my-context-loader.js
```js
module.exports = function (content) {
//this.context:属性, 被处理的文件所在的路径
console.log("======this.context======", this.context);
//this.data:属性, loader有两个执行的阶段,一个是normal阶段,一个是pitching阶段
//pitching阶段在normal阶段之前执行,这两个阶段如何共享数据呢,就是通过这个this.data ,
//在pitching阶段可以给data属性传一个值,在normal阶段就可以拿到这个值
//如果loader没有定义pitching阶段的处理,该值为null
console.log("======this.data======", this.data);
//this.cacheable:函数,设置当前loader是否可以被缓存,
//被缓存的loader,当文件没有发生变化时,直接返回之前的处理结果
//默认情况下,设置为true
this.cacheable(false);
//this.callback:函数,返回loader处理的结果,可能是成功的结果,也可能是失败的结果
//失败的结果通过this.callback的第一个参数传递,成果的结果就是通过第二个参数传递
//如果第一参数不为null,则第二个参数会被无视
//之前我们是通过return来返回loader处理完的结果,当然我们可以通过callback来返回处理完成的结果
// this.callback(new Error("loader出错了,亲~"));
this.callback(null, `export default ${JSON.stringify(content)}`);
// this.emitFile:函数,生成一个文件,到output指定的路径.(文件的复制,file-loader就是通过这个函数来实现的)
//函数的第一个参数为路径,第二个参数为文件内容
this.emitFile("index-out.js", content);
//this.async:函数,声明当前的loader为异步loader,并返回一个callback函数。
//通过调用callback函数,向webpack传递loader的处理结果
// return `export default ${JSON.stringify(content)}`;
};
```
* ##### 同步loader
```sh
mv webpack-config.js webapck.config.js 重命名文件名
```
需求分析:
1.loader要处理的文件扩展名xxx.guoping
2.文件的内容,引用到两个去全局变量 guopingName,guopingUrl(不需要自己定义,由webpack为我们定义)
3.这两个变量的值,从json文件中提取出来的
4.导出JS语法正确的转换结果
5.在页面中执行导出的JS文件
index.js
```js
//my-sync-loader.js
import "./main.guoping";
```
```sh
touch loader/var.json
touch src/main.guoping
```
main.guoping
```guoping
const h3 =document.createElement('h3')
h3.innerHTML = guopingName
const a = document.createElement('a')
a.innerHTML = guopingUrl
document.body.appendChild(h3)
document.body.appendChild(a)
```
var.json
```json
{
"guopingName": "国萍",
"guopingUrl": "国萍的地址"
}
```
my-sync-loader.js
```js
const path = require("path");
const fs = require("fs");
module.exports = function (content) {
//同步loader:我们在loader函数的处理,都是以同步的方式进行的调用
//此时,我们就可以使用同步loader。默认情况下,我们的loader都是同步loader
//注意:return和this.callback不可以同时使用
const jsonPath = path.resolve(__dirname, "var.json");
//readFileSync(同步) 文件比较小的时候,可以同步的方式读取,文件比较大的时候,可以采用异步读取的方式
const varResult = fs.readFileSync(jsonPath, "utf-8");
const varJson = JSON.parse(varResult);
//1。读取json文件
//2、拼装定义的变量
//3.拼装后的结果导出
//4.同步的loader,直接使return就可以返回loader转换的结果,异步loader不能使用return,当然同步loader可以使用this.callback
const result = `
var guopingName =${JSON.stringify(varJson.guopingName)};
var guopingUrl = ${JSON.stringify(varJson.guopingUrl)};
${content}
`;
return result;
};
```
同步loader要注意的几个点:
1、默认就是同步loader
2、同步loader里面不要有异步处理,比如说ajax请求和事件监听
* ##### 异步loader
my-async-loader.js
```js
const path = require("path");
const fs = require("fs");
module.exports = function (content) {
//异步loader,必须通过this.callback来返回文件处理的结果,不能使用return
//在异步loader中,使用异步方式调用this.callback之前,必须调用this.async
// 这样webpack就不会把loader函数返回undefined作为loader的处理结果,而是等待callback被调用
this.async(); //关键第一步
let result = "";
setTimeout(() => {
result = `export default "hello world"`;
this.callback(null, result);
}, 1000);
//函数return的时候,setTimeout的回调还没有被调用,此时result并不是想要的结果
//所以loader中包含异步处理的时候,不能用return返回处理结果,只能使用callback
//return result
};
```
my-async-loader2.js
```js
const path = require("path");
const fs = require("fs");
module.exports = function (content) {
//声明该loader是一个异步loader
this.async();
//1。读取json文件
const jsonPath = path.resolve(__dirname, "var.json");
//readFileSync(同步) 文件比较小的时候,可以同步的方式读取,文件比较大的时候,可以采用异步读取的方式
const varResult = fs.readFile(jsonPath, "utf-8", (err, data) => {
if (err) {
console.log("===读取json文件失败===", err);
this.callback(new Error("===读取json文件失败==="));
return;
}
console.log(data);
//2、拼装定义的变量
//3.拼装后的结果导出
const varJson = JSON.parse(data);
const result = `
var guopingName =${JSON.stringify(varJson.guopingName)};
var guopingUrl = ${JSON.stringify(varJson.guopingUrl)};
${content}
`;
this.callback(null, result);
});
};
```
* ##### 多个loader的执行顺序
* ##### raw loader
my-raw-loader.js
```js
module.exports = function (content) {
//默认的webpack会将要处理的文件utf-8字符串的形式传递给loader
//但有些时候,我们处理的是二进制流文件,
//如果将其转换成字符串就会出错,此时我们需要得到的是这个文件的二进制流的buffer,
//此时我们可以使用raw loader
//使用方法:module.exports.exports.raw = true
console.log(content); //如果是图片,就可以对这个buffer值进行文件的存储和拷贝
this.emitFile("summary.png", content);
return "";
};
module.exports.raw = true;
```
* ##### pitching loader
loader有两个阶段,一个是normal阶段,一个是pitching阶段。
先pitching阶段的有pitching阶段的loader的数组的先后顺序
后normal阶段的loader数组的后先顺序
===loader-a的pitching阶段===
===loader-b的pitching阶段===
===loader-c的pitching阶段===
===loader-c的normal阶段===
===loader-b的normal阶段===
===loader-a的normal阶段===
```js
const path = require("path");
module.exports = {
entry: "./src/index.js",
mode: "development",
output: {
path: path.resolve(__dirname, "dist"),
filename: "bundle.js",
},
module: {
rules: [
{
test: /.txt$/,
use: [
"./loaders/loader-a.js",
"./loaders/loader-b.js",
"./loaders/loader-c.js",
],
},
],
},
};
```
loader-b.js
```js
module.exports = function (content) {
//loader的normal阶段
console.log("===loader-b的normal阶段===");
return content;
};
//增加pitching阶段
// 函数:三个参数
//remainingReq 还剩下的请求
//previousReq 之前的请求
//data
module.exports.pitch = function (remainingReq, previousReq, data) {
console.log("===loader-b的pitching阶段===");
console.log("===loader-b的remainingReq===", remainingReq);
console.log("===loader-b的previousReq===", previousReq);
console.log("===loader-b的data===", data);
};
```
loader的配置,除了在webpack使用数组,还有一种使用内联的方式导入
webpack在解析完配置文件后,会将loader转换为下面格式
===loader-b的remainingReq===
D:\ziliao\webpack-code2\loaders\loader-b.js!D:\ziliao\webpack-code2\loaders\loader-c.js!D:\ziliao\webpack-code2\src\index.txt
==loader-b的previousReq===
D:\ziliao\webpack-code2\loaders\loader-a.js
===loader-c的remainingReq===
D:\ziliao\webpack-code2\loaders\loader-c.js!D:\ziliao\webpack-code2\src\index.txt
==loader-c的previousReq===
D:\ziliao\webpack-code2\loaders\loader-a.js!D:\ziliao\webpack-code2\loaders\loader-b.js
loader-a.js
```js
module.exports = function (content) {
//loader的normal阶段
console.log("===loader-a的normal阶段===");
console.log("===loader-a的normal[this.data]===", this.data);
console.log("===loader-a的normal阶段的content===", content);
return content;
};
//增加pitching阶段
// 函数:三个参数
module.exports.pitch = function (remainingReq, previousReq, data) {
console.log("===loader-a的pitching阶段===");
console.log("===loader-a的remainingReq===", remainingReq);
console.log("===loader-a的previousReq===", previousReq);
console.log("===loader-a的data===", data);
};
```
loader-b.js
```js
module.exports = function (content) {
//loader的normal阶段
console.log("===loader-b的normal阶段===");
return content;
};
//增加pitching阶段
// 函数:三个参数
//remainingReq 当前loader还剩下的请求
//previousReq 当前loader之前的请求
//data 要跟loader normal阶段进行数据传递的对象。在data上追加的属性,在normal阶段进行访问
module.exports.pitch = function (remainingReq, previousReq, data) {
console.log("===loader-b的pitching阶段===");
console.log("===loader-b的remainingReq===", remainingReq);
console.log("===loader-b的previousReq===", previousReq);
console.log("===loader-b的data===", data);
data.guoping = "国萍";
//当一个pitching阶段函数有返回值,此时,正常loader的执行阶段就会被打破。
//当b的pitching阶段函数有返回值,就不会执行c的pitching、c的normal、b的normal,
//直接到最后执行a的normal(只会执行它之前loader的normal),a的normal的content就是b的pitching阶段函数的返回值
return "loader b pitching result";
};
```
loader-c.js
```js
module.exports = function (content) {
//loader的normal阶段
console.log("===loader-c的normal阶段===");
return content;
};
//增加pitching阶段
// 函数:三个参数
module.exports.pitch = function (remainingReq, previousReq, data) {
console.log("===loader-c的pitching阶段===");
console.log("===loader-c的remainingReq===", remainingReq);
console.log("===loader-c的previousReq===", previousReq);
console.log("===loader-c的data===", data);
};
```
使用pitching的目的,想获取被请求数据的原始数据,在pitching阶段来通过data属性把处理的结果给normal
然后normal再去使用
* ##### 错误处理
### Loader源码解读
* ##### [file-loader](https://www.npmjs.com/package/file-loader)
1. 概述
在require或者import文件时,得到该文件的url,并将文件拷贝到output指定的文件夹
hash值.文件。在拷贝的过程中进行了重命名,这个hash值是根据文件的内容生成的,如果文件的内容不发生变化,
这个hash值就不会变化,好处就是浏览器会把请求过的这个文件进行缓存,这样就不会重复请求。
2. 使用
```sh
$ npm install file-loader --save-dev
```
```js
import img from './file.png';
const pic = new Image();
pic.src = img;
document.body.appendChild(pic)
```
```json
module.exports = {
module: {
rules: [
{
test: /\.(png|jpe?g|gif)$/i,
use: [
{
loader: 'file-loader',
//加上这个就代表你想要生成的文件名,而不是hash文件名
options:{
name:'20.png'
}
},
],
},
],
},
};
```
3. 下载源码
[github](https://github.com/webpack-contrib/file-loader)
4. 开始阅读
5. debug源码
6. 自己实现
* ##### [style-loader](https://www.npmjs.com/package/style-loader)
1. 概述
将css注入到DOM中(接受的是css-loader的处理结果)
2. 使用
```sh
$ npm install style-loader
```
```css
body {
background: red;
}
```
```js
import './index.css'
```
```js
module.exports = {
...
module: {
rules: [
{
test: /\.css$/,
loader: ['style-loader', 'css-loader']
}
]
}
}
```
3. 下载源码
4. 开始阅读
5. debug源码
6. 自己实现
```js
//下面的js代码是在./src/index.js导入./src/index.css时,执行的
//也就是说当bundle.js被导入到html时,下面代码会执行
import API from "!../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js";
import domAPI from "!../node_modules/style-loader/dist/runtime/styleDomAPI.js";
import insertFn from "!../node_modules/style-loader/dist/runtime/insertBySelector.js";
import setAttributes from "!../node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js";
import insertStyleElement from "!../node_modules/style-loader/dist/runtime/insertStyleElement.js";
import styleTagTransformFn from "!../node_modules/style-loader/dist/runtime/styleTagTransform.js";
import content, * as namedExport from "!!../node_modules/css-loader/dist/cjs.js!./index.css";
var options = {};
//转换css-loader的结果,到style
options.styleTagTransform = styleTagTransformFn;
//设置标签
options.setAttributes = setAttributes;
//将style节点插入到head
options.insert = insertFn.bind(null, "head");
options.domAPI = domAPI;
//插入style元素
options.insertStyleElement = insertStyleElement;
//所有的处理入口为这个API函数
var update = API(content, options);
export * from "!!../node_modules/css-loader/dist/cjs.js!./index.css";
export default content && content.locals ? content.locals : undefined;
```
style-loader源码解读总结:
1、在pitching阶段进行的处理(关于style-loader为什么要把loader的处理放在pitching阶段呢,是因为
1)style-loader在css-loader之后运行,在pitching阶段可以避免使用出css-loader 函数导出的字符串
)
2、在pitching阶段首先读取了用户传递进来的options
3、然后根据options对导出内容(模板字符串)进行拼装
4、拼装完成后,导出(runtime的内容)
5、bundle.js被导入到html中时,会执行上面的运行时内容
['my-style-loader','css-loader']
pitching阶段没有返回值:
my-style-loader pitching —>css loader pitching —>css loader normal —>my-style-loader normal
pitching阶段有返回值:
//如果在第一个loader的pitching阶段返回来内容,默认情况下,后续的loader的所有处理都不会执行
//所以要在该函数中,处理所有的转换流程
my-style-loader pitching —>webpack 处理
为什么官方的style-loader在pitching阶段处理,它直接通过remainRequest,直接把css-loader处理的结果拿过来了
实现一个在pitching阶段的my-style-loader
my-style-loader.js
```js
const getImportHandleCssCode = require("./utils");
module.exports = function (content) {};
module.exports.pitch = function (remainingReq, previousReq, data) {
//如果在第一个loader的pitching阶段返回来内容,默认情况下,后续的loader的所有处理都不会执行
//所以要在该函数中,处理所有的转换流程
console.log(remainingReq);
return `
// 将css-loader处理css文件的导入,使用内联的方式(在导入文件时,直接指定loader)
// 在webpack.config.js中配置的css处理,与下面内联方式处理css相冲突
// 解决冲突的办法:在请求的最前面加上!,忽略webpack.config.js中的配置
import cssList from '!../node_modules/css-loader/dist/cjs.js!D:/ziliao/webpack-code/src/index.css';
// import handleCss from '../loaders/runtime/handle-css.js'
// 这个css是一个二维数组,css:[id,cssText]
// import handleCss from '../loaders/runtime/handle-css.js'
${getImportHandleCssCode(this)}
//将下面的循环提取到一个loader的一个函数中
// for (let css of cssList){
// const style =document.createElement('style')
// document.head.appendChild(style)
// style.appendChild(document.createTextNode(css[1]))
// }
handleCss(cssList)
export default cssList
`;
};
```
utils.js
```js
const path = require("path");
function getImportHandleCssCode(loaderContext) {
//返回handle-css.js所在的路径,与被处理css文件所在的路径之间的相对路径
//找到handle-css.js的路径
const handleCssPath = path.resolve(__dirname, "runtime/handle-css.js");
console.log(handleCssPath, "handleCssPath");
//找到css的路径:loaderContext.context
//使用loaderContext.utils.contextify来计算相对路径
const relativePath = loaderContext.utils.contextify(
loaderContext.context,
handleCssPath
);
console.log(
relativePath,
"relativePath",
`import handleCss from ${JSON.stringify(relativePath)}`
);
return `import handleCss from ${JSON.stringify(relativePath)}`;
}
module.exports = getImportHandleCssCode;
```
runtime/handle-css.js
```js
function handleCss(cssList) {
for (let css of cssList) {
const style = document.createElement("style");
document.head.appendChild(style);
style.appendChild(document.createTextNode(css[1]));
}
}
module.exports = handleCss;
```
webpack.config.js
```js
const path = require("path");
module.exports = {
entry: "./src/index.js", //entry可以是相对路径,也可以是绝对路径
//output只能是绝对路径
output: {
path: path.resolve(__dirname, "dist"), //__dirname是指项目所在的路径,指定路径
filename: "bundle.js", //指定文件名
},
mode: "development",
module: {
//test要处理的文件类型,use 指定loader,use的查找方式是以当前webpack.config.js的路径为基准,去找js的文件
rules: [
{
test: /\.css$/,
use: ["./loaders/my-style-loader.js", "css-loader"],
},
],
},
};
```
在normal阶段实现一个style-loader
my-style-loader2.js
```js
const getImportHandleCssCode = require("./utils");
module.exports = function (content) {
console.log(content);
content = content.replace("export default ___CSS_LOADER_EXPORT___;", "");
return `
//将css-loader的导出内容,原封不动的返回了
${content}
//中间插入将css-loader的导出结果,注入到html的过程
const cssList = ___CSS_LOADER_EXPORT___;
${getImportHandleCssCode(this)}
handleCss(cssList)
export default ___CSS_LOADER_EXPORT___
`;
};
```
1、在pitching阶段,直接请求了css文件,通过内联的方式,指定了css-loader,直接拿到css-loader处理后的结果
2、将处理的结果(list)遍历,放在dom树上
3、将放置dom树的过程,封装到函数中
4、处理import时的路径问题,并将import的生成代码,封装到函数中
5、在normal阶段,来处理了style-loader的转换过程
pitching阶段的处理相对复杂且抽象,好处是:不依赖于css-loader内部的导出代码。
normal阶段,严重依赖css-loader的导出代码,这种强耦合的方式,不适用于在第三方node包中使用
需求:
导入一个pdf文件,
1、生成对应的图片(每一页对应一张图片)
2、返回生成图片所对应的路径
```sh
npm i loader-utils -D
npm i pdf2pic -D
```
遇到一个问题,坑死我了。解决了半天,原来是在使用 Ghostscript **9.52**和GraphicsMagick,用vscode要以管理员权限运行!!!
pdf2img-loader.js
```js
const { interpolateName } = require("loader-utils");
const schema = require("./options.json");
const path = require("path");
const { fromBuffer } = require("pdf2pic");
const createFolderIfNoExist = require("./utils-pdf2pic");
module.exports = async function (content) {
//声明为异步loader,因为转换pdf到img的处理是异步函数
this.async();
const options = this.getOptions(schema);
// console.log(options, "options");
//生成被转换后图片的名字
let name = options.name || "[contenthash]-[name].pdf";
name = interpolateName(this, name, {
content,
});
//决定被转换图片的目标路径
//webpack的输出路径是this._compiler.outputPath
let outputPath = this._compiler.outputPath;
if (options.outputPath) {
outputPath = path.resolve(outputPath, options.outputPath);
}
//使用第三方的库 将pdf转换为图片 pdf2pic
const pdf2imgOptions = {
density: 100,
saveFilename: name,
savePath: outputPath,
format: "png",
width: 2550,
height: 3000,
};
//check outputPath是否存在,如果不存在,则创建该文件夹
createFolderIfNoExist(outputPath);
const result = await fromBuffer(content, pdf2imgOptions).bulk(-1);
console.log(result);
const paths = result.map((img) => {
return this.utils.contextify(this._compiler.outputPath, img.path);
});
console.log(paths, "paths");
this.callback(null, `export default ${JSON.stringify(paths)}`);
};
module.exports.raw = true;
```
webpack.config.js
```js
const path = require("path");
module.exports = {
entry: "./src/index.js", //entry可以是相对路径,也可以是绝对路径
//output只能是绝对路径
output: {
path: path.resolve(__dirname, "dist"), //__dirname是指项目所在的路径,指定路径
filename: "bundle.js", //指定文件名
},
mode: "development",
module: {
//test要处理的文件类型,use 指定loader,use的查找方式是以当前webpack.config.js的路径为基准,去找js的文件
rules: [
{
test: /\.pdf$/,
use: {
loader: "./loaders/pdf2img-loader.js",
options: {
outputPath: "pdfImages",
},
},
},
],
},
};
```
webpack的 file-loader和style-loader都有一个固定的模板,这个是webpack-default脚手架生成的
```sh
yarn init -y
//package.json
"script":{
"defaults":"webpack-defaults"
}
yarn defaults
```