loader模块的学习
这篇文档主要记录loader一些基础的知识点
loader文档:webpack.docschina.org/concepts/lo…
loader列表:webpack.docschina.org/loaders/
如何处理静态资源模块
1.处理图片或字体等
- file-loader:处理静态资源模块
所以我们什么时候⽤file-loader呢?
场景:就是当我们需要模块,仅仅是从源代码挪移到打包⽬录,就可以使⽤file-loader来处理, txt,svg,csv,excel,图⽚资源等等
npm install file-loader -D //安装
案例:处理图片格式文件
module: {
rules: [
{
test: /\.(png|jpe?g|gif)$/,
//use使⽤⼀个loader可以⽤对象,字符串,两个loader需要⽤数组
use: {
loader: "file-loader",
// options额外的配置,⽐如资源名称
options: {
// placeholder 占位符 [name]原本资源模块的名称
// [hash]随机生成的hash字符串
// [ext]原本资源模块的后缀
// https://webpack.js.org/loaders/file-loader#placeholders
name: "[name]_[hash].[ext]",
//打包后的存放位置
outputPath: "images/"
}
}
}
]
},
有关file-loader占位符的说明文档:webpack.docschina.org/loaders/fil…
处理字体
//css
@font-face {
font-family: "webfont";
font-display: swap;
src: url("webfont.woff2") format("woff2");
}
body {
background: blue;
font-family: "webfont" !important;
}
//webpack.config.js
{
test: /\.(eot|ttf|woff|woff2|svg)$/,
use: "file-loader"
}
- url-loader
url-loader 功能类似于 file-loader,但是在文件大小(单位 byte)低于指定的限制时,可以返回一个 DataURL
npm install --save-dev url-loader //安装
案例:
module: {
rules: [
{
test: /\.(png|jpe?g|gif)$/,
use: {
loader: "url-loader",
options: {
name: "[name]_[hash].[ext]",
outputPath: "images/",
//⼩于2048,才转换成base64
limit: 2048
}
}
}
]
},
2.处理样式
处理样式有很多个loader
- less-loader --处理less样式文件
- sass-loader --处理sass样式文件
- stylus-loader --处理stylus样式文件
- css-loader --处理css样式文件
- style-loader --将处理好的样式使用style标签插入文档
- postcss-loader --添加样式浏览器前缀,处理兼容性
案例:
//css样式处理
{
test: /\.css$/,
use: ["style-loader", "css-loader"]
}
//less样式处理
{
test: /\.less$/,
use: ["style-loader", "css-loader", "less-loader"]
}
loader有顺序,从右到左,从下到上
样式自动添加前缀:
Postcss-loader
npm i postcss-loader autoprefixer -D //安装
新建一个postcss.config.js
//webpack.config.js
{
test: /\.css$/,
use: ["style-loader", "css-loader", "postcss-loader"]
},
//postcss.config.js
//postcss 的配置
const autoprefixer = require("autoprefixer");
module.exports = {
plugins: [
autoprefixer({
//autoprefixer 根据我们的配置 自动的帮助我们做兼容
overrideBrowserslist: ["last 2 versions", ">1%"],
}),
],
};
loader的要点
- loader 处理webpack不⽀持的格式⽂件,模块
- ⼀个loader只处理⼀件事情
- loader有执⾏顺序
手写一个loader
⾃⼰编写⼀个Loader的过程是⽐较简单的,
Loader就是⼀个函数,声明式函数,不能⽤箭头函数
拿到源代码,作进⼀步的修饰处理,再返回处理后的源码就可以了
实现一个简单案例
1.创建⼀个替换源码中字符串的loader
//index.js
console.log("hello kkb");
//replaceLoader.js
module.exports = function (source) {
const result = source.replace("hello", "你好")
return result
}
需要⽤声明式函数,因为要上到上下⽂的this,⽤到this的数据,该函数接受⼀个参数,是源码
- 在配置⽂件中使⽤loader
//需要使⽤node核⼼模块path来处理路径
const path = require('path')
module: {
rules: [
{
test: /\.js$/,
use: path.resolve(__dirname, "./loader/replaceLoader.js")
}
]
},
2.如何给loader配置参数,loader如何接受参数?
- this.query
- loader-utils -- loader-utils是一个webpack工具类,通过一些方法配合loader处理文件。
//webpack.config.js
module: {
rules: [
{
test: /\.js$/,
use: [
{
loader: path.resolve(__dirname, "./myLoader/replaceLoader.js"),
options: {
name: "webpack"
}
}
]
}
]
},
// replaceLoader.js
// loader本质上是一个函数。但是不可以是箭头函数
// 拿到原内容,做进一步加工处理,处理后把加工后的内容返回
const loaderUtils = require("loader-utils");//官⽅推荐处理loader,query的⼯具
// source 来源
module.exports = function (source) {
const options = loaderUtils.getOptions(this);
// const result = source.replace("kkb", options.name);
return source.replace("哈哈哈哈", options.name);
}
// index.js
console.log("hello 哈哈哈哈!!!!");
打包完成后:
成功替换!
3.如何返回多个信息,不⽌是处理好的源码呢,可以使⽤this.callback来处理
// callback有四个参数
this.callback(
err: Error | null,
content: string | Buffer,
sourceMap ?: SourceMap,
meta ?: any);
//replaceLoader.js
// 使用this.callback返回最后的字符串
const loaderUtils = require("loader-utils");//官⽅推荐处理loader,query的⼯具
module.exports = function (source) {
const options = loaderUtils.getOptions(this);
const result = source.replace("哈哈哈哈", options.name);
this.callback(null, result);
};
4.如果loader⾥⾯有异步的事情要怎么处理呢
const loaderUtils = require("loader-utils");
module.exports = function(source) {
const options = loaderUtils.getOptions(this);
setTimeout(() => {
const result = source.replace("哈哈哈哈", options.name);
return result;
}, 1000);
};
//先⽤setTimeout处理下试试,发现会报错
出现报错
报错说明 无法获取到返回的buffer和字符串
再用this.async来处理,它会返回this.callback
const loaderUtils = require("loader-utils");
module.exports = function (source) {
const options = loaderUtils.getOptions(this);
//定义⼀个异步处理,告诉webpack,这个loader⾥有异步事件,在⾥⾯调⽤下这个异步
//callback 就是 this.callback 注意参数的使⽤
const callback = this.async();
setTimeout(() => {
const result = source.replace("哈哈哈哈", options.name);
callback(null, result);
}, 3000);
};
异步loader编译结果
5.多个自定义loader的组合使用
//replaceLoader.js
module.exports = function (source) {
return source.replace("哈哈哈哈", "webpack");
};
//replaceLoaderAsync.js
const loaderUtils = require("loader-utils");
module.exports = function (source) {
const options = loaderUtils.getOptions(this);
//定义⼀个异步处理,告诉webpack,这个loader⾥有异步事件,在⾥⾯调⽤下这个异步
const callback = this.async();
setTimeout(() => {
const result = source.replace("哈哈哈哈", options.name);
callback(null, result);
}, 3000);
};
//webpack.config.js
module: {
rules: [
{
test: /\.js$/,
use: [
path.resolve(__dirname, "./myLoader/replaceLoader.js"),
{
loader: path.resolve(__dirname, "./myLoader/replaceLoaderAsync.js"),
options: {
name: "webpack"
}
}
]
}
]
},
实现一套简版的css处理loader
我们可以来根据自定义loader的原理来实现一套简版的style-loader,css-loader,less-loader
//hless-loader.js
//引入less模块
const less = require("less");
module.exports = function (source) {
// 使用less render函数对less文件转换成css文件
less.render(source, (err, output) => {
this.callback(err, output.css);
});
};
//hcss-loader.js
//拿到返回的css 文件
module.exports = function (source) {
return source;
};
// htyle-loader.js
//动态生成style 把css插入
module.exports = function (source) {
console.log(source);
// 将拿到的字符串进行转义 插入到style标签里面 并添加到head头部
return `const tag = document.createElement('style');
tag.innerHTML = ${JSON.stringify(source)};
document.head.appendChild(tag)`;
};
//最后在配置中使用
{
test: /\.less$/,
use: [
"./myLoaders/hstyle-loader",
"./myLoaders/hcss-loader",
"./myLoaders/hless-loader",
],
},
处理loader的路径问题
resolveLoader字段可以告诉webpack去哪里找loader解析
//到哪去找loader
resolveLoader: {
modules: ["./node_modules", "./myLoaders"],
},
module: {
rules: [
{
test: /\.less$/,
use: [
"hstyle-loader",
"hcss-loader",
"hless-loader",
],
},
]
}
这样的话就不用加上长串路径前缀了
实现一个新的需求:将css文件模块单独抽离出来
mini-css-extract-plugin
作用:打包的时候将css文件单独抽离出来放在一个文件中
npm install mini-css-extract-plugin -D //安装
如何使用:
//webpack-config.js
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module: {
rules: [
{
test: /\.less$/,
use: [
{
loader: miniCssExtractPlugin.loader,
options: {
publicPath: "../",
},
},
"css-loader"
]
}
]
}
plugins: [
new miniCssExtractPlugin({
filename: "css/[name]-[contenthash].css",
}),
]
打包后的结果:
最后整理一道面试题
盘一盘下面几个hash的区别
- hash
- Chunkhash
- Contenthash
hash是跟整个项目的构建相关,构建生成的文件hash值都是一样的,所以hash计算是跟整个项目的构建相关,同一次构建过程中生成的hash都是一样的,只要项目里有任意文件更改,整个项目构建的hash值都会更改。
chunkhash和hash不一样,它根据不同的入口文件(Entry)进行依赖文件解析、构建对应的chunk,生成对应的hash值。我们在生产环境里把一些公共库和程序入口文件区分开,单独打包构建,接着我们采用chunkhash的方式生成hash值,那么只要我们不改动公共库的代码,就可以保证其hash值不会受影响。
contenthash表示由文件内容产生的hash值,内容不同产生的contenthash值也不一样。在项目中,通常做法是把项目中css都抽离出对应的css文件来加以引用。
js推荐使用Chunkhash
css推荐使用Contenthash