loader相关文档地址:https://webpack.docschina.org/api/loaders/
初始化项目
首先我们初始化一个项目
然后执行npm init -y 创建package.json
目录结构如下:
文件内容如下:
// get-time.js
export default () => {
return new Date()
}
// utils.js
const queryParse = query => {
const result = {};
if (query) {
decodeURIComponent(query)
.split('&')
.map(v => v.split('='))
.forEach(v => {
result[v[0]] = v[1];
});
}
return result;
};
const getQueryStringify = (query = {}) => {
const keys = Object.keys(query);
if (!query || !keys.length) return '';
return keys.map(key => `${key}=${query[key]}`).join('&');
};
module.exports = { queryParse, getQueryStringify }
// index.js
import { queryParse, getQueryStringify } from './utils'
import getTime from './get-time'
console.log('queryParse:', queryParse('name=张三&age=28'))
console.log('getQueryStringify:', getQueryStringify({name: '李四', age: 33 }))
console.log('getTime:', getTime())
<!--index.html-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
</body>
</html>
// webpack.config.js
const path = require('path')
module.exports = {
mode: 'development',
entry: './src/js/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js'
},
}
webpack的基本配置
1、安装依赖包
- webpack
- webpack-cli
- html-webpack-plugin
- babel-loader
- babel-loader
- @babel/core
// babel-loader和@babel/core分开装的,我一起装的话执行会报错,可能是版本对不上
npm install webpack webpack-cli html-webpack-plugin babel-loader --save-dev
npm install @babel/core --save-dev
- 然后给webpack.config添加基本的loader、plugin
// webpack.config.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
mode: 'development',
entry: './src/js/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js'
},
module: {
rules: [
{
test: /.js$/,
loader: 'babel-loader'
},
]
},
plugins: [
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'index.html',
inject: 'body', //script标签的放置
title: 'webpack5自定义loader',
// minify: { //html压缩
// removeComments: true, //移除注释
// collapseWhitespace: true //移除空格
// }
})
]
}
然后执行npx webpack之后如下
点击dist里面的index.html查看如下,我们发现title修改了,main.js自动帮我们引入了
实现一个自定义的同步loader
在项目根目录创建目录loader,然后进入loader目录新建sync.js文件
// sync.js
// 注意:同步和异步都不能用箭头函数方式,因为会改变this指向为undefined
module.exports = function (content, map, meta) {
console.log('content:', content)
// this.callback比直接return content 方法则更灵活,因为它允许传递多个参数,而不仅仅是 content。
this.callback(null, content, map, meta)
// return content
}
同时,我们在webpack.config.js中使用这个loader,
这里使用resolveLoader配置项,指定loader查找文件路径,这样我们使用loader的时候可以直接指定loader的名字,不用带loader的具体的路径
同时匹配到.js文件同时使用多个loader,所以/.js$/修改为用use[]的写法,代码如下:
// webpack.config.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
mode: 'development',
entry: './src/js/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js'
},
resolveLoader: {
modules: ['node_modules', './loader']
},
module: {
rules: [
{
test: /.js$/,
use: [
{ loader: 'babel-loader' },
{
loader: 'sync',
options: {
showCopyright: true
}
}
],
},
]
},
plugins: [
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'index.html',
inject: 'body', //script标签的放置
title: 'webpack5自定义loader',
// minify: { //html压缩
// removeComments: true, //移除注释
// collapseWhitespace: true //移除空格
// }
})
]
}
接着我们给loader加一个功能:给匹配到的js文件根据loader的options判断是否添加一行console.log('这里可以是作者的名字啊');
// sync.js
// 注意:同步和异步都不能用箭头函数方式,因为会改变this指向为undefined
module.exports = function (content, map, meta) {
console.log('content:', content)
// 获取配置的options, webpack5版本自带,老版本需要用loader-utils来获取
const { showCopyright } = this.getOptions()
// 自动给文件添加copyright的功能
if(showCopyright) {
content = `console.log('这里可以是作者的名字啊');\n${content}`
}
console.log(content)
// this.callback比直接return content 方法则更灵活,因为它允许传递多个参数,而不仅仅是 content。
this.callback(null, content, map, meta)
// return content
}
我们再次执行npx webpack,打包后的代码如下:
然后用浏览器打开dist里面的index.html文件,发现三个js文件也都打印出来了这行信息
至此,我们就实现了一个简单的给.js文件添加信息的loader
实现一个自定义的异步loader
和同步的方式基本差不多,异步的loader,使用是的 this.async 来获取 callback 函数:
我们在src/loader下面新建async.js
// async.js
// 注意:同步和异步都不能用箭头函数方式,因为会改变this指向为undefined
module.exports = function (content, map, meta) {
var callback = this.async();
// 获取配置的options, webpack5版本自带,老版本需要用loader-utils来获取
const { showCompileTime } = this.getOptions()
// 自动给文件添加copyright的功能
if(showCompileTime) {
content = `// 项目编译时间:${new Date()}\n${content}`
}
console.log(content)
callback(null, content, map, meta)
}
然后在webpack.config.js里面使用async这个loader
// webpack.config.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
mode: 'development',
entry: './src/js/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js'
},
resolveLoader: {
modules: ['node_modules', './loader']
},
module: {
rules: [
{
test: /.js$/,
use: [
{ loader: 'babel-loader' },
{
loader: 'sync',
options: {
showCopyright: true
}
},
{
loader: 'async',
options: {
showCompileTime: true
}
},
],
},
]
},
plugins: [
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'index.html',
inject: 'body', //script标签的放置
title: 'webpack5自定义loader',
// minify: { //html压缩
// removeComments: true, //移除注释
// collapseWhitespace: true //移除空格
// }
})
]
}
至此我们就介绍了如何实现自定义同步、异步的loader
loader从下往上解析
文章最后说明一下:webpack内loader的执行是从下往上的解析的