前言
前章有说过 loader 相当于一个文件翻译员,主要是对不同类型的文件进行是识别与转换,由于 Webpack 是由 node 编写,所以只能识别 js 文件,那么今天我们就来探索 webpack 中的 loader 是如何实现文件识别与转换的
了解
通过一个简单的配置,来了解一下,最经典的就是 css-loader 因为 webpack 只能识别js文件,那 css 文件怎么办 ?
// 一个简单的 webpack 配置
const path = require('path');
module.exports = {
entry: './main.js',
output: {
filename: 'bundle[hash:6].js',
path: path.resolve(__dirname, './dist'),
},
module: { // loader 主要使用的地方
rules: [
// 配置打包css文件的loader,use定义使用到的loader,注意顺序
// style-loader 必须放在 css-loader 前面,因为loader的执行顺序是从右至左
{
test: /\.css$/, // 匹配后缀为 .css 的文件
use: ['style-loader', 'css-loader']
}
]
}
};
职责与逻辑
Loader
的职责是单一的,只需要完成一种识别与转换。 如果一个源文件需要经过多次转换才能达到预期效果,那么久需要通过多个 Loader
进行转换, 在调用多个 Loader
去转换一个文件时,每个 Loader
会链式的从右向左顺序执行, 前一个Loader
处理后的结果会传给下一个 Loader
继续处理,最后的Loader
将处理后的结果返回给 Webpack
基础
通过上边的了解之后,我们可以知道 Loader
的职责单一性,其实它是一个由 node 编写的文件处理模块,实质就是一个函数,ok,看一下结构
function LoadrFunc (source) {
// source 是参数,也就是要处理的文件实体内容
// do something
// 相当于该`Loader`没有做任何转换
return source;
};
module.exports = LoadrFunc
接收一个参数 source
源码,就是要做转换的文件源码,然后做转换,最后返回给 webpack
进阶
在了解过 loader 是如何实现的之后,那么我们还知道日常使用中,可能会这么写 es6 转化 es5
{
test:/\.js$/, // 匹配后缀为 .js 的文件
use:{ // 这里采用对象的形式编写
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env'], // es6 转化 es5
}
}
}
这里使用 es6 转换 es5 的例子来演示一下,loader 中 options 是如何实现的 ? 在介绍之前,我们需要知道一个包 loader-utils
,故名思意就是了 loader 的工具类包,里边有一些 loader 常用的方法,ok
// 引入loader-utils 包
const loaderUtils = require('loader-utils');
function LoadrFunc (source) {
// source 是参数,也就是要处理的文件实体内容
// 获取到用户给当前 Loader 传入的 options
const options = loaderUtils.getOptions(this);
// do something
// 相当于该`Loader`没有做任何转换
return source;
};
module.exports = LoadrFunc
如何实现一个 loader
就 ES6 转换 es5 loader 来示例演进一下,看看如何实现一个类似的 loader
根据上述说法,首先安装一下 loader-utils 工具包
npm install loader-utils -D
使用相关配置
// 一个简单的 webpack 配置
const path = require('path');
module.exports = {
entry: './main.js',
output: {
filename: 'bundle[hash:6].js',
path: path.resolve(__dirname, './dist'),
},
resolveLoader:{
// 默认从 node_modules 查找,找不到则去 customLoaderDir 目录查找
modules:["node_modules",path.resolve(__dirname,"customLoaderDir")],
// 别名的方式,绝对路径
// alias:{
// "es6ToEs5-loader":path.resolve(__dirname,"customLoaderDir","es6ToEs5-loader.js")
// }
},
module: { // loader 主要使用的地方
rules: [
{
test:/\.js$/, // 匹配后缀为 .js 的文件
use:{ // 这里采用对象的形式编写
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env'], // es6 转化 es5
}
}
}
]
}
};
下面我来编写一下 es6 转换 es5 的 loader, 这里命名为 es6ToEs5-loader
, 用用常规 loader 的结构
loader 默认是从 node-modules 目录下查找, 根据上述相关配置,会从 node_modules 下查找,找不到的话,则会从 customLoaderDir 目录查找,loader 的名称就是自定义 loader js 文件的名称
// customLoaderDir 目录,下创建 es6ToEs5-loader.js
// 引入loader-utils 包
let loaderUtils = require('loader-utils');
// 引入babel的核心
let babel = require("@babel/core")
function LoadrFunc (source) {
// source 是参数,也就是要处理的文件实体内容
// 获取到用户给当前 Loader 传入的 options
const options = loaderUtils.getOptions(this);
// 这里是异步,所以需要调用异步 api async , 这里可以打印一下 this 看看还有哪些方法和属性
let callback = this.async()
// 通过 babel 核心准换语法
babel.transform(source,{
...options,
sourceMap:true, // 这个呢也可以在使用loader的配置 options 里
fileNmae: this.resourcePath.split("/").pop() // 给map文件起一个昵称
},function (err,result) {
// 回调函数接受两个参数,一个失败原因,一个结果 result
// result.code 转化后的代码
// result.map 则是映射的源文件
callback(err,result.code,result.map)
})
// 在这里这个 return 就没有任何作用了,因为上述是个回调函数,显然是异步处理的,所以核心在 callback 回调函数里
// return source;
};
module.exports = LoadrFunc
ok,通过一个简单的示例,可以清楚的了解到 webpack 中 loader 具体是怎么实现的,下一章,我将来分享 webpack 中 plugin 插件又是怎样的原理
小小鼓励,大大成长,欢迎点赞