webpack 作为前端项目的打包工具,具有很好的学习价值。下面来学习下其中的
Loader
Loader 可以帮助webpack 将不同类型的文件转换为webpack 可识别的模块
webpack 中Loader 使用:https://www.webpackjs.com/loaders/
webpack 中Loader API 的介绍:https://www.webpackjs.com/api/loaders/
在Webpack 中,默认的配置文件为:webpack.config.js 。在学习Loader 之前,要先了解下webpack.config.js 和vue.config.js 的区别:
-
webpack.config.js 是webpack 的配置文件,所有使用webpack 作为打包工具的项目都可以使用,vue 项目可以使用,react 项目也可以使用 -
vue.config.js 是vue 项目的配置文件,专用于vue 项目。通过vue.config.js 中常用功能的配置,简化配置工作,当然如果需要更专业的配置工作,两者在vue 项目中是可以并存的 -
vue-cli3 创建项目时并不会自动创建vue.config.js ,因为这个是可选项,所以一般都是修改webpack 时才会自己创建一个vue.config.js -
因为vue-cli3 内部高度集成了webpack ,一般来说使用者不需要再为webpack 做什么配置,所以没有暴露webpack 的配置文件,但开发中依然可以创建vue.config.js 去修改默认的webpack -
Vue 项目中 vue.config.js 文件就等同于 webpack 的 webpack.config.js
vue 中配置文件说明:https://cli.vuejs.org/zh/config/
1. Loader 分类与执行顺序
1.1 Loader 分类
pre : 前置 loader normal : 普通 loader inline : 内联 loader post : 后置 loader
1.2 Loader 执行顺序
- 4 类
loader 的执行优级为:pre > normal > inline > post - 相同优先级的
loader 执行顺序为:从右到左,从下到上
module: {
rules: [
{
test: /\.js$/,
loader: "loader1",
},
{
test: /\.js$/,
loader: "loader2",
},
{
test: /\.js$/,
loader: "loader3",
},
],
},
module: {
rules: [
{
enforce: "pre",
test: /\.js$/,
loader: "loader1",
},
{
test: /\.js$/,
loader: "loader2",
},
{
enforce: "post",
test: /\.js$/,
loader: "loader3",
},
],
},
1.3 使用Loader方式
- 配置方式:在
webpack.config.js 文件中指定 loader 。(pre 、normal 、post ) - 内联方式:在每个
import 语句中显式指定 loader 。(inline loader )
1.3.1 内联 Loader
用法:import Styles from 'style-loader!css-loader?modules!./styles.css';
含义:
- 使用
css-loader 和 style-loader 处理 styles.css 文件 - 通过
! 将资源中的 loader 分开
inline loader 可以通过添加不同前缀,跳过其他类型 loader
import Styles from '!style-loader!css-loader?modules!./styles.css';
-! 跳过 pre 和 normal loader
import Styles from '-!style-loader!css-loader?modules!./styles.css';
!! 跳过 pre 、 normal 和 post loader
import Styles from '!!style-loader!css-loader?modules!./styles.css';
2. 开发 Loader 步骤
2.1 开发环境
新建一个文件在,并在里面新建一个webpack.config.js 文件,同时新建src 、public 文件夹,具体目录结构如下:
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry: './src/main.js',
output: {
path: path.resolve(__dirname, './dist'),
filename: 'js/[name].js',
clean: true,
},
module: {
rules: [
{
test: /\.js$/,
loader: './loaders/test-loader/test-loader.js',
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, 'public/index.html'),
}),
],
mode: 'development',
}
然后打开终端,来到项目根目录,分别运行以下指令:
- 初始化
package.json ,此时会生成一个基础的 package.json 文件
npm init -y
npm i webpack webpack-cli -D
-
安装html-webpack-plugin 插件 该插件生成一个HTML 文件,使用自己提供的模板 -
编写main.js 文件
console.log('hello main')
开发模式:npx webpack ./src/main.js --mode=development
生产模式:npx webpack ./src/main.js --mode=production
npx webpack : 是用来运行本地安装 Webpack 包的。./src/main.js : 指定 Webpack 从 main.js 文件开始打包,不但会打包 main.js ,还会将其依赖也一起打包进来。--mode=xxx :指定模式(环境)
npx是执行Node软件包的工具,它从npm5.2版本开始,就与npm捆绑在一起
npx作用:
- 默认情况下,首先检查路径中是否存在要执行的包(即在项目中)
- 如果存在,它将执行
- 若不存在,意味着尚未安装该软件包,npx将安装其最新版本,然后执行它
执行完上面操作后,会在项目根目录下生成一个dist 文件夹,把其中的index.html 文件在浏览器打开
2.2 最简单的 Loader
在loaders 文件夹下新建test-loader/test-loader.js 文件
module.exports = function (content, map, meta) {
console.log('hello test loader ... ')
return content.replace('main', 'webpack')
}
它接受要处理的源码作为参数,输出转换后的 js 代码。Loader 接受的参数
content :源文件的内容map :SourceMap 数据meta 数据:可以是任何内容
在webpack.config.js 文件中,使用该Loader
module: {
rules: [
{
test: /\.js$/,
loader: './loaders/test-loader/test-loader.js',
},
],
},
执行打包命令,并查询运行结果:
2.3 Loader 分类
2.3.1 同步 Loader
module.exports = function (content, map, meta) {
return content;
};
this.callback 方法则更灵活,因为它允许传递多个参数,而不仅仅是 content 。
module.exports = function (content, map, meta) {
this.callback(null, content, map, meta);
return;
};
2.3.2 异步 loader
module.exports = function (content, map, meta) {
const callback = this.async();
setTimeout(() => {
callback(null, result, map, meta);
}, 1000);
};
由于同步计算过于耗时,在 Node.js 这样的单线程环境下进行此操作并不是好的方案,建议尽可能地使loader 异步化。但如果计算量很小,同步 loader 也是可以的。
2.3.3 Raw Loader
默认情况下,资源文件会被转化为 UTF-8 字符串,然后传给 loader 。通过设置 raw 为 true ,loader 可以接收原始的 Buffer 。
module.exports = function (content) {
return content;
};
module.exports.raw = true;
2.3.4 Pitching Loader
module.exports = function (content) {
return content;
};
module.exports.pitch = function (remainingRequest, precedingRequest, data) {
console.log("do somethings");
};
webpack 会先从左到右执行 loader 链中的每个 loader 上的 pitch 方法(如果有),然后再从右到左执行 loader 链中的每个 loader 上的普通 loader 方法
在这个过程中如果任何 pitch 有返回值,则 loader 链被阻断。webpack 会跳过后面所有的的 pitch 和 loa der,直接进入上一个 loader 。
2.4 loader API
方法名 | 含义 | 用法 |
---|
this.async | 异步回调 loader。返回 this.callback | const callback = this.async() | this.callback | 可以同步或者异步调用的并返回多个结果的函数 | this.callback(err, content, sourceMap?, meta?) | this.getOptions(schema) | 获取 loader 的 options | this.getOptions(schema) | this.emitFile | 产生一个文件 | this.emitFile(name, content, sourceMap) | this.utils.contextify | 返回一个相对路径 | this.utils.contextify(context, request) | this.utils.absolutify | 返回一个绝对路径 | this.utils.absolutify(context, request) |
3. 自定义Loader
3.1 clean-log-loader
需求:清理 js 代码中的console.log
- 在
loaders 文件夹下新建clean-log-loader/clean-log-loader.js 文件
module.exports = function cleanLogLoader(content) {
return content.replace(/console\.log\(.*\);?/g, "");
};
- 在
webpack.config.js 文件中,使用该Loader
module: {
rules: [
{
test: /\.js$/,
loader: './loaders/clean-log-loader/clean-log-loader.js',
},
],
},
3.2 banner-loader
需求:给 js 代码添加文本注释
- 在
loaders 文件夹下新建banner-loader/banner-loader.js 和schema.json 文件
const schema = require("./schema.json");
module.exports = function (content) {
const options = this.getOptions(schema);
const prefix = `
/*
* Author: ${options.author}
*/
`;
return prefix + content;
};
loaders/banner-loader/schema.json 文件可以对在配置loader 时的参数进行校验
{
"type": "object",
"properties": {
"author": {
"type": "string"
}
},
"additionalProperties": false
}
{
test: /\.js$/,
loader: "./loaders/banner-loader/banner-loader.js",
options: {
author: "scorpios",
},
},
3.3 babel-loader
需求:编译 js 代码,将 ES6+ 语法编译成 ES5 语法。(虽然这个功能已经有现场的loader ,但可以自己模拟,加深理解)
npm i @babel/core @babel/preset-env -D
- 在
loaders 文件夹下新建babel-loader/babel-loader.js 和schema.json 文件
const babel = require("@babel/core");
const schema = require("./schema.json");
module.exports = function (content) {
const callback = this.async();
const options = this.getOptions(schema);
babel.transform(content, options, function (err, result) {
if (err) callback(err);
else callback(null, result.code);
});
};
loaders/banner-loader/schema.json
{
"type": "object",
"properties": {
"presets": {
"type": "array"
}
},
"additionalProperties": true
}
{
test: /\.js$/,
loader: "./loaders/babel-loader/babel-loader.js",
options: {
presets: ["@babel/preset-env"],
},
}
3.4 file-loader
需求:将文件原封不动输出出去
npm i loader-utils -D
- 在
loaders 文件夹下新建file-loader/file-loader.js 文件
const loaderUtils = require("loader-utils");
module.exports = function (content) {
let interpolatedName = loaderUtils.interpolateName(this, "[hash].[ext][query]", {
content,
});
interpolatedName = `images/${interpolatedName}`
this.emitFile(interpolatedName, content);
return `module.exports = "${interpolatedName}"`;
};
module.exports.raw = true;
{
test: /\.(png|jpe?g|gif)$/,
loader: "./loaders/file-loader/file-loader.js",
type: "javascript/auto",
},
4. 小结
Loader 就是一个函数, 当webpack 解析资源时,会调用相应的Loader 去处理, Loader 接受到文件内容作为参数,返回内容出去。
content : 文件内容map : SourceMap meta : 别的Loader 传递的数据
|