UNPKG

vite-plugin-preloader

Version:

🚀 Vite plugin for intelligent route preloading - 智能路由预加载插件

290 lines (260 loc) 9.32 kB
// vite-plugin-preloader - 智能路由预加载插件 "use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var index_exports = {}; __export(index_exports, { default: () => preloaderPlugin }); module.exports = __toCommonJS(index_exports); // src/runtime.ts var runtimeTemplate = `// \u{1F680} Auto-generated by vite-plugin-preloader (function() { 'use strict'; // \u{1F3AF} \u9884\u52A0\u8F7D\u914D\u7F6E\uFF08\u6784\u5EFA\u65F6\u6CE8\u5165\uFF09 const PRELOAD_ROUTES = __PRELOAD_ROUTES__ const PRELOAD_OPTIONS = __PRELOAD_OPTIONS__ class PreloaderManager { constructor() { this.preloadedRoutes = new Set() this.isPreloading = false this.stats = { total: 0, completed: 0, failed: 0, startTime: 0, endTime: 0 } } async start() { if (this.isPreloading) return this.isPreloading = true this.stats = { total: PRELOAD_ROUTES.length, completed: 0, failed: 0, startTime: Date.now(), endTime: 0 } if (PRELOAD_OPTIONS.debug) { console.log(\`\u{1F680} [\u9884\u52A0\u8F7D] \u5F00\u59CB\u9884\u52A0\u8F7D \${PRELOAD_ROUTES.length} \u4E2A\u9875\u9762\`) } const sortedRoutes = [...PRELOAD_ROUTES].sort((a, b) => a.priority - b.priority) for (const route of sortedRoutes) { await this.preloadSingle(route) await this.sleep(100) } this.stats.endTime = Date.now() this.isPreloading = false if (PRELOAD_OPTIONS.debug) { console.log(\`\u{1F389} [\u9884\u52A0\u8F7D] \u5B8C\u6210! \u8017\u65F6 \${this.stats.endTime - this.stats.startTime}ms\`) } } async preloadSingle(route) { if (this.preloadedRoutes.has(route.path)) return try { const startTime = Date.now() let componentPath = route.component // \u5728\u5F00\u53D1\u73AF\u5883\u4E2D\uFF0CVite \u4F1A\u5904\u7406\u6A21\u5757\u89E3\u6790 // \u6211\u4EEC\u9700\u8981\u4F7F\u7528\u6B63\u786E\u7684\u6A21\u5757 ID if (componentPath.startsWith('@/')) { // \u5BF9\u4E8E Vite\uFF0C@ \u522B\u540D\u5E94\u8BE5\u5728\u6784\u5EFA\u65F6\u5C31\u88AB\u89E3\u6790 // \u4F46\u5728\u8FD0\u884C\u65F6\u6211\u4EEC\u9700\u8981\u4F7F\u7528\u5B9E\u9645\u7684\u8DEF\u5F84 componentPath = componentPath.replace('@/', '/src/') } // \u786E\u4FDD\u8DEF\u5F84\u4EE5 / \u5F00\u5934\uFF08\u76F8\u5BF9\u4E8E\u9879\u76EE\u6839\u76EE\u5F55\uFF09 if (!componentPath.startsWith('/') && !componentPath.startsWith('./')) { componentPath = '/' + componentPath } if (PRELOAD_OPTIONS.debug) { console.log(\`\u{1F50D} [\u9884\u52A0\u8F7D] \u5C1D\u8BD5\u52A0\u8F7D: \${componentPath}\`) } const module = await import(/* @vite-ignore */ componentPath) const loadTime = Date.now() - startTime this.preloadedRoutes.add(route.path) this.stats.completed++ if (PRELOAD_OPTIONS.debug) { console.log(\`\u2705 [\u9884\u52A0\u8F7D] \${route.path} (\${loadTime}ms) - \${route.reason}\`) } } catch (error) { this.stats.failed++ if (PRELOAD_OPTIONS.debug) { console.error(\`\u274C [\u9884\u52A0\u8F7D] \${route.path} \u5931\u8D25:\`, error) } } } sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)) } isPreloaded(path) { return this.preloadedRoutes.has(path) } getStats() { return { ...this.stats, preloadedPaths: Array.from(this.preloadedRoutes), isPreloading: this.isPreloading } } } // \u{1F680} \u5168\u5C40\u5B9E\u4F8B const preloader = new PreloaderManager() // \u{1F6E0}\uFE0F \u5F00\u53D1\u73AF\u5883\u8C03\u8BD5\u5DE5\u5177 if (PRELOAD_OPTIONS.debug) { window.preloaderDebug = { stats: () => preloader.getStats(), restart: () => preloader.start(), check: (path) => preloader.isPreloaded(path), help: () => console.log('\u{1F6E0}\uFE0F \u9884\u52A0\u8F7D\u8C03\u8BD5: stats() | restart() | check(path)') } console.log('\u{1F6E0}\uFE0F \u9884\u52A0\u8F7D\u8C03\u8BD5\u5DE5\u5177: window.preloaderDebug') } // \u{1F680} \u81EA\u52A8\u542F\u52A8 - \u7B49\u5F85 DOM \u52A0\u8F7D\u5B8C\u6210 function autoStart() { if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => { setTimeout(() => preloader.start(), PRELOAD_OPTIONS.delay) }) } else { setTimeout(() => preloader.start(), PRELOAD_OPTIONS.delay) } } // \u7ACB\u5373\u6267\u884C\u81EA\u52A8\u542F\u52A8 autoStart() // \u5BFC\u51FA\u5230\u5168\u5C40\uFF08\u53EF\u9009\u4F7F\u7528\uFF09 window.usePreloader = () => ({ start: () => preloader.start(), isPreloaded: (path) => preloader.isPreloaded(path), getStats: () => preloader.getStats() }) })();`; // src/generator.ts var CodeGenerator = class { constructor(options) { this.options = options; } /** * 生成运行时代码 */ generateRuntime() { const routes = this.processRoutes(); const options = this.processOptions(); return runtimeTemplate.replace("__PRELOAD_ROUTES__", JSON.stringify(routes, null, 2)).replace("__PRELOAD_OPTIONS__", JSON.stringify(options, null, 2)); } /** * 处理路由配置 */ processRoutes() { return this.options.routes.map((route) => { if (typeof route === "string") { const componentPath2 = this.inferComponentPath(route); return { path: route, component: componentPath2, reason: "\u81EA\u52A8\u63A8\u65AD\u7684\u9884\u52A0\u8F7D\u9875\u9762", priority: 2 }; } const componentPath = route.component || this.inferComponentPath(route.path); return { path: route.path, component: componentPath, reason: route.reason || "\u7528\u6237\u914D\u7F6E\u7684\u9884\u52A0\u8F7D\u9875\u9762", priority: route.priority || 2 }; }); } /** * 处理选项配置 */ processOptions() { const isDev = process.env.NODE_ENV !== "production"; return { delay: this.options.delay ?? 2e3, // 默认2秒 showStatus: this.options.showStatus ?? true, // 默认显示状态 statusPosition: this.options.statusPosition ?? "bottom-right", // 默认右下角 debug: this.options.debug ?? isDev // 开发环境默认开启调试,生产环境默认关闭 }; } /** * 推断组件路径 - 使用 Vite 别名格式 */ inferComponentPath(routePath) { const cleanPath = routePath.replace(/^\//, ""); if (cleanPath.startsWith("demo/")) { return `@/views/${cleanPath}/index.vue`; } const pathSegments = cleanPath.split("/"); return `@/views/${pathSegments.join("/")}/index.vue`; } /** * 生成注入到 HTML 头部的脚本 */ generateHtmlInject() { return `<script type="module"> ${this.generateRuntime()} </script>`; } }; // src/index.ts function preloaderPlugin(options) { let generator; const isDev = process.env.NODE_ENV !== "production"; const finalOptions = { debug: isDev, // 开发环境默认开启调试 delay: 2e3, // 默认2秒 showStatus: true, // 默认显示状态 statusPosition: "bottom-right", // 默认右下角 ...options // 用户配置覆盖默认配置 }; return { name: "vite-plugin-preloader", // 🎯 设置插件执行顺序 enforce: "post", configResolved() { generator = new CodeGenerator(finalOptions); if (finalOptions.debug) { console.log(`\u{1F680} [\u9884\u52A0\u8F7D\u63D2\u4EF6] \u5DF2\u542F\u7528\uFF0C\u9884\u52A0\u8F7D ${finalOptions.routes.length} \u4E2A\u9875\u9762\uFF0C\u8BE6\u60C5\u8BF7\u67E5\u770B\u6D4F\u89C8\u5668\u63A7\u5236\u53F0`); } }, // 🎨 HTML 转换 - 直接注入脚本到 HTML transformIndexHtml(html) { const inject = generator.generateHtmlInject(); return html.replace("</head>", `${inject} </head>`); }, // 🔥 HMR 支持 handleHotUpdate(ctx) { if (ctx.file.includes("vite.config")) { if (finalOptions.debug) { console.log("\u{1F504} [\u9884\u52A0\u8F7D\u63D2\u4EF6] \u914D\u7F6E\u5DF2\u66F4\u65B0"); } ctx.server.ws.send({ type: "full-reload" }); return []; } return void 0; } }; } //# sourceMappingURL=index.js.map