Vite自动导入指定目录内的静态资源文件,并生成导出模块

135 阅读2分钟

原文:nicen.cn/7098.html

项目中经常会用到很多图片,用的时候每次都得写很多import,假如能够自动引入指定目录下所有图片的资源链接到一个公共对象上面,这样的话使用某个图片就只需要引入这个对象。

尝试了网上很多现成的自动引入图片的插件,但是都封装成了单个组件,于是就有了下面这些代码。

1.最终效果

使用方法如下:

// 从生成的模块中引入
import images from '@/config/images';

// 模板中使用
// 代表调用目录内名称是smile的资源文件
<img :src='images.smile'/>

2.插件代码

import fs from 'fs/promises';
import path from 'path';

/**
 * 递归读取目录下的所有文件
 * @param dir
 * @returns {Promise<unknown[]>}
 */
async function readDirectoryFiles(dir) {
    const dirs = await fs.readdir(dir, {withFileTypes: true});
    const files = await Promise.all(dirs.map(dirent => {
        const res = path.resolve(dir, dirent.name);
        return dirent.isDirectory() ? readDirectoryFiles(res) : res;
    }));
    return Array.prototype.concat(...files);
}


/**
 * 创建目录,如果目录不存在
 * @param dir
 * @returns {Promise<void>}
 */
async function ensureDirExists(dir) {
    try {
        await fs.mkdir(dir, {recursive: true});
    } catch (err) {
        if (err.code !== 'EEXIST') {
            throw err;
        }
    }
}

/**
 * JS关键字
 * @type {string[]}
 */
const keywords = [
    'break', 'case', 'catch', 'class', 'const', 'continue', 'debugger',
    'default', 'delete', 'do', 'else', 'export', 'extends', 'finally',
    'for', 'function', 'if', 'import', 'in', 'instanceof', 'new',
    'return', 'super', 'switch', 'this', 'throw', 'try', 'typeof',
    'var', 'void', 'while', 'with', 'let', 'yield'
];


/**
 * 定义插件
 * @param options
 * @returns {{name: string, config(): Promise<void>}}
 */
export default function createAuto(options = {}) {

    const {
        image, // 默认的图片文件目录
        output, // 输出 JS 文件的路径
        prefix, //import的前缀
        type //需要识别的文件类型
    } = options;


    return {
        name: 'vite-plugin-autoload-image',
        async config() {

            /* 参数不完整,终止代码 */
            if (!image || !output || !prefix || !type) {
                return;
            }

            /* 读取指定目录下的所有文件 */
            const files = await readDirectoryFiles(image);

            /* 过滤出所有的指定类型的文件 */
            const imageFiles = files.filter(file => type.indexOf(path.extname(file)) > -1);

            /* 所有文件的名称 */
            const filesName = [];

            /* 为每个 image 文件生成 import 语句 */
            const imports = imageFiles.map(file => {

                /* 路径判断 */
                const relativePath = prefix + path
                    .relative(path.dirname(image), file)
                    .replace(/\/g, '/');

                const fileName = path.basename(file, path.extname(file))
                    .replace(/[^0-9a-zA-Z]/g, "_");

                /* 排除js关键字 */
                if (keywords.indexOf(fileName) > -1 || filesName.indexOf(fileName) > -1) {
                    filesName.push(`_${fileName}`); //记录文件名
                    return `import _${fileName} from '${relativePath}';`;
                } else {
                    filesName.push(fileName); //记录文件名
                    return `import ${fileName} from '${relativePath}';`;
                }
            });


            /* 确保输出文件的目录存在 */
            const outputDir = path.dirname(output);
            await ensureDirExists(outputDir);

            /* 将所有 import 和 export 语句写入到指定的 JS 文件中 */
            const content = imports.join('\n') + "\n\n\n\n\n" + `export default {\n\t${filesName.join(",\n\t")}\n}`;
            await fs.writeFile(output, content, 'utf-8');

            /* 拼接ts类型 */
            const types = filesName.map(item => {
                return `\t\t\t${item} : string`
            }).join(",\n");

            /* 添加ts类型,让IDE能够识别 */
            await fs.writeFile(`${outputDir}/global.d.ts`, `
declare module "@vue/runtime-core" {
    export interface ComponentCustomProperties {
        images: { \n ${types} \n\t\t\t} 
    }
}

export {}
                                            `, 'utf-8');

        },
    };
}

3.Vite配置

export default defineConfig({
    plugins: [
        vue(),
        createAuto({
            /* 需要引入的图片目录 */
            image: 'src/assets',
            /* 输出的模块路径 */
            output: 'src/config/images.js',
            /* 引入时添加的前缀 */
            prefix: '@/',
            /* 引入哪些类型的文件 */
            type: ['.jpg', '.svg', '.png']
        }),
    ],
}