实现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读取文件时,使用二进制的形式读取 js- loader.raw = true // 读取的图片转换为buffer 二进制 // 不开启的话,读取的资源就跟文本打开图片一样,一堆乱码
- loaderUtils.interpolateName 可以读取到原来的图片,生成一个新的图片路径,发射图片时,发射新的图片路径 js- let 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
- 配置 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去处理图片 } } }, ] }, 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