/** * 懒加载工具模块 * * 提供按需动态导入辅助函数,支持: * 1. 首次加载时自动调用初始化函数(如 bind*Controls) * 2. 包裹目标函数,实现 window.xxx = (...args) => 自动加载并调用 * 3. 模块缓存机制,避免重复加载 * 4. Alpine 组件懒加载:返回同步 stub,在 $watch 触发时异步加载真实组件 */ /** * 创建一个可延迟加载的模块引用。 * * @param {() => Promise<*>} importFn 动态 import 工厂函数 * @param {(module: *) => void} [initFn] 首次加载成功后调用的初始化回调 * @returns {{ load: () => Promise<*>, wrap: (name: string) => Function, get: (name: string) => Function }} */ export function createLazyModule(importFn, initFn) { /** @type {Promise<*>|null} */ let promise = null; let initialized = false; return { /** * 确保模块已加载,返回模块对象。 * @returns {Promise<*>} */ async load() { if (!promise) { promise = importFn().then((mod) => { if (!initialized && initFn) { initialized = true; initFn(mod); } return mod; }); } return promise; }, /** * 创建一个延迟执行的目标函数包装器。 * 首次调用时自动加载模块,之后直接调用缓存。 * * @param {string} fnName 模块中导出的函数名 * @returns {Function} */ wrap(fnName) { return async (...args) => { const mod = await this.load(); const fn = mod[fnName]; if (typeof fn !== "function") { throw new Error( `懒加载模块中找不到函数 "${fnName}"`, ); } return fn(...args); }; }, /** * 返回一个返回模块导出的 getter 函数。 * 适用于返回非函数值(如 Alpine 组件对象)。 * * @param {string} name 模块中导出的名称 * @returns {Function} */ get(name) { return async () => { const mod = await this.load(); return mod[name]; }; }, }; } /** * 创建 Alpine 组件延迟加载包装器。 * * Alpine 的 x-data="ComponentName()" 要求工厂函数返回一个同步对象。 * 本函数返回一个函数,该函数: * 1. 立即返回一个包含安全默认值的 stub 对象 * 2. 通过 Alpine 的 $watch 监听显示状态变化 * 3. 仅当组件变为可见(show/showUserModal 变为 true)时,才异步加载真实模块 * 4. 通过 Alpine 的响应式代理(this)写入真实数据,触发模板重新渲染 * * 这实现了"真懒加载"——用户若不打开面板,对应代码块永远不会下载。 * * @param {() => Promise<*>} importFn 动态 import 工厂函数 * @param {string} exportName 模块导出的组件工厂函数名 * @param {Record} [defaults={}] 安全默认值 * @param {string} [watchKey='show'] 用于触发懒加载的 $watch 属性名 * @returns {Function} Alpine 组件工厂函数 */ export function createLazyAlpineComponent(importFn, exportName, defaults = {}, watchKey = "show") { return function (...args) { const stub = { [watchKey]: false, ...defaults, init() { const proxy = this; let loaded = false; if ( watchKey in proxy && typeof proxy.$watch === "function" ) { proxy.$watch(watchKey, (value) => { if (value && !loaded) { loaded = true; importFn() .then((mod) => { const componentFn = mod[exportName]; const realData = typeof componentFn === "function" ? componentFn(...args) : componentFn; Object.keys(realData).forEach((key) => { if (key !== "init") { proxy[key] = realData[key]; } }); for (const key in realData) { if (!(key in proxy)) { proxy[key] = realData[key]; } } if ( typeof proxy.init === "function" && proxy.init !== stub.init ) { proxy.init.call(proxy); } }) .catch((err) => { console.error( `[懒加载] Alpine 组件 "${exportName}" 加载失败:`, err, ); loaded = false; }); } }); } }, }; return stub; }; }