Skip to content

实现file-loader和url-loader

本节来实现一下file-loader和url-loader。

webpack中是无法处理图片的,所以就需要编写loader来处理。

  1. file-loader,根据图片生成一个md5戳,发射到打包目录下,并返回一个路径,然后引用的就是打包后的路径,所以图片能正常显示
  2. 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来处理。

  1. file-loader,读取原来的图片,使用buffer二进制的形式,将文件发射到打包目录下,并返回一个新路径,然后引用的就是打包后的路径,所以图片能正常显示
  2. url-loader,可以设置一些配置项,如果图片很小。打包的时候可以转换为base64的格式,这样就减少了http请求,起到一个性能优化的作用,依赖file-loader,因为在转换大图片的时候,使用的还是file-loader

实现的过程中有下面几个注意项

  1. loader读取文件时,使用二进制的形式读取

    js
    loader.raw = true // 读取的图片转换为buffer 二进制
    // 不开启的话,读取的资源就跟文本打开图片一样,一堆乱码
  2. loaderUtils.interpolateName 可以读取到原来的图片,生成一个新的图片路径,发射图片时,发射新的图片路径

    js
    let loaderUtils = require('loader-utils')
    
    // 将读取到的图片,发射到打包目录下,然后生成一个新的路径,返回出去
    let filename = loaderUtils.interpolateName(this, '[hash].[ext]', {
        content: source
    }) // 使用loader-utils 生成一个新的名字
    
    // 发射图片到打包目录下
    this.emitFile(filename, source)
  3. 二进制的buffer文件,可以直接.length获取文件大小

  4. 二进制的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指向来调用
    }
  5. 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
  6. 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
  7. 配置

    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