Standardized dangerous API naming workspace-wide. By: AICoder
This commit is contained in:
parent
d77cdd92fd
commit
2f4e5967d9
15
CHANGELOG.md
15
CHANGELOG.md
@ -1,5 +1,20 @@
|
|||||||
# CHANGELOG
|
# CHANGELOG
|
||||||
|
|
||||||
|
## v1.0.17 (2026-06-05)
|
||||||
|
|
||||||
|
### 重大变更 (Philosophical Restoration)
|
||||||
|
- **核心逻辑回位**: 物理还原 v2.3 原始架构,彻底消灭所有 ESM 时期的“Magic”补丁逻辑(如 `_stManaged` 等标志位)。
|
||||||
|
- **异步观察引擎**: 恢复 `DOMContentLoaded` 驱动的 `MutationObserver` 启动时序,确保框架作为页面的“地基”稳定运行。
|
||||||
|
- **危险 API 重命名**: 将 `RefreshState` 重命名为 `__RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios`。此 API 仅供极端性能调优使用,严禁在常规业务中调用,旨在物理隔绝 AI 幻觉和不合理使用。
|
||||||
|
|
||||||
|
### 修复
|
||||||
|
- **升级队列恢复**: 重新启用 `_pendingTemplates` 机制,解决了在同步加载(Head 加载)模式下,组件注册先于 Body 存在时导致的渲染失效问题。
|
||||||
|
- **Hash setter 同步**: 修复了 `Hash` 对象在赋值后内部状态更新延迟的问题,确保响应式即时可见。
|
||||||
|
|
||||||
|
### 增强 (批准合入)
|
||||||
|
- **样式智能合并**: 优化 `_mergeNode` 逻辑,支持组件内外 `style` 属性合并。
|
||||||
|
- **Keyed Each**: 仅在克隆环节增加 `key` 支持,不破坏原有递归扫描哲学。
|
||||||
|
|
||||||
## v1.0.16 (2026-06-05)
|
## v1.0.16 (2026-06-05)
|
||||||
|
|
||||||
### 修复
|
### 修复
|
||||||
|
|||||||
@ -75,6 +75,6 @@ AI 必须根据不同的元素类型执行以下逻辑:
|
|||||||
|
|
||||||
## 6. 运行约束 (Constraints)
|
## 6. 运行约束 (Constraints)
|
||||||
|
|
||||||
1. **禁止滥用同步刷新**:`RefreshState()` 仅用于要求“同步等待 DOM 更新”的场景。知晓其会导致一次额外的渲染任务。
|
1. **禁止滥用同步刷新**:**严禁** 在常规开发中调用 `__RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios()`。该函数仅为极高性能干预(如万级数据表格)预留。
|
||||||
2. **数据流向**:所有状态变更必须通过对 `NewState` 代理对象的赋值完成。
|
2. **数据流向**:所有状态变更必须通过对 `NewState` 代理对象的赋值完成。
|
||||||
3. **Key 的必要性**:在大规模数据(>100条)或复杂交互列表中,必须提供唯一 `key` 以激活节点复用逻辑。
|
3. **Key 的必要性**:在大规模数据(>100条)或复杂交互列表中,必须提供唯一 `key` 以激活节点复用逻辑。
|
||||||
|
|||||||
9
dist/state.js
vendored
9
dist/state.js
vendored
@ -138,7 +138,7 @@
|
|||||||
if (template) {
|
if (template) {
|
||||||
const tplnode = template.content.cloneNode(true);
|
const tplnode = template.content.cloneNode(true);
|
||||||
if (tplnode.childNodes.length) {
|
if (tplnode.childNodes.length) {
|
||||||
const rootNode = Array.from(tplnode.childNodes).find((n) => n.nodeType === Node.ELEMENT_NODE);
|
const rootNode = tplnode.children[0];
|
||||||
if (rootNode) _mergeNode(rootNode, node, scanObj, exists);
|
if (rootNode) _mergeNode(rootNode, node, scanObj, exists);
|
||||||
$$(node, "[slot-id]").forEach((placeholder) => {
|
$$(node, "[slot-id]").forEach((placeholder) => {
|
||||||
const slotName = placeholder.getAttribute("slot-id");
|
const slotName = placeholder.getAttribute("slot-id");
|
||||||
@ -508,7 +508,7 @@
|
|||||||
});
|
});
|
||||||
node.childNodes && node.childNodes.forEach((child) => _unbindTree(child));
|
node.childNodes && node.childNodes.forEach((child) => _unbindTree(child));
|
||||||
};
|
};
|
||||||
const RefreshState = _scanTree;
|
const ____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios = _scanTree;
|
||||||
const Util = {
|
const Util = {
|
||||||
clone: window.structuredClone || ((obj) => JSON.parse(JSON.stringify(obj))),
|
clone: window.structuredClone || ((obj) => JSON.parse(JSON.stringify(obj))),
|
||||||
base64: (str) => btoa(String.fromCharCode(...new TextEncoder().encode(str))),
|
base64: (str) => btoa(String.fromCharCode(...new TextEncoder().encode(str))),
|
||||||
@ -603,7 +603,7 @@
|
|||||||
Component,
|
Component,
|
||||||
$,
|
$,
|
||||||
$$,
|
$$,
|
||||||
RefreshState,
|
____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios,
|
||||||
SetTranslator,
|
SetTranslator,
|
||||||
_scanTree,
|
_scanTree,
|
||||||
_unbindTree,
|
_unbindTree,
|
||||||
@ -621,6 +621,7 @@
|
|||||||
}
|
}
|
||||||
if (typeof document !== "undefined") {
|
if (typeof document !== "undefined") {
|
||||||
const init = () => {
|
const init = () => {
|
||||||
|
Component._initPending();
|
||||||
const htmlNode = document.documentElement;
|
const htmlNode = document.documentElement;
|
||||||
if (!htmlNode.hasAttribute("$data-bs-theme") && !htmlNode.hasAttribute("data-bs-theme")) {
|
if (!htmlNode.hasAttribute("$data-bs-theme") && !htmlNode.hasAttribute("data-bs-theme")) {
|
||||||
htmlNode.setAttribute("$data-bs-theme", "LocalStorage.darkMode?'dark':'light'");
|
htmlNode.setAttribute("$data-bs-theme", "LocalStorage.darkMode?'dark':'light'");
|
||||||
@ -644,10 +645,10 @@
|
|||||||
exports2.Hash = Hash;
|
exports2.Hash = Hash;
|
||||||
exports2.LocalStorage = LocalStorage;
|
exports2.LocalStorage = LocalStorage;
|
||||||
exports2.NewState = NewState;
|
exports2.NewState = NewState;
|
||||||
exports2.RefreshState = RefreshState;
|
|
||||||
exports2.SetTranslator = SetTranslator;
|
exports2.SetTranslator = SetTranslator;
|
||||||
exports2.State = State;
|
exports2.State = State;
|
||||||
exports2.Util = Util;
|
exports2.Util = Util;
|
||||||
|
exports2.____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios = ____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios;
|
||||||
exports2._scanTree = _scanTree;
|
exports2._scanTree = _scanTree;
|
||||||
exports2._unbindTree = _unbindTree;
|
exports2._unbindTree = _unbindTree;
|
||||||
Object.defineProperty(exports2, Symbol.toStringTag, { value: "Module" });
|
Object.defineProperty(exports2, Symbol.toStringTag, { value: "Module" });
|
||||||
|
|||||||
2
dist/state.min.js
vendored
2
dist/state.min.js
vendored
File diff suppressed because one or more lines are too long
2
dist/state.min.mjs
vendored
2
dist/state.min.mjs
vendored
File diff suppressed because one or more lines are too long
9
dist/state.mjs
vendored
9
dist/state.mjs
vendored
@ -134,7 +134,7 @@ function _makeComponent(name, node, scanObj, exists = {}) {
|
|||||||
if (template) {
|
if (template) {
|
||||||
const tplnode = template.content.cloneNode(true);
|
const tplnode = template.content.cloneNode(true);
|
||||||
if (tplnode.childNodes.length) {
|
if (tplnode.childNodes.length) {
|
||||||
const rootNode = Array.from(tplnode.childNodes).find((n) => n.nodeType === Node.ELEMENT_NODE);
|
const rootNode = tplnode.children[0];
|
||||||
if (rootNode) _mergeNode(rootNode, node, scanObj, exists);
|
if (rootNode) _mergeNode(rootNode, node, scanObj, exists);
|
||||||
$$(node, "[slot-id]").forEach((placeholder) => {
|
$$(node, "[slot-id]").forEach((placeholder) => {
|
||||||
const slotName = placeholder.getAttribute("slot-id");
|
const slotName = placeholder.getAttribute("slot-id");
|
||||||
@ -504,7 +504,7 @@ const _unbindTree = (node) => {
|
|||||||
});
|
});
|
||||||
node.childNodes && node.childNodes.forEach((child) => _unbindTree(child));
|
node.childNodes && node.childNodes.forEach((child) => _unbindTree(child));
|
||||||
};
|
};
|
||||||
const RefreshState = _scanTree;
|
const ____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios = _scanTree;
|
||||||
const Util = {
|
const Util = {
|
||||||
clone: window.structuredClone || ((obj) => JSON.parse(JSON.stringify(obj))),
|
clone: window.structuredClone || ((obj) => JSON.parse(JSON.stringify(obj))),
|
||||||
base64: (str) => btoa(String.fromCharCode(...new TextEncoder().encode(str))),
|
base64: (str) => btoa(String.fromCharCode(...new TextEncoder().encode(str))),
|
||||||
@ -599,7 +599,7 @@ const ApigoState = {
|
|||||||
Component,
|
Component,
|
||||||
$,
|
$,
|
||||||
$$,
|
$$,
|
||||||
RefreshState,
|
____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios,
|
||||||
SetTranslator,
|
SetTranslator,
|
||||||
_scanTree,
|
_scanTree,
|
||||||
_unbindTree,
|
_unbindTree,
|
||||||
@ -617,6 +617,7 @@ if (typeof window !== "undefined") {
|
|||||||
}
|
}
|
||||||
if (typeof document !== "undefined") {
|
if (typeof document !== "undefined") {
|
||||||
const init = () => {
|
const init = () => {
|
||||||
|
Component._initPending();
|
||||||
const htmlNode = document.documentElement;
|
const htmlNode = document.documentElement;
|
||||||
if (!htmlNode.hasAttribute("$data-bs-theme") && !htmlNode.hasAttribute("data-bs-theme")) {
|
if (!htmlNode.hasAttribute("$data-bs-theme") && !htmlNode.hasAttribute("data-bs-theme")) {
|
||||||
htmlNode.setAttribute("$data-bs-theme", "LocalStorage.darkMode?'dark':'light'");
|
htmlNode.setAttribute("$data-bs-theme", "LocalStorage.darkMode?'dark':'light'");
|
||||||
@ -641,10 +642,10 @@ export {
|
|||||||
Hash,
|
Hash,
|
||||||
LocalStorage,
|
LocalStorage,
|
||||||
NewState,
|
NewState,
|
||||||
RefreshState,
|
|
||||||
SetTranslator,
|
SetTranslator,
|
||||||
State,
|
State,
|
||||||
Util,
|
Util,
|
||||||
|
____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios,
|
||||||
_scanTree,
|
_scanTree,
|
||||||
_unbindTree
|
_unbindTree
|
||||||
};
|
};
|
||||||
|
|||||||
@ -475,7 +475,7 @@
|
|||||||
}
|
}
|
||||||
node.childNodes && node.childNodes.forEach(child => _unbindTree(child))
|
node.childNodes && node.childNodes.forEach(child => _unbindTree(child))
|
||||||
}
|
}
|
||||||
globalThis.RefreshState = _scanTree
|
globalThis.__RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios = _scanTree
|
||||||
|
|
||||||
const _components = new Map()
|
const _components = new Map()
|
||||||
const _pendingTemplates = []
|
const _pendingTemplates = []
|
||||||
|
|||||||
@ -458,7 +458,7 @@
|
|||||||
}
|
}
|
||||||
node.childNodes && node.childNodes.forEach(child => _unbindTree(child))
|
node.childNodes && node.childNodes.forEach(child => _unbindTree(child))
|
||||||
}
|
}
|
||||||
globalThis.RefreshState = _scanTree
|
globalThis.__RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios = _scanTree
|
||||||
|
|
||||||
const _components = new Map()
|
const _components = new Map()
|
||||||
const _pendingTemplates = []
|
const _pendingTemplates = []
|
||||||
|
|||||||
@ -71,7 +71,7 @@ export function _makeComponent(name, node, scanObj, exists = {}) {
|
|||||||
if (template) {
|
if (template) {
|
||||||
const tplnode = template.content.cloneNode(true);
|
const tplnode = template.content.cloneNode(true);
|
||||||
if (tplnode.childNodes.length) {
|
if (tplnode.childNodes.length) {
|
||||||
const rootNode = Array.from(tplnode.childNodes).find(n => n.nodeType === Node.ELEMENT_NODE);
|
const rootNode = tplnode.children[0];
|
||||||
if (rootNode) _mergeNode(rootNode, node, scanObj, exists);
|
if (rootNode) _mergeNode(rootNode, node, scanObj, exists);
|
||||||
$$(node, '[slot-id]').forEach(placeholder => {
|
$$(node, '[slot-id]').forEach(placeholder => {
|
||||||
const slotName = placeholder.getAttribute('slot-id');
|
const slotName = placeholder.getAttribute('slot-id');
|
||||||
|
|||||||
@ -363,4 +363,4 @@ export const _unbindTree = (node) => {
|
|||||||
node.childNodes && node.childNodes.forEach(child => _unbindTree(child));
|
node.childNodes && node.childNodes.forEach(child => _unbindTree(child));
|
||||||
};
|
};
|
||||||
|
|
||||||
export const RefreshState = _scanTree;
|
export const ____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios = _scanTree;
|
||||||
|
|||||||
@ -337,4 +337,4 @@ export const _unbindTree = (node) => {
|
|||||||
node.childNodes && node.childNodes.forEach(child => _unbindTree(child));
|
node.childNodes && node.childNodes.forEach(child => _unbindTree(child));
|
||||||
};
|
};
|
||||||
|
|
||||||
export const RefreshState = _scanTree;
|
export const ____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios = _scanTree;
|
||||||
|
|||||||
@ -349,4 +349,4 @@ export const _unbindTree = (node) => {
|
|||||||
node.childNodes && node.childNodes.forEach(child => _unbindTree(child));
|
node.childNodes && node.childNodes.forEach(child => _unbindTree(child));
|
||||||
};
|
};
|
||||||
|
|
||||||
export const RefreshState = _scanTree;
|
export const __RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios = _scanTree;
|
||||||
|
|||||||
@ -1,19 +1,19 @@
|
|||||||
// src/index.js
|
// src/index.js
|
||||||
export { NewState } from './observer.js';
|
export { NewState } from './observer.js';
|
||||||
export { Component } from './component.js';
|
export { Component } from './component.js';
|
||||||
export { $, $$, RefreshState, SetTranslator, _scanTree, _unbindTree } from './dom.js';
|
export { $, $$, ____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios, SetTranslator, _scanTree, _unbindTree } from './dom.js';
|
||||||
export { Util } from './utils.js';
|
export { Util } from './utils.js';
|
||||||
export { Hash, LocalStorage, State } from './globals.js';
|
export { Hash, LocalStorage, State } from './globals.js';
|
||||||
|
|
||||||
import { Component } from './component.js';
|
import { Component } from './component.js';
|
||||||
import { _scanTree, _unbindTree, $, $$, RefreshState, SetTranslator } from './dom.js';
|
import { _scanTree, _unbindTree, $, $$, ____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios, SetTranslator } from './dom.js';
|
||||||
import { LocalStorage, Hash, State } from './globals.js';
|
import { LocalStorage, Hash, State } from './globals.js';
|
||||||
import { NewState, onNotifyUpdate, setActiveBinding } from './observer.js';
|
import { NewState, onNotifyUpdate, setActiveBinding } from './observer.js';
|
||||||
import { Util } from './utils.js';
|
import { Util } from './utils.js';
|
||||||
import { _runCode, _returnCode } from './core.js';
|
import { _runCode, _returnCode } from './core.js';
|
||||||
|
|
||||||
const ApigoState = {
|
const ApigoState = {
|
||||||
NewState, Component, $, $$, RefreshState, SetTranslator, _scanTree, _unbindTree, Util, Hash, LocalStorage, State,
|
NewState, Component, $, $$, ____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios, SetTranslator, _scanTree, _unbindTree, Util, Hash, LocalStorage, State,
|
||||||
_runCode, _returnCode, onNotifyUpdate, setActiveBinding
|
_runCode, _returnCode, onNotifyUpdate, setActiveBinding
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -23,6 +23,9 @@ if (typeof window !== 'undefined') {
|
|||||||
|
|
||||||
if (typeof document !== 'undefined') {
|
if (typeof document !== 'undefined') {
|
||||||
const init = () => {
|
const init = () => {
|
||||||
|
// 关键修复:在启动观察器和首次扫描前,务必将所有等待中的组件模板注入 DOM!
|
||||||
|
Component._initPending();
|
||||||
|
|
||||||
const htmlNode = document.documentElement;
|
const htmlNode = document.documentElement;
|
||||||
if (!htmlNode.hasAttribute('$data-bs-theme') && !htmlNode.hasAttribute('data-bs-theme')) {
|
if (!htmlNode.hasAttribute('$data-bs-theme') && !htmlNode.hasAttribute('data-bs-theme')) {
|
||||||
htmlNode.setAttribute('$data-bs-theme', "LocalStorage.darkMode?'dark':'light'");
|
htmlNode.setAttribute('$data-bs-theme', "LocalStorage.darkMode?'dark':'light'");
|
||||||
|
|||||||
@ -56,8 +56,8 @@ Received: "failed"
|
|||||||
32 | const items = [];
|
32 | const items = [];
|
||||||
33 | for(let i=0; i<1000; i++) items.push({val: 'item ' + i});
|
33 | for(let i=0; i<1000; i++) items.push({val: 'item ' + i});
|
||||||
34 | window.state.benchItems = items;
|
34 | window.state.benchItems = items;
|
||||||
35 | const { RefreshState } = await import('@apigo.cc/state');
|
35 | const { __RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios } = await import('@apigo.cc/state');
|
||||||
36 | RefreshState(document.documentElement);
|
36 | __RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios(document.documentElement);
|
||||||
37 | return performance.now() - start;
|
37 | return performance.now() - start;
|
||||||
38 | });
|
38 | });
|
||||||
39 | console.log(`BENCHMARK: 1000 items initial render: ${renderTime.toFixed(2)}ms`);
|
39 | console.log(`BENCHMARK: 1000 items initial render: ${renderTime.toFixed(2)}ms`);
|
||||||
@ -75,10 +75,10 @@ Received: "failed"
|
|||||||
51 |
|
51 |
|
||||||
52 | // Extreme Data Test
|
52 | // Extreme Data Test
|
||||||
53 | await page.evaluate(async () => {
|
53 | await page.evaluate(async () => {
|
||||||
54 | const { RefreshState } = await import('@apigo.cc/state');
|
54 | const { __RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios } = await import('@apigo.cc/state');
|
||||||
55 | document.body.innerHTML = '<div id="extreme" $each="state.extreme"></div>';
|
55 | document.body.innerHTML = '<div id="extreme" $each="state.extreme"></div>';
|
||||||
56 | window.state.extreme = null;
|
56 | window.state.extreme = null;
|
||||||
57 | RefreshState(document.getElementById('extreme'));
|
57 | __RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios(document.getElementById('extreme'));
|
||||||
58 | window.state.extreme = undefined;
|
58 | window.state.extreme = undefined;
|
||||||
59 | window.state.extreme = { a: 1 };
|
59 | window.state.extreme = { a: 1 };
|
||||||
60 | window.state.extreme = [1, 2];
|
60 | window.state.extreme = [1, 2];
|
||||||
|
|||||||
@ -32,8 +32,8 @@ test('modular unit tests and benchmark', async ({ page }) => {
|
|||||||
const items = [];
|
const items = [];
|
||||||
for(let i=0; i<1000; i++) items.push({val: 'item ' + i});
|
for(let i=0; i<1000; i++) items.push({val: 'item ' + i});
|
||||||
window.state.benchItems = items;
|
window.state.benchItems = items;
|
||||||
const { RefreshState } = await import('@apigo.cc/state');
|
const { ____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios } = await import('@apigo.cc/state');
|
||||||
RefreshState(document.documentElement);
|
____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios(document.documentElement);
|
||||||
return performance.now() - start;
|
return performance.now() - start;
|
||||||
});
|
});
|
||||||
console.log(`BENCHMARK: 1000 items initial render: ${renderTime.toFixed(2)}ms`);
|
console.log(`BENCHMARK: 1000 items initial render: ${renderTime.toFixed(2)}ms`);
|
||||||
@ -51,10 +51,10 @@ test('modular unit tests and benchmark', async ({ page }) => {
|
|||||||
|
|
||||||
// Extreme Data Test
|
// Extreme Data Test
|
||||||
await page.evaluate(async () => {
|
await page.evaluate(async () => {
|
||||||
const { RefreshState } = await import('@apigo.cc/state');
|
const { ____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios } = await import('@apigo.cc/state');
|
||||||
document.body.innerHTML = '<div id="extreme" $each="state.extreme"></div>';
|
document.body.innerHTML = '<div id="extreme" $each="state.extreme"></div>';
|
||||||
window.state.extreme = null;
|
window.state.extreme = null;
|
||||||
RefreshState(document.getElementById('extreme'));
|
____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios(document.getElementById('extreme'));
|
||||||
window.state.extreme = undefined;
|
window.state.extreme = undefined;
|
||||||
window.state.extreme = { a: 1 };
|
window.state.extreme = { a: 1 };
|
||||||
window.state.extreme = [1, 2];
|
window.state.extreme = [1, 2];
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
// test/component.test.js
|
// test/component.test.js
|
||||||
window.testComponent = async function() {
|
window.testComponent = async function() {
|
||||||
const { Component, RefreshState, $ } = ApigoState;
|
const { Component, ____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios, $ } = ApigoState;
|
||||||
console.log('Testing component.js...');
|
console.log('Testing component.js...');
|
||||||
|
|
||||||
// 1. Register component
|
// 1. Register component
|
||||||
@ -10,7 +10,7 @@ window.testComponent = async function() {
|
|||||||
|
|
||||||
// 2. Render component
|
// 2. Render component
|
||||||
document.body.innerHTML = '<TestComp id="comp-inst"></TestComp>';
|
document.body.innerHTML = '<TestComp id="comp-inst"></TestComp>';
|
||||||
RefreshState(document.documentElement);
|
____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios(document.documentElement);
|
||||||
|
|
||||||
const instance = $('#comp-inst');
|
const instance = $('#comp-inst');
|
||||||
if (!instance) throw new Error('Component instance not found');
|
if (!instance) throw new Error('Component instance not found');
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
// test/dom.test.js
|
// test/dom.test.js
|
||||||
window.testDom = async function() {
|
window.testDom = async function() {
|
||||||
const { RefreshState, $, $$, NewState } = ApigoState;
|
const { ____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios, $, $$, NewState } = ApigoState;
|
||||||
console.log('Testing dom.js...');
|
console.log('Testing dom.js...');
|
||||||
|
|
||||||
const wait = () => new Promise(r => setTimeout(r, 10));
|
const wait = () => new Promise(r => setTimeout(r, 10));
|
||||||
@ -10,7 +10,7 @@ window.testDom = async function() {
|
|||||||
const state = NewState({ msg: 'hello' });
|
const state = NewState({ msg: 'hello' });
|
||||||
window.state = state; // TRY: 确保在非 ESM 环境下 state 全局可见
|
window.state = state; // TRY: 确保在非 ESM 环境下 state 全局可见
|
||||||
document.documentElement._thisObj = { state };
|
document.documentElement._thisObj = { state };
|
||||||
RefreshState(document.documentElement);
|
____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios(document.documentElement);
|
||||||
if ($('#test-text').textContent !== 'hello') throw new Error('$text binding failed');
|
if ($('#test-text').textContent !== 'hello') throw new Error('$text binding failed');
|
||||||
|
|
||||||
state.msg = 'world';
|
state.msg = 'world';
|
||||||
@ -20,7 +20,7 @@ window.testDom = async function() {
|
|||||||
// 2. $if directive
|
// 2. $if directive
|
||||||
document.body.innerHTML = '<template $if="state.show"><div id="test-if">visible</div></template>';
|
document.body.innerHTML = '<template $if="state.show"><div id="test-if">visible</div></template>';
|
||||||
state.show = false;
|
state.show = false;
|
||||||
RefreshState(document.documentElement);
|
____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios(document.documentElement);
|
||||||
if ($('#test-if')) throw new Error('$if fail: should be hidden');
|
if ($('#test-if')) throw new Error('$if fail: should be hidden');
|
||||||
|
|
||||||
state.show = true;
|
state.show = true;
|
||||||
@ -30,7 +30,7 @@ window.testDom = async function() {
|
|||||||
// 3. $each directive
|
// 3. $each directive
|
||||||
document.body.innerHTML = '<template $each="state.items"><div class="test-item" $text="item"></div></template>';
|
document.body.innerHTML = '<template $each="state.items"><div class="test-item" $text="item"></div></template>';
|
||||||
state.items = ['A', 'B'];
|
state.items = ['A', 'B'];
|
||||||
RefreshState(document.documentElement);
|
____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios(document.documentElement);
|
||||||
if ($$('.test-item').length !== 2) throw new Error('$each fail: count mismatch');
|
if ($$('.test-item').length !== 2) throw new Error('$each fail: count mismatch');
|
||||||
if ($$('.test-item')[0].textContent !== 'A') throw new Error('$each fail: content mismatch');
|
if ($$('.test-item')[0].textContent !== 'A') throw new Error('$each fail: content mismatch');
|
||||||
|
|
||||||
@ -41,14 +41,14 @@ window.testDom = async function() {
|
|||||||
// 4. Event binding $onclick
|
// 4. Event binding $onclick
|
||||||
document.body.innerHTML = '<button id="test-click" $onclick="state.count++"></button>';
|
document.body.innerHTML = '<button id="test-click" $onclick="state.count++"></button>';
|
||||||
state.count = 0;
|
state.count = 0;
|
||||||
RefreshState(document.documentElement);
|
____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios(document.documentElement);
|
||||||
$('#test-click').click();
|
$('#test-click').click();
|
||||||
if (state.count !== 1) throw new Error('$onclick failed');
|
if (state.count !== 1) throw new Error('$onclick failed');
|
||||||
|
|
||||||
// 5. $bind (input)
|
// 5. $bind (input)
|
||||||
document.body.innerHTML = '<input id="test-bind" $bind="state.val">';
|
document.body.innerHTML = '<input id="test-bind" $bind="state.val">';
|
||||||
state.val = 'init';
|
state.val = 'init';
|
||||||
RefreshState(document.documentElement);
|
____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios(document.documentElement);
|
||||||
await wait(); // TRY: 等待 $bind 的 setTimeout 完成
|
await wait(); // TRY: 等待 $bind 的 setTimeout 完成
|
||||||
const input = $('#test-bind');
|
const input = $('#test-bind');
|
||||||
if (input.value !== 'init') throw new Error('$bind initial failed');
|
if (input.value !== 'init') throw new Error('$bind initial failed');
|
||||||
@ -74,13 +74,13 @@ window.testDom = async function() {
|
|||||||
const root = $('#double-eval-root');
|
const root = $('#double-eval-root');
|
||||||
root._thisObj = { state: doubleState };
|
root._thisObj = { state: doubleState };
|
||||||
|
|
||||||
RefreshState(root);
|
____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios(root);
|
||||||
await wait();
|
await wait();
|
||||||
if ($('#inner-node')) throw new Error('$$if failed: should be hidden initially');
|
if ($('#inner-node')) throw new Error('$$if failed: should be hidden initially');
|
||||||
|
|
||||||
console.log('Enabling inner node...');
|
console.log('Enabling inner node...');
|
||||||
doubleState.innerShow = true;
|
doubleState.innerShow = true;
|
||||||
RefreshState(root);
|
____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios(root);
|
||||||
await wait();
|
await wait();
|
||||||
const inner = $('#inner-node');
|
const inner = $('#inner-node');
|
||||||
if (!inner) throw new Error('$$if failed: should be visible after innerShow=true');
|
if (!inner) throw new Error('$$if failed: should be visible after innerShow=true');
|
||||||
@ -100,12 +100,12 @@ window.testDom = async function() {
|
|||||||
nestedRoot._thisObj = { state: doubleState };
|
nestedRoot._thisObj = { state: doubleState };
|
||||||
doubleState.outer = true;
|
doubleState.outer = true;
|
||||||
doubleState.innerShow = false;
|
doubleState.innerShow = false;
|
||||||
RefreshState(nestedRoot);
|
____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios(nestedRoot);
|
||||||
await wait();
|
await wait();
|
||||||
if ($('#nested-inner')) throw new Error('nested $$if failed: should be hidden initially');
|
if ($('#nested-inner')) throw new Error('nested $$if failed: should be hidden initially');
|
||||||
|
|
||||||
doubleState.innerShow = true;
|
doubleState.innerShow = true;
|
||||||
RefreshState(nestedRoot);
|
____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios(nestedRoot);
|
||||||
await wait();
|
await wait();
|
||||||
if (!$('#nested-inner')) throw new Error('nested $$if failed: should be visible after update');
|
if (!$('#nested-inner')) throw new Error('nested $$if failed: should be visible after update');
|
||||||
|
|
||||||
|
|||||||
4
test/foundation.test.js
vendored
4
test/foundation.test.js
vendored
@ -1,6 +1,6 @@
|
|||||||
// test/foundation.test.js
|
// test/foundation.test.js
|
||||||
window.testFoundation = async function() {
|
window.testFoundation = async function() {
|
||||||
const { RefreshState, Component, NewState, $, Util } = ApigoState;
|
const { ____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios, Component, NewState, $, Util } = ApigoState;
|
||||||
console.log('Testing framework foundation...');
|
console.log('Testing framework foundation...');
|
||||||
|
|
||||||
Component.register('NavTest', container => {
|
Component.register('NavTest', container => {
|
||||||
@ -26,7 +26,7 @@ window.testFoundation = async function() {
|
|||||||
const inst = document.createElement('NAVTEST');
|
const inst = document.createElement('NAVTEST');
|
||||||
inst.id = 'nav-inst';
|
inst.id = 'nav-inst';
|
||||||
document.body.appendChild(inst);
|
document.body.appendChild(inst);
|
||||||
RefreshState(document.documentElement);
|
____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios(document.documentElement);
|
||||||
await new Promise(r => setTimeout(r, 100));
|
await new Promise(r => setTimeout(r, 100));
|
||||||
|
|
||||||
const nav = $('#nav-inst');
|
const nav = $('#nav-inst');
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
// test/inheritance.test.js
|
// test/inheritance.test.js
|
||||||
window.testInheritance = async function() {
|
window.testInheritance = async function() {
|
||||||
const { RefreshState, Component, NewState, $, Util } = ApigoState;
|
const { ____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios, Component, NewState, $, Util } = ApigoState;
|
||||||
console.log('Testing inheritance...');
|
console.log('Testing inheritance...');
|
||||||
|
|
||||||
Component.register('MyComp', container => {
|
Component.register('MyComp', container => {
|
||||||
@ -26,7 +26,7 @@ window.testInheritance = async function() {
|
|||||||
inst.id = 'comp-inst';
|
inst.id = 'comp-inst';
|
||||||
document.body.appendChild(inst);
|
document.body.appendChild(inst);
|
||||||
|
|
||||||
RefreshState(document.documentElement);
|
____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios(document.documentElement);
|
||||||
await new Promise(r => setTimeout(r, 100));
|
await new Promise(r => setTimeout(r, 100));
|
||||||
|
|
||||||
const comp = $('#comp-inst');
|
const comp = $('#comp-inst');
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
// test/merging.test.js
|
// test/merging.test.js
|
||||||
window.testMerging = async function() {
|
window.testMerging = async function() {
|
||||||
const { Component, RefreshState, $ } = ApigoState;
|
const { Component, ____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios, $ } = ApigoState;
|
||||||
console.log('Testing complex class/style merging (DataTable Resizer scenario)...');
|
console.log('Testing complex class/style merging (DataTable Resizer scenario)...');
|
||||||
|
|
||||||
Component.register('Resizer-Real', container => {
|
Component.register('Resizer-Real', container => {
|
||||||
@ -8,7 +8,7 @@ window.testMerging = async function() {
|
|||||||
}, document.createRange().createContextualFragment('<div $class="tpl-static ${this.isVertical?\'tpl-v\':\'tpl-h\'}" $style="color:blue"></div>').firstChild);
|
}, document.createRange().createContextualFragment('<div $class="tpl-static ${this.isVertical?\'tpl-v\':\'tpl-h\'}" $style="color:blue"></div>').firstChild);
|
||||||
|
|
||||||
document.body.innerHTML = '<Resizer-Real id="res-inst" class="ins-static" style="opacity:0.5"></Resizer-Real>';
|
document.body.innerHTML = '<Resizer-Real id="res-inst" class="ins-static" style="opacity:0.5"></Resizer-Real>';
|
||||||
RefreshState(document.documentElement);
|
____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios(document.documentElement);
|
||||||
await new Promise(r => setTimeout(r, 50));
|
await new Promise(r => setTimeout(r, 50));
|
||||||
|
|
||||||
const inst = $('#res-inst');
|
const inst = $('#res-inst');
|
||||||
|
|||||||
@ -1,13 +1,13 @@
|
|||||||
// test/priority.test.js
|
// test/priority.test.js
|
||||||
window.testPriority = async function() {
|
window.testPriority = async function() {
|
||||||
const { RefreshState, $, NewState } = ApigoState;
|
const { ____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios, $, NewState } = ApigoState;
|
||||||
console.log('Testing directive priorities...');
|
console.log('Testing directive priorities...');
|
||||||
|
|
||||||
// Test $if vs $text (if should hide text)
|
// Test $if vs $text (if should hide text)
|
||||||
document.body.innerHTML = '<div id="prio-test" $if="state.hide" $text="state.msg"></div>';
|
document.body.innerHTML = '<div id="prio-test" $if="state.hide" $text="state.msg"></div>';
|
||||||
const state = NewState({ hide: false, msg: 'visible' });
|
const state = NewState({ hide: false, msg: 'visible' });
|
||||||
document.documentElement._thisObj = { state };
|
document.documentElement._thisObj = { state };
|
||||||
RefreshState(document.documentElement);
|
____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios(document.documentElement);
|
||||||
|
|
||||||
if ($('#prio-test').textContent !== 'visible') throw new Error('Basic visibility failed');
|
if ($('#prio-test').textContent !== 'visible') throw new Error('Basic visibility failed');
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
// test/timing.test.js
|
// test/timing.test.js
|
||||||
window.testTiming = async function() {
|
window.testTiming = async function() {
|
||||||
const { RefreshState, Component, NewState, $, Util } = ApigoState;
|
const { ____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios, Component, NewState, $, Util } = ApigoState;
|
||||||
console.log('Testing initialization timing...');
|
console.log('Testing initialization timing...');
|
||||||
|
|
||||||
Component.register('TimingComp', container => {
|
Component.register('TimingComp', container => {
|
||||||
@ -14,7 +14,7 @@ window.testTiming = async function() {
|
|||||||
`));
|
`));
|
||||||
|
|
||||||
document.body.innerHTML += `<TimingComp id="timing-inst"></TimingComp>`;
|
document.body.innerHTML += `<TimingComp id="timing-inst"></TimingComp>`;
|
||||||
RefreshState(document.documentElement);
|
____RefreshState_Internal_Force_Full_Scan_Only_In_Extreme_Performance_Scenarios(document.documentElement);
|
||||||
|
|
||||||
await new Promise(r => setTimeout(r, 100));
|
await new Promise(r => setTimeout(r, 100));
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user