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

10,757次阅读
没有评论

共计 3930 个字符,预计需要花费 10 分钟才能阅读完成。

上一次详细学习 Vite 的文档的时候还是 Vite2.x,现在 Vite 已经到了 Vite5,在此期间已经有了很多新的变化和特性,刚好今天抽空重新学习一下。

关于 Vite5

Vite 不再支持 Node.js 14 / 16 / 17 / 19,因为它们已经到了 EOL。现在需要 Node.js 18 / 20+。

查看 Vite 预设的各种开发模板:https://github.com/vitejs/vite/tree/main/packages/create-vite

社区维护的各种开发模板:https://github.com/vitejs/awesome-vite#templates

1. 库模式

可用于快速便捷的开发 JS 库,库模式打包的时候将打包单独的 js 文件。

相关文档:https://cn.vitejs.dev/guide/build.html#library-mode

2.server.hmr

进行公众号网页开发的时候,需要解析实际的域名并配置 https 才能进行授权操作。

一般会通过内网穿透来进行开发,但是内网穿透需要自己搭建代理,有时候请求速度还不尽如人意。

所以我会选择把域名解析到本地的局域网,然后反向代理到开发服务器。

印象中在以前版本的 Vite 中,热重载依赖的 Websocker 服务器端口是跟随当前访问的地址,当配置如下:

{
  server: {
    hmr: {path: "/ws"}
  }
}

解析好域名(dev.nicen.cn)并配置好反向代理:

location /
{
    proxy_set_header Connection "keep-alive";
    proxy_set_header X-Real-IP $remote_addr;
    fastcgi_connect_timeout 600;
    fastcgi_send_timeout 600;
    fastcgi_read_timeout 600;

    if (!-f $request_filename) {proxy_pass http://192.168.2.63:4001;}

}

location /ws {
	proxy_pass http://192.168.2.63:4001;
	proxy_http_version 1.1;
	proxy_read_timeout 3600s;
	proxy_send_timeout 300s;
	proxy_set_header Upgrade $http_upgrade;
	proxy_set_header Connection "upgrade";
}

以前 Vite 连接的 websocket 地址会是:wss://dev.nicen.cn/ws

现在 Vite 连接的 websocket 地址会是:wss://dev.nicen.cn:4001/ws

所以现在需要将 vite 配置改为:

{
  server: {
    hmr: {
       clientPort: 443,
       path: "/ws"
    }
  }
}

Vite 插件开发

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

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

1. 最终效果

使用方法如下:

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

// 模板中使用
// 代表调用目录内名称是 smile 的资源文件
Vite 如何自动导入指定目录内的静态资源文件,并生成导出模块?

2. 插件代码

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

/**
 * 递归读取目录下的所有文件
 * @param dir
 * @returns {Promise}
 */
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}
 */
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}}
 */
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') + "nnnnn" + `export default {nt${filesName.join(",nt")}n}`;
            await fs.writeFile(output, content, 'utf-8');

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

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

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

        },
    };
}

3.Vite 配置

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

    正文完
     0
    Yojack
    版权声明:本篇文章由 Yojack 于2024-06-28发表,共计3930字。
    转载说明:
    1 本网站名称:优杰开发笔记
    2 本站永久网址:https://yojack.cn
    3 本网站的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,请联系站长进行删除处理。
    4 本站一切资源不代表本站立场,并不代表本站赞同其观点和对其真实性负责。
    5 本站所有内容均可转载及分享, 但请注明出处
    6 我们始终尊重原创作者的版权,所有文章在发布时,均尽可能注明出处与作者。
    7 站长邮箱:laylwenl@gmail.com
    评论(没有评论)