vite-plugin-preloader
Version:
🚀 Vite plugin for intelligent route preloading - 智能路由预加载插件
290 lines (260 loc) • 9.32 kB
JavaScript
// 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