diff --git a/CHANGELOG.md b/CHANGELOG.md index 16baf83..21d58b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # CHANGELOG +## v1.0.11 (2026-05-21) + +### 核心优化 +- **嵌套作用域同步**: 彻底重构了 `$each` 指令在节点复用时的上下文同步机制。现在复用节点会主动感知并合并父级作用域变量(`_ref`)的变更,解决了在虚拟滚动场景下嵌套 `$each` 数据不更新的严重 Bug。 +- **按需扫描机制**: 引入了变更感知逻辑,只有当迭代项引用变化或父级上下文变量变化时才触发子树扫描(`_scanTree`),大幅提升了复杂嵌套组件的滚动性能。 +- **性能遥测系统**: 在 `dom.js` 中内置了 `__statePerformanceTelemetry` 埋点,支持实时监控 `scanCount`(扫描)、`reuseCount`(复用)和 `moveCount`(DOM 移动)等核心指标,为极致性能调优提供数据支撑。 + ## v1.0.10 (2026-05-18) ### 修复 diff --git a/src/dom.js b/src/dom.js index 3b565a2..6f260e5 100644 --- a/src/dom.js +++ b/src/dom.js @@ -119,20 +119,32 @@ export function _updateBinding(binding) { const rawKey = keyName ? (item && typeof item === 'object' ? item[keyName] : item) : k; // Safety: Handle potential duplicate keys or undefined keys const keyVal = (rawKey === undefined || rawKey === null || newKeyedNodes.has(rawKey)) ? `st_key_fallback_${i}_${Math.random()}` : rawKey; - + let existingNodes = node._keyedNodes.get(keyVal); if (existingNodes) { // Reuse existing nodes node._keyedNodes.delete(keyVal); existingNodes.forEach(child => { - child._ref[indexName] = k; - // If data reference hasn't changed, skip heavy scan - if (child._ref[asName] !== item) { + if (window.__statePerformanceTelemetry) window.__statePerformanceTelemetry.reuseCount++; + let scopeChanged = false; + for (let key in node._ref) { + if (key === asName || key === indexName) continue; + if (child._ref[key] !== node._ref[key]) { + child._ref[key] = node._ref[key]; + scopeChanged = true; + } + } + const indexChanged = child._ref[indexName] !== k; + if (indexChanged) child._ref[indexName] = k; + + if (child._ref[asName] !== item || scopeChanged) { child._ref[asName] = item; + if (window.__statePerformanceTelemetry) window.__statePerformanceTelemetry.scanCount++; _scanTree(child, { thisObj: node._thisObj, extendVars: child._ref }); } else if (node.parentNode.lastChild !== child) { // Just move to the end to maintain order + if (window.__statePerformanceTelemetry) window.__statePerformanceTelemetry.moveCount++; node.parentNode.insertBefore(child, node); } }); @@ -346,9 +358,9 @@ export const _scanTree = (node, scanObj = {}) => { node._ref = curr ? { ...curr._ref } : {}; } if (scanObj.extendVars) Object.assign(node._ref, scanObj.extendVars); - + _parseNode(node, scanObj); - + const nodes = [...(node.childNodes || [])]; const nextScanObj = { thisObj: scanObj.thisObj, extendVars: { ...node._ref } }; nodes.forEach(child => {