feat(lab-content-engine): phase 3 - ленивая загрузка кода симуляций
Старт /lab грузит только каркас (~530KB) вместо ~2.9MB + three.js(~600KB):
- _loader.js — LabLoader.ensure(id): грузит файлы симуляции по манифесту +
three.js при необходимости; кеш по URL; САМОВОССТАНОВЛЕНИЕ (если open-функция
не определена после загрузки — грузит все ленивые файлы -> корректность
гарантирована независимо от точности манифеста)
- _sim_deps.js — сгенерированный манифест SIM_DEPS{id:{open,files,three}} +
LAB_LAZY_FILES; three:true только для crystal/orbitals/stereo/periodic
- _register-all.js — open-обёртка: LabLoader.ensure(id).then(rawOpen)
- lab-init.js openSim — обработка Promise от open() (lucide после init)
- lab.html — убраны 45 ленивых <script> + three.js из eager; каркас: registry,
loader, sim_deps, fx-движки, общие визуалы, graph.js (GRID для 15 сим)
Проверка: vm-harness (per-sim load, three only 3D, кеш, self-heal) ALL PASS;
инвариант owner-in-files для всех 40; нет утечки ленивых в eager; node --check OK.
В БРАУЗЕРЕ НЕ ПРОВЕРЕНО.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,76 @@
|
|||||||
|
'use strict';
|
||||||
|
/*
|
||||||
|
* LabLoader — ленивый загрузчик кода симуляций (контент-движок, Фаза 3).
|
||||||
|
*
|
||||||
|
* Тяжёлый код симуляций (~2.5 МБ) и three.js (~600 КБ) больше НЕ грузятся на старте.
|
||||||
|
* При открытии симуляции LabLoader.ensure(id) подгружает её файлы (по манифесту
|
||||||
|
* window.SIM_DEPS из _sim_deps.js) и, при необходимости, three.js — затем резолвит.
|
||||||
|
*
|
||||||
|
* Гарантия корректности (самовосстановление): если после загрузки указанных файлов
|
||||||
|
* глобальная open-функция (SIM_DEPS[id].open, напр. "_openPendulum") всё ещё не
|
||||||
|
* определена, грузятся ВСЕ ленивые файлы (window.LAB_LAZY_FILES). Поэтому ошибка в
|
||||||
|
* манифесте не может «сломать» симуляцию — в худшем случае грузится больше файлов
|
||||||
|
* (поведение как до Фазы 3). Манифест лишь оптимизирует объём загрузки.
|
||||||
|
*
|
||||||
|
* Все загрузки кешируются (по URL) и дедуплицируются.
|
||||||
|
*/
|
||||||
|
(function () {
|
||||||
|
var BASE = '/js/labs/';
|
||||||
|
var THREE_URL = 'https://cdn.jsdelivr.net/npm/three@0.149.0/build/three.min.js';
|
||||||
|
var _cache = {}; // url -> Promise
|
||||||
|
var _allLoaded = false;
|
||||||
|
|
||||||
|
function loadScript(url) {
|
||||||
|
if (_cache[url]) return _cache[url];
|
||||||
|
_cache[url] = new Promise(function (resolve, reject) {
|
||||||
|
var s = document.createElement('script');
|
||||||
|
s.src = url;
|
||||||
|
s.async = false; // сохранить порядок при добавлении нескольких сразу
|
||||||
|
s.onload = function () { resolve(url); };
|
||||||
|
s.onerror = function () { delete _cache[url]; reject(new Error('LabLoader: не удалось загрузить ' + url)); };
|
||||||
|
document.head.appendChild(s);
|
||||||
|
});
|
||||||
|
return _cache[url];
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureThree() {
|
||||||
|
if (typeof window.THREE !== 'undefined') return Promise.resolve();
|
||||||
|
return loadScript(THREE_URL);
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadFiles(files) {
|
||||||
|
return Promise.all((files || []).map(function (f) { return loadScript(BASE + f); }));
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadAllLazy() {
|
||||||
|
if (_allLoaded) return Promise.resolve();
|
||||||
|
var list = window.LAB_LAZY_FILES || [];
|
||||||
|
return loadFiles(list).then(function () { _allLoaded = true; });
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensure(id): загрузить всё необходимое для симуляции id, вернуть Promise.
|
||||||
|
function ensure(id) {
|
||||||
|
var dep = (window.SIM_DEPS && window.SIM_DEPS[id]) || null;
|
||||||
|
if (!dep) {
|
||||||
|
// нет манифеста для id — безопасно грузим всё
|
||||||
|
return loadAllLazy();
|
||||||
|
}
|
||||||
|
var p = dep.three ? ensureThree() : Promise.resolve();
|
||||||
|
return p
|
||||||
|
.then(function () { return loadFiles(dep.files); })
|
||||||
|
.then(function () {
|
||||||
|
var openName = dep.open;
|
||||||
|
if (openName && typeof window[openName] !== 'function') {
|
||||||
|
if (window.console) console.warn('[LabLoader] самовосстановление для "' + id + '": ' + openName + ' не найдена после загрузки ' + JSON.stringify(dep.files) + ' — гружу все ленивые файлы');
|
||||||
|
return loadAllLazy();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
window.LabLoader = {
|
||||||
|
ensure: ensure,
|
||||||
|
ensureThree: ensureThree,
|
||||||
|
loadScript: loadScript,
|
||||||
|
loadAllLazy: loadAllLazy
|
||||||
|
};
|
||||||
|
})();
|
||||||
@@ -0,0 +1,300 @@
|
|||||||
|
'use strict';
|
||||||
|
/* Контент-движок, Фаза 3 — манифест зависимостей симуляций (СГЕНЕРИРОВАН).
|
||||||
|
id -> { open: имя глобальной _openX, files: [ленивые файлы], three: нужен ли three.js }.
|
||||||
|
Файлы загружаются лениво по клику (см. _loader.js). three.js — только для 3D-симуляций.
|
||||||
|
Самовосстановление в _loader: если после загрузки open-функция не определена,
|
||||||
|
грузятся ВСЕ ленивые файлы -> корректность не зависит от точности манифеста.
|
||||||
|
Регенерация: node tools/gen-sim-deps.js (см. CONTEXT). НЕ редактировать вручную. */
|
||||||
|
window.SIM_DEPS = {
|
||||||
|
"graph": {
|
||||||
|
"open": "_openGraph",
|
||||||
|
"files": [],
|
||||||
|
"three": false
|
||||||
|
},
|
||||||
|
"projectile": {
|
||||||
|
"open": "_openProjectile",
|
||||||
|
"files": [
|
||||||
|
"projectile.js"
|
||||||
|
],
|
||||||
|
"three": false
|
||||||
|
},
|
||||||
|
"collision": {
|
||||||
|
"open": "_openCollision",
|
||||||
|
"files": [
|
||||||
|
"collision.js"
|
||||||
|
],
|
||||||
|
"three": false
|
||||||
|
},
|
||||||
|
"triangle": {
|
||||||
|
"open": "_openTriangle",
|
||||||
|
"files": [
|
||||||
|
"triangle.js"
|
||||||
|
],
|
||||||
|
"three": false
|
||||||
|
},
|
||||||
|
"trigcircle": {
|
||||||
|
"open": "_openTrigCircle",
|
||||||
|
"files": [
|
||||||
|
"trigcircle.js"
|
||||||
|
],
|
||||||
|
"three": false
|
||||||
|
},
|
||||||
|
"emfield": {
|
||||||
|
"open": "_openEMField",
|
||||||
|
"files": [
|
||||||
|
"emfield.js",
|
||||||
|
"logic.js"
|
||||||
|
],
|
||||||
|
"three": false
|
||||||
|
},
|
||||||
|
"molphys": {
|
||||||
|
"open": "_openMolPhys",
|
||||||
|
"files": [
|
||||||
|
"brownian.js",
|
||||||
|
"diffusion.js",
|
||||||
|
"gas.js",
|
||||||
|
"states.js"
|
||||||
|
],
|
||||||
|
"three": false
|
||||||
|
},
|
||||||
|
"circuit": {
|
||||||
|
"open": "_openCircuit",
|
||||||
|
"files": [
|
||||||
|
"circuit.js"
|
||||||
|
],
|
||||||
|
"three": false
|
||||||
|
},
|
||||||
|
"chemistry": {
|
||||||
|
"open": "_openChemistry",
|
||||||
|
"files": [
|
||||||
|
"circuit.js",
|
||||||
|
"flask.js",
|
||||||
|
"ionexchange.js",
|
||||||
|
"reactions.js",
|
||||||
|
"redox.js"
|
||||||
|
],
|
||||||
|
"three": false
|
||||||
|
},
|
||||||
|
"dynamics": {
|
||||||
|
"open": "_openDynamics",
|
||||||
|
"files": [
|
||||||
|
"forcesandbox.js",
|
||||||
|
"newton.js"
|
||||||
|
],
|
||||||
|
"three": false
|
||||||
|
},
|
||||||
|
"crystal": {
|
||||||
|
"open": "_openCrystal",
|
||||||
|
"files": [
|
||||||
|
"crystal.js"
|
||||||
|
],
|
||||||
|
"three": true
|
||||||
|
},
|
||||||
|
"orbitals": {
|
||||||
|
"open": "_openOrbitals",
|
||||||
|
"files": [
|
||||||
|
"orbitals.js"
|
||||||
|
],
|
||||||
|
"three": true
|
||||||
|
},
|
||||||
|
"stereo": {
|
||||||
|
"open": "_openStereo",
|
||||||
|
"files": [
|
||||||
|
"stereo.js"
|
||||||
|
],
|
||||||
|
"three": true
|
||||||
|
},
|
||||||
|
"chemsandbox": {
|
||||||
|
"open": "_openChemSandbox",
|
||||||
|
"files": [
|
||||||
|
"chemsandbox.js",
|
||||||
|
"collision.js"
|
||||||
|
],
|
||||||
|
"three": false
|
||||||
|
},
|
||||||
|
"celldivision": {
|
||||||
|
"open": "_openCellDivision",
|
||||||
|
"files": [
|
||||||
|
"celldivision.js"
|
||||||
|
],
|
||||||
|
"three": false
|
||||||
|
},
|
||||||
|
"photosynthesis": {
|
||||||
|
"open": "_openPhotosynthesis",
|
||||||
|
"files": [
|
||||||
|
"photosynthesis.js"
|
||||||
|
],
|
||||||
|
"three": false
|
||||||
|
},
|
||||||
|
"angrybirds": {
|
||||||
|
"open": "_openAngryBirds",
|
||||||
|
"files": [
|
||||||
|
"angrybirds.js"
|
||||||
|
],
|
||||||
|
"three": false
|
||||||
|
},
|
||||||
|
"quadratic": {
|
||||||
|
"open": "_openQuadratic",
|
||||||
|
"files": [
|
||||||
|
"quadratic.js"
|
||||||
|
],
|
||||||
|
"three": false
|
||||||
|
},
|
||||||
|
"normaldist": {
|
||||||
|
"open": "_openNormalDist",
|
||||||
|
"files": [
|
||||||
|
"normaldist.js"
|
||||||
|
],
|
||||||
|
"three": false
|
||||||
|
},
|
||||||
|
"graphtransform": {
|
||||||
|
"open": "_openGraphTransform",
|
||||||
|
"files": [
|
||||||
|
"graphtransform.js"
|
||||||
|
],
|
||||||
|
"three": false
|
||||||
|
},
|
||||||
|
"pendulum": {
|
||||||
|
"open": "_openPendulum",
|
||||||
|
"files": [
|
||||||
|
"pendulum.js"
|
||||||
|
],
|
||||||
|
"three": false
|
||||||
|
},
|
||||||
|
"equilibrium": {
|
||||||
|
"open": "_openEquilibrium",
|
||||||
|
"files": [
|
||||||
|
"equilibrium.js"
|
||||||
|
],
|
||||||
|
"three": false
|
||||||
|
},
|
||||||
|
"opticsbench": {
|
||||||
|
"open": "_openOpticsBench",
|
||||||
|
"files": [
|
||||||
|
"opticsbench.js"
|
||||||
|
],
|
||||||
|
"three": false
|
||||||
|
},
|
||||||
|
"isoprocess": {
|
||||||
|
"open": "_openIsoprocess",
|
||||||
|
"files": [
|
||||||
|
"isoprocess.js"
|
||||||
|
],
|
||||||
|
"three": false
|
||||||
|
},
|
||||||
|
"titration": {
|
||||||
|
"open": "_openTitration",
|
||||||
|
"files": [
|
||||||
|
"titration.js"
|
||||||
|
],
|
||||||
|
"three": false
|
||||||
|
},
|
||||||
|
"probability": {
|
||||||
|
"open": "_openProbability",
|
||||||
|
"files": [
|
||||||
|
"probability.js"
|
||||||
|
],
|
||||||
|
"three": false
|
||||||
|
},
|
||||||
|
"bohratom": {
|
||||||
|
"open": "_openBohrAtom",
|
||||||
|
"files": [
|
||||||
|
"bohratom.js"
|
||||||
|
],
|
||||||
|
"three": false
|
||||||
|
},
|
||||||
|
"electrolysis": {
|
||||||
|
"open": "_openElectrolysis",
|
||||||
|
"files": [
|
||||||
|
"electrolysis.js"
|
||||||
|
],
|
||||||
|
"three": false
|
||||||
|
},
|
||||||
|
"race": {
|
||||||
|
"open": "_openRace",
|
||||||
|
"files": [
|
||||||
|
"race.js"
|
||||||
|
],
|
||||||
|
"three": false
|
||||||
|
},
|
||||||
|
"waves": {
|
||||||
|
"open": "_openWaves",
|
||||||
|
"files": [
|
||||||
|
"waves.js"
|
||||||
|
],
|
||||||
|
"three": false
|
||||||
|
},
|
||||||
|
"hydrostatics": {
|
||||||
|
"open": "_openHydro",
|
||||||
|
"files": [
|
||||||
|
"hydrostatics.js"
|
||||||
|
],
|
||||||
|
"three": false
|
||||||
|
},
|
||||||
|
"radioactive": {
|
||||||
|
"open": "_openRadioactive",
|
||||||
|
"files": [
|
||||||
|
"radioactive.js"
|
||||||
|
],
|
||||||
|
"three": false
|
||||||
|
},
|
||||||
|
"geometry": {
|
||||||
|
"open": "_openGeometry",
|
||||||
|
"files": [
|
||||||
|
"geometry.js",
|
||||||
|
"triangle.js"
|
||||||
|
],
|
||||||
|
"three": false
|
||||||
|
},
|
||||||
|
"logic": {
|
||||||
|
"open": "_openLogic",
|
||||||
|
"files": [
|
||||||
|
"logic.js"
|
||||||
|
],
|
||||||
|
"three": false
|
||||||
|
},
|
||||||
|
"heatengine": {
|
||||||
|
"open": "_openHeatEngine",
|
||||||
|
"files": [
|
||||||
|
"heatengine.js"
|
||||||
|
],
|
||||||
|
"three": false
|
||||||
|
},
|
||||||
|
"stoichiometry": {
|
||||||
|
"open": "_openStoich",
|
||||||
|
"files": [
|
||||||
|
"stoichiometry.js"
|
||||||
|
],
|
||||||
|
"three": false
|
||||||
|
},
|
||||||
|
"qualanalysis": {
|
||||||
|
"open": "_openQualAnalysis",
|
||||||
|
"files": [
|
||||||
|
"qualanalysis.js"
|
||||||
|
],
|
||||||
|
"three": false
|
||||||
|
},
|
||||||
|
"periodic": {
|
||||||
|
"open": "_openPeriodic",
|
||||||
|
"files": [
|
||||||
|
"_periodic_data.js",
|
||||||
|
"periodic.js"
|
||||||
|
],
|
||||||
|
"three": true
|
||||||
|
},
|
||||||
|
"organic": {
|
||||||
|
"open": "_openOrganic",
|
||||||
|
"files": [
|
||||||
|
"organic.js"
|
||||||
|
],
|
||||||
|
"three": false
|
||||||
|
},
|
||||||
|
"solutions": {
|
||||||
|
"open": "_openSolutions",
|
||||||
|
"files": [
|
||||||
|
"solutions.js"
|
||||||
|
],
|
||||||
|
"three": false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
window.LAB_LAZY_FILES = ["angrybirds.js","bohratom.js","brownian.js","celldivision.js","chemsandbox.js","circuit.js","collision.js","crystal.js","diffusion.js","electrolysis.js","emfield.js","equilibrium.js","flask.js","forcesandbox.js","gas.js","geometry.js","graphtransform.js","heatengine.js","hydrostatics.js","ionexchange.js","isoprocess.js","logic.js","newton.js","normaldist.js","opticsbench.js","orbitals.js","organic.js","pendulum.js","periodic.js","photosynthesis.js","probability.js","projectile.js","quadratic.js","qualanalysis.js","race.js","radioactive.js","reactions.js","redox.js","solutions.js","states.js","stereo.js","stoichiometry.js","titration.js","triangle.js","trigcircle.js","waves.js","_periodic_data.js"];
|
||||||
+15
-53
@@ -400,71 +400,33 @@
|
|||||||
|
|
||||||
<script src="/js/api.js"></script>
|
<script src="/js/api.js"></script>
|
||||||
<script src="/js/sidebar.js"></script>
|
<script src="/js/sidebar.js"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/three@0.149.0/build/three.min.js"></script>
|
<!-- ════════════════════════════════════════════════════════════════════════
|
||||||
|
Контент-движок, Фаза 3 — ЛЕНИВАЯ ЗАГРУЗКА КОДА СИМУЛЯЦИЙ.
|
||||||
|
На старте грузится только КАРКАС (~360 КБ): реестр, загрузчик, манифест,
|
||||||
|
fx-движки, общие визуалы (_phys_visuals/_chem_visuals/_graph_panel/_util),
|
||||||
|
graph.js (предоставляет GRID для 15 симуляций), lab-init/glue/register-all.
|
||||||
|
Код конкретной симуляции (~2.5 МБ суммарно) и three.js (~600 КБ) грузятся
|
||||||
|
по клику через LabLoader (см. _loader.js + _sim_deps.js). three.js — только
|
||||||
|
для 3D-симуляций (crystal/orbitals/stereo/periodic).
|
||||||
|
════════════════════════════════════════════════════════════════════════ -->
|
||||||
<script src="/js/labs/_registry.js"></script>
|
<script src="/js/labs/_registry.js"></script>
|
||||||
|
<script src="/js/labs/_loader.js"></script>
|
||||||
|
<script src="/js/labs/_sim_deps.js"></script>
|
||||||
<script src="/js/labs/_fx_core.js"></script>
|
<script src="/js/labs/_fx_core.js"></script>
|
||||||
<script src="/js/labs/_fx_particles.js"></script>
|
<script src="/js/labs/_fx_particles.js"></script>
|
||||||
<script src="/js/labs/_fx_motion.js"></script>
|
<script src="/js/labs/_fx_motion.js"></script>
|
||||||
<script src="/js/labs/_fx_sound.js"></script>
|
<script src="/js/labs/_fx_sound.js"></script>
|
||||||
<script src="/js/labs/graph.js"></script>
|
|
||||||
<script src="/js/labs/_phys_visuals.js"></script>
|
|
||||||
<script src="/js/labs/emfield.js"></script>
|
|
||||||
<script src="/js/labs/triangle.js"></script>
|
|
||||||
<script src="/js/labs/_graph_panel.js"></script>
|
<script src="/js/labs/_graph_panel.js"></script>
|
||||||
<script src="/js/labs/projectile.js"></script>
|
<script src="/js/labs/_phys_visuals.js"></script>
|
||||||
<script src="/js/labs/collision.js"></script>
|
|
||||||
<script src="/js/labs/gas.js"></script>
|
|
||||||
<script src="/js/labs/states.js"></script>
|
|
||||||
<script src="/js/labs/brownian.js"></script>
|
|
||||||
<script src="/js/labs/diffusion.js"></script>
|
|
||||||
<!-- coulomb.js removed: merged into emfield.js -->
|
|
||||||
<script src="/js/labs/circuit.js"></script>
|
|
||||||
<script src="/js/labs/_chem_visuals.js"></script>
|
<script src="/js/labs/_chem_visuals.js"></script>
|
||||||
<script src="/js/labs/reactions.js"></script>
|
<script src="/js/labs/_util.js"></script>
|
||||||
<script src="/js/labs/flask.js"></script>
|
<script src="/js/labs/graph.js"></script>
|
||||||
<script src="/js/labs/redox.js"></script>
|
|
||||||
<script src="/js/labs/ionexchange.js"></script>
|
|
||||||
<script src="/js/labs/stereo.js?v=10"></script>
|
|
||||||
<script src="/js/notifications.js"></script>
|
<script src="/js/notifications.js"></script>
|
||||||
<script src="/js/search.js"></script>
|
<script src="/js/search.js"></script>
|
||||||
<script src="/js/mobile.js"></script>
|
<script src="/js/mobile.js"></script>
|
||||||
<script src="/js/labs/lab-init.js"></script>
|
<script src="/js/labs/lab-init.js"></script>
|
||||||
<script src="/js/labs/lab-glue.js"></script>
|
<script src="/js/labs/lab-glue.js"></script>
|
||||||
<script src="/js/labs/newton.js"></script>
|
<script src="/js/labs/_register-all.js"></script>
|
||||||
<script src="/js/labs/forcesandbox.js"></script>
|
|
||||||
<script src="/js/labs/angrybirds.js"></script>
|
|
||||||
<script src="/js/labs/waves.js"></script>
|
|
||||||
<script src="/js/labs/chemsandbox.js"></script>
|
|
||||||
<script src="/js/labs/stoichiometry.js"></script>
|
|
||||||
<script src="/js/labs/celldivision.js"></script>
|
|
||||||
<script src="/js/labs/photosynthesis.js"></script>
|
|
||||||
<script src="/js/labs/crystal.js"></script>
|
|
||||||
<script src="/js/labs/orbitals.js"></script>
|
|
||||||
<script src="/js/labs/trigcircle.js"></script>
|
|
||||||
<script src="/js/labs/_util.js"></script>
|
|
||||||
<script src="/js/labs/quadratic.js"></script>
|
|
||||||
<script src="/js/labs/normaldist.js"></script>
|
|
||||||
<script src="/js/labs/graphtransform.js"></script>
|
|
||||||
<script src="/js/labs/pendulum.js"></script>
|
|
||||||
<script src="/js/labs/equilibrium.js"></script>
|
|
||||||
<script src="/js/labs/opticsbench.js?v=10"></script>
|
|
||||||
<script src="/js/labs/isoprocess.js"></script>
|
|
||||||
<script src="/js/labs/titration.js"></script>
|
|
||||||
<script src="/js/labs/probability.js"></script>
|
|
||||||
<script src="/js/labs/bohratom.js"></script>
|
|
||||||
<script src="/js/labs/electrolysis.js"></script>
|
|
||||||
<script src="/js/labs/race.js"></script>
|
|
||||||
<script src="/js/labs/hydrostatics.js"></script>
|
|
||||||
<script src="/js/labs/radioactive.js"></script>
|
|
||||||
<script src="/js/labs/geometry.js"></script>
|
|
||||||
<script src="/js/labs/logic.js"></script>
|
|
||||||
<script src="/js/labs/heatengine.js"></script>
|
|
||||||
<script src="/js/labs/solutions.js" defer></script>
|
|
||||||
<script src="/js/labs/organic.js" defer></script>
|
|
||||||
<script src="/js/labs/_periodic_data.js" defer></script>
|
|
||||||
<script src="/js/labs/periodic.js" defer></script>
|
|
||||||
<script src="/js/labs/qualanalysis.js" defer></script>
|
|
||||||
<script src="/js/labs/_register-all.js" defer></script>
|
|
||||||
<script>
|
<script>
|
||||||
/* Sync sound toggle button icon with localStorage state on load */
|
/* Sync sound toggle button icon with localStorage state on load */
|
||||||
(function() {
|
(function() {
|
||||||
|
|||||||
Reference in New Issue
Block a user