实现file-loader和url-loader
本节来实现一下file-loader和url-loader。
webpack中是无法处理图片的,所以就需要编写loader来处理。
- file-loader,根据图片生成一个md5戳,发射到打包目录下,并返回一个路径,然后引用的就是打包后的路径,所以图片能正常显示
- url-loader,可以设置一些配置项,如果图片很小。打包的时候可以转换为base64的格式,这样就减少了http请求,起到一个性能优化的作用,依赖file-loader,因为在转换大图片的时候,使用的还是file-loader
本节来手动实现一下这两个loader。
准备工作
准备一张图片,放到src目录下
安装几个工具包,写loader的时候用
shell
yarn add mime@^3.0.0 -D
修改index.js
js
// webpack\webpack-loader\src\index.js
import aa from './aa.jpg' // 引入图片
let img = document.createElement('img')
img.src = aa
document.body.appendChild(img)
实现file-loader
修改配置文件
js
// webpack\webpack-loader\webpack.config.js
let path = require('path')
module.exports = {
mode: 'development',
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js'
},
module: {
rules: [
{
test: /\.jpg/,
// 根据图片生成一个md5戳,发射到打包目录下,然后返回一个打包后的图片路径
use: 'file-loader', // 自己写一个file-loader
},
{
test: /\.js$/,
use: {
loader: 'banner-loader', // 自创一个banner-loader
// 给所有匹配到的js模块,增加一个额外的注释
// 比如 /** cheny && xzz 2022-02-06 17:08 */
options: {
text: 'cheny && xzz', // 默认作者的名字
filename: path.resolve(__dirname, 'banner.txt'), // 如果没有给text,那么就读取这个文件中的内容
}
}
},
]
},
resolveLoader: {
// 找loader的时候,先从node_modules中找,找不到再从我们的loaders目录下找
modules: ['node_modules', path.resolve(__dirname, 'loaders')]
/* alias: { // loader的别名
loader1: path.resolve(__dirname, 'loaders', 'loader1.js')
} */
}
}
file-loader
js
// webpack\webpack-loader\loaders\file-loader.js
let loaderUtils = require('loader-utils')
// file-loader
function loader(source) {
console.log('file-loader 加载了');
// 将读取到的图片,发射到打包目录下,然后生成一个新的路径,返回出去
let filename = loaderUtils.interpolateName(this, '[hash].[ext]', {
content: source
}) // 使用loader-utils 生成一个新的名字
// 发射图片到打包目录下
this.emitFile(filename, source)
// 返回新路径,已commonjs模块的方式
return `module.exports = "${filename}"`
}
loader.raw = true // 读取的图片转换为buffer 二进制
// 不开启的话,读取的资源就跟文本打开图片一样,一堆乱码
module.exports = loader
打包看下效果, npx webpack ,成功把图片发射到了dist目录下,并且在浏览器中是可以访问到的
实现url-loader
修改配置文件
js
// webpack\webpack-loader\webpack.config.js
let path = require('path')
module.exports = {
mode: 'development',
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js'
},
module: {
rules: [
{
test: /\.jpg/,
// 根据图片生成一个md5戳,发射到打包目录下,然后返回一个打包后的图片路径
// use: 'file-loader', // 自己写一个file-loader
use: {
// 可以设定一个限制,将图片转换为base64,减少http请求
loader: 'url-loader', // 自己写一个url-loader
options: {
limit: 100 * 1024, // 如果图片小于100k,就直接将图片转换为base64
// 大于100k,就会用file-loader去处理图片
}
}
},
{
test: /\.js$/,
use: {
loader: 'banner-loader', // 自创一个banner-loader
// 给所有匹配到的js模块,增加一个额外的注释
// 比如 /** cheny && xzz 2022-02-06 17:08 */
options: {
text: 'cheny && xzz', // 默认作者的名字
filename: path.resolve(__dirname, 'banner.txt'), // 如果没有给text,那么就读取这个文件中的内容
}
}
},
]
},
resolveLoader: {
// 找loader的时候,先从node_modules中找,找不到再从我们的loaders目录下找
modules: ['node_modules', path.resolve(__dirname, 'loaders')]
/* alias: { // loader的别名
loader1: path.resolve(__dirname, 'loaders', 'loader1.js')
} */
}
}
url-loader
js
// webpack\webpack-loader\loaders\url-loader.js
let loaderUtils = require('loader-utils')
let mime = require('mime') // 获取资源的 mime 格式,比如.jpg -> image/jpeg
// url-loader
// 图片设置了一个限制,如果小于这个值,会将图片转换为base64
// 大于这个限制,就使用file-loader来处理图片
function loader(source) {
console.log('url-loader 加载了');
let { limit } = loaderUtils.getOptions(this)
// source.length 二进制的大小
// 如果图片在阈值内,就转换为base64
if (limit && limit > source.length) {
// base64 前面一部分都是差不多的
// mime.getType(this.resourcePath) 会获取到文件的mime格式
// .jpg -> image/jpeg
return `module.exports = "data:${mime.getType(this.resourcePath)};base64,${source.toString('base64')}"`
} else {
// 如果比阈值大,就还用file-loader处理图片
return require('./file-loader').call(this, source) // 使用call绑定一下this指向来调用
}
}
loader.raw = true // 读取到的资源转换为buffer二进制
module.exports = loader
打包看下效果, npx webpack ,成功把图片发射到了dist目录下,并且在浏览器中是可以访问到的
总结
webpack中是无法处理图片的,所以就需要编写loader来处理。
- file-loader,读取原来的图片,使用buffer二进制的形式,将文件发射到打包目录下,并返回一个新路径,然后引用的就是打包后的路径,所以图片能正常显示
- url-loader,可以设置一些配置项,如果图片很小。打包的时候可以转换为base64的格式,这样就减少了http请求,起到一个性能优化的作用,依赖file-loader,因为在转换大图片的时候,使用的还是file-loader
实现的过程中有下面几个注意项
loader读取文件时,使用二进制的形式读取
jsloader.raw = true // 读取的图片转换为buffer 二进制 // 不开启的话,读取的资源就跟文本打开图片一样,一堆乱码
loaderUtils.interpolateName 可以读取到原来的图片,生成一个新的图片路径,发射图片时,发射新的图片路径
jslet loaderUtils = require('loader-utils') // 将读取到的图片,发射到打包目录下,然后生成一个新的路径,返回出去 let filename = loaderUtils.interpolateName(this, '[hash].[ext]', { content: source }) // 使用loader-utils 生成一个新的名字 // 发射图片到打包目录下 this.emitFile(filename, source)
二进制的buffer文件,可以直接.length获取文件大小
二进制的buffer可以直接使用toString('base64')方法,转换为base64格式
js// source.length 二进制的大小 // 如果图片在阈值内,就转换为base64 if (limit && limit > source.length) { // base64 前面一部分都是差不多的 // mime.getType(this.resourcePath) 会获取到文件的mime格式 // .jpg -> image/jpeg return `module.exports = "data:${mime.getType(this.resourcePath)};base64,${source.toString('base64')}"` } else { // 如果比阈值大,就还用file-loader处理图片 return require('./file-loader').call(this, source) // 使用call绑定一下this指向来调用 }
file-loader
js// webpack\webpack-loader\loaders\file-loader.js let loaderUtils = require('loader-utils') // file-loader function loader(source) { console.log('file-loader 加载了'); // 将读取到的图片,发射到打包目录下,然后生成一个新的路径,返回出去 let filename = loaderUtils.interpolateName(this, '[hash].[ext]', { content: source }) // 使用loader-utils 生成一个新的名字 // 发射图片到打包目录下 this.emitFile(filename, source) // 返回新路径,已commonjs模块的方式 return `module.exports = "${filename}"` } loader.raw = true // 读取的图片转换为buffer 二进制 // 不开启的话,读取的资源就跟文本打开图片一样,一堆乱码 module.exports = loader
url-loader
js// webpack\webpack-loader\loaders\url-loader.js let loaderUtils = require('loader-utils') let mime = require('mime') // 获取资源的 mime 格式,比如.jpg -> image/jpeg // url-loader // 图片设置了一个限制,如果小于这个值,会将图片转换为base64 // 大于这个限制,就使用file-loader来处理图片 function loader(source) { console.log('url-loader 加载了'); let { limit } = loaderUtils.getOptions(this) // source.length 二进制的大小 // 如果图片在阈值内,就转换为base64 if (limit && limit > source.length) { // base64 前面一部分都是差不多的 // mime.getType(this.resourcePath) 会获取到文件的mime格式 // .jpg -> image/jpeg return `module.exports = "data:${mime.getType(this.resourcePath)};base64,${source.toString('base64')}"` } else { // 如果比阈值大,就还用file-loader处理图片 return require('./file-loader').call(this, source) // 使用call绑定一下this指向来调用 } } loader.raw = true // 读取到的资源转换为buffer二进制 module.exports = loader
配置
jsmodule: { rules: [ { test: /\.jpg/, // 根据图片生成一个md5戳,发射到打包目录下,然后返回一个打包后的图片路径 // use: 'file-loader', // 自己写一个file-loader use: { // 可以设定一个限制,将图片转换为base64,减少http请求 loader: 'url-loader', // 自己写一个url-loader options: { limit: 100 * 1024, // 如果图片小于100k,就直接将图片转换为base64 // 大于100k,就会用file-loader去处理图片 } } }, ] }, resolveLoader: { // 找loader的时候,先从node_modules中找,找不到再从我们的loaders目录下找 modules: ['node_modules', path.resolve(__dirname, 'loaders')] /* alias: { // loader的别名 loader1: path.resolve(__dirname, 'loaders', 'loader1.js') } */ } }
参考
https://www.bilibili.com/video/BV1a4411e7Bz?p=44&spm_id_from=pageDriver