// P4:`data.js` 瘦身 —— 分步降低运行时对源码全文的依赖 // // 背景(实测): // data.json 991KB,其中 components[].sources 占 888KB(89.6%) // 各端源码量:html 245KB / vue2 184KB / vue3 181KB / jsx 155KB / css 119KB // // 哪些 sources 被【渲染期同步】消费(改造难点): // extractComponentAPI → vue3 / vue2 / jsx(3 端) // extractScenarios → html(1 端) // css → 不参与任何解析(可安全分离) // // 策略: // 阶段 A(本文件)—— 预计算 scenarios,验证「构建期预计算 + 运行时回退」链路可行 // 阶段 B(后续)—— 分离 css 到独立文件(零风险,不参与解析) // 阶段 C(后续)—— 预计算 API 表,再分离 4 端源码 // // 设计原则:运行时保留回退路径,预计算缺失时功能不受影响。 import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs'; const ROOT = 'C:/Users/12914/Desktop/组件规范第一套'; const DATA_JSON = `${ROOT}/site/data.json`; const DATA_JS = `${ROOT}/site/data.js`; const SRC_DIR = `${ROOT}/site/sources`; if (!existsSync(DATA_JSON)) { console.error('[FATAL] 找不到 site/data.json,请先运行 build-site.ps1'); process.exit(1); } const data = JSON.parse(readFileSync(DATA_JSON, 'utf8')); /* 与 app.js 的 extractScenarios 完全一致(保证结果可复现) */ function extractScenarios(c) { const html = (c.sources && c.sources.html) || ''; const bodyM = html.match(/]*>([\s\S]*)<\/body>/i); const body = bodyM ? bodyM[1] : html; const out = []; const re = /]*>([\s\S]*?)<\/h2>/gi; let m; while ((m = re.exec(body)) !== null) { let text = m[1].replace(/<[^>]+>/g, '').replace(/\s+/g, ' ').trim(); text = text.replace(/^\d+[.、]\s*/, ''); if (text) out.push(text); } return out; } /* ---------- 阶段 A:预计算 scenarios ---------- */ let withScenarios = 0; let totalScenarios = 0; for (const c of data.components) { const s = extractScenarios(c); c.scenarios = s; totalScenarios += s.length; if (s.length >= 2) withScenarios++; // app.js 的 buildScenarioCard 要求 >= 2 } console.log(`[precompute] 阶段A · scenarios:${totalScenarios} 条 / ${withScenarios} 个组件有 ≥2 条`); /* ---------- 阶段 B:分离 css 源码到独立文件 ---------- css 不参与任何解析(extractComponentAPI 只读 vue3/vue2/jsx,extractScenarios 只读 html), 因此可安全移出 data.js,改为按需 fetch。 data.json 保持完整(For Agents 承诺不变)。 */ if (!existsSync(SRC_DIR)) mkdirSync(SRC_DIR, { recursive: true }); let cssSplit = 0; for (const c of data.components) { if (!c.sources || !c.sources.css) continue; const dir = `${SRC_DIR}/${c.slug}`; if (!existsSync(dir)) mkdirSync(dir, { recursive: true }); writeFileSync(`${dir}/css.txt`, c.sources.css); cssSplit++; } console.log(`[precompute] 阶段B · css 已分离:${cssSplit} 个文件 → site/sources//css.txt`); /* 在 data.js 里把 css 换成引用(data.json 保持完整) */ const dataForJs = JSON.parse(JSON.stringify(data)); for (const c of dataForJs.components) { if (c.sources && c.sources.css) { c.sourcesRef = c.sourcesRef || {}; c.sourcesRef.css = `sources/${c.slug}/css.txt`; delete c.sources.css; } } /* ---------- 写回 ---------- */ writeFileSync(DATA_JSON, JSON.stringify(data, null, 2) + '\n'); const js = '/* AUTO-GENERATED by build-site.ps1 + tools/precompute.mjs - do not edit by hand */\r\nwindow.AA_DATA = ' + JSON.stringify(dataForJs) + ';\r\n'; writeFileSync(DATA_JS, js); const beforeKb = 1058; const afterKb = (Buffer.byteLength(js) / 1024).toFixed(0); console.log(`[precompute] data.js: ${beforeKb} KB → ${afterKb} KB(−${beforeKb - afterKb} KB)`);