当前位置: 首页 > news >正文

前端工程化资源预加载

资源预加载

1、通过函数实现
/*** 并发预加载文件(支持图片、字体、脚本、样式等)* @param {string[]} files - 文件 URL 数组* @param {number} maxConcurrent - 最大并发数,默认 3* @returns {Promise<{ loaded: string[], failed: { url: string, error: string }[] }>}*/
export async function preloadFiles(files, maxConcurrent = 3) {// 输入验证if (!files || !Array.isArray(files) || files.length === 0) {throw new Error('必须提供有效的文件数组');}if (typeof maxConcurrent !== 'number' || maxConcurrent < 1) {throw new Error('maxConcurrent 必须是大于 0 的数字');}// 去重 + 过滤无效 URLconst uniqueFiles = [...new Set(files)].filter(file => {try {new URL(file);return true;} catch {console.warn(`无效的 URL: ${file}`);return false;}});if (uniqueFiles.length === 0) {return { loaded: [], failed: [] };}const results = {loaded: [],failed: []};// 使用索引代替 shift(),避免 O(n) 操作let currentIndex = 0;// 根据扩展名确定 as 类型const getTypeFromExtension = (url) => {const extension = url.split('.').pop().toLowerCase().split(/\#|\?/)[0]; // 忽略 query/hashconst typeMap = {'jpg': 'image', 'jpeg': 'image', 'png': 'image', 'gif': 'image', 'webp': 'image', 'avif': 'image','woff': 'font', 'woff2': 'font', 'ttf': 'font', 'eot': 'font', 'otf': 'font','js': 'script', 'mjs': 'script','css': 'style','json': 'json','xml': 'xml','svg': 'image','ico': 'image'};return typeMap[extension] || 'fetch';};// 预加载单个文件,返回 Promiseconst preloadFile = (url) => {return new Promise((resolve, reject) => {const link = document.createElement('link');link.rel = 'preload';link.as = getTypeFromExtension(url);link.href = url;// 加载成功link.onload = () => {results.loaded.push(url);resolve();};// 加载失败link.onerror = () => {const errorMsg = `Failed to preload: ${url}`;results.failed.push({ url, error: errorMsg });console.warn(errorMsg);reject(new Error(errorMsg));};document.head.appendChild(link);});};// 工作器函数:使用索引安全递增,不依赖 shift()const worker = async () => {while (currentIndex < uniqueFiles.length) {const url = uniqueFiles[currentIndex];currentIndex++; // 先取后加,确保每个 URL 只被一个 worker 处理await preloadFile(url); // 等待完成(串行发起,但多个 worker 并发执行)}};// 创建并发工作器(最多 maxConcurrent 个)const workers = Array(Math.min(maxConcurrent, uniqueFiles.length)).fill(null).map(() => worker());// 等待所有工作器完成await Promise.allSettled(workers);return results;
}
2、通过插件实现

vite.config.ts

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { preloadFiles } from './plugins/preloadFiles'export default defineConfig({plugins: [vue(),preloadFiles({dir: 'prefetch/*.{js,css,png,jpg,jpeg,gif,webp,woff,woff2}',rel: 'prefetch' // 或 preload})],resolve: {alias: {'@': path.resolve(__dirname, 'src')}}
})

plugins/preloadFiles.ts

import type { Plugin } from 'vite'
import fg from 'fast-glob'interface PreloadFileOption {dir: stringrel: 'prefetch' | 'preload'
}// 根据扩展名推断 as 类型
function getTypeFromExtension(url: string): string {const ext = url.split('.').pop()?.toLowerCase().split(/[?#]/)[0]const map: Record<string, string> = {// imagesjpg: 'image', jpeg: 'image', png: 'image', gif: 'image',webp: 'image', avif: 'image', svg: 'image', ico: 'image',// fontswoff: 'font', woff2: 'font', ttf: 'font', eot: 'font', otf: 'font',// scriptsjs: 'script', mjs: 'script',// stylescss: 'style',// datajson: 'json', xml: 'xml'}return map[ext!] || 'fetch'
}export const preloadFiles = (option: PreloadFileOption): Plugin => {const { dir, rel = 'prefetch' } = optionreturn {name: 'vite-plugin-file-preload',transformIndexHtml(html, ctx) {const publicDir = ctx.server?.config.publicDir || 'public'const base = ctx.server?.config?.base || '/'// 使用 fast-glob 匹配文件const matchedFiles = fg.sync(dir, {cwd: publicDir,absolute: false,onlyFiles: true})// 构造 HTML 注入标签return matchedFiles.map(file => {const href = base + fileconst tagConfig = {tag: 'link',attrs: {rel,href} as Record<string, string>}// 只有 preload 才需要 as 属性if (rel === 'preload') {tagConfig.attrs.as = getTypeFromExtension(href)}return tagConfig})}}
}

文章转载自:

http://4NTlbTOR.yhywr.cn
http://o5xD2TT9.yhywr.cn
http://XGU2xE8Y.yhywr.cn
http://FgDIB6Kl.yhywr.cn
http://COa6gzRk.yhywr.cn
http://S9jHW9hb.yhywr.cn
http://gSQt6maP.yhywr.cn
http://SgRX9gOD.yhywr.cn
http://W8KGtRZp.yhywr.cn
http://hELpHoQ9.yhywr.cn
http://FoPsREKB.yhywr.cn
http://PgPxmsZs.yhywr.cn
http://5cxIpBLc.yhywr.cn
http://7QrFWkxX.yhywr.cn
http://h1tKhHGO.yhywr.cn
http://zSi7u7jV.yhywr.cn
http://X2981KLz.yhywr.cn
http://o4RtGz5j.yhywr.cn
http://XqFS34Oo.yhywr.cn
http://NhdFwqHh.yhywr.cn
http://OdM9Q4cu.yhywr.cn
http://tL3qeJeq.yhywr.cn
http://HlCuxpDT.yhywr.cn
http://DZtdK4rE.yhywr.cn
http://oiQalmdE.yhywr.cn
http://CDbyAf3M.yhywr.cn
http://NbefdEqb.yhywr.cn
http://2kpfhr5J.yhywr.cn
http://zghT1HW8.yhywr.cn
http://oHZopOig.yhywr.cn
http://www.dtcms.com/a/375754.html

相关文章:

  • Linux-Shell编程正则表达式
  • CentOS7静态IP设置全攻略
  • Kafka面试精讲 Day 12:副本同步与数据一致性
  • [职业竞赛][移动应用]网络请求、JSON 文件读取解析、APP全局变量
  • 2、Python函数设计与字典应用
  • 数据分析与AI丨如何用数据分析找到更优的橡胶配方?
  • Flask 核心基础:从 路由装饰器 到 __name__ 变量 的底层逻辑解析
  • 微服务事务管理利器:Seata 核心原理与实践指南
  • ZYNQ PS 端 UART 接收数据数据帧(初学者友好版)
  • 【ARM-day03】
  • TI-92 Plus计算器:单位换算功能介绍
  • TDengine 选择函数 Max() 用户手册
  • 总结 IO、存储、硬盘、文件系统相关常识
  • MATLAB基于GM(灰色模型)与LSTM(长短期记忆网络)的组合预测方法
  • cnn,vit,mamba是如何解决医疗影像问题的
  • 数据库连接池:性能优化的秘密武器
  • 鸿蒙(HarmonyOS) 历史
  • 华为Ai岗机考20250903完整真题
  • 机器人控制器开发(文章总览)
  • 怎么选适合企业的RPA财务机器人?
  • Vite:Next-Gen Frontend Tooling 的高效之道——从原理到实践的性能革命
  • 常用优化器及其区别
  • 【Ansible】管理变量和事实知识点
  • 2025-09-08升级问题记录:app提示“此应用专为旧版Android打造..”或“此应用与最新版 Android 不兼容”
  • 网络通信的“地址”与“门牌”:详解IP地址与端口号的关系
  • 基于Python的旅游数据分析可视化系统【2026最新】
  • Nginx 优化与防盗链全解析:从性能调优到资源保护
  • 【AI】Tensorflow在jupyterlab中运行要注意的问题
  • (论文速读)从语言模型到通用智能体
  • 3-9〔OSCP ◈ 研记〕❘ WEB应用攻击▸利用REST API提权