fix(dom): correctly bind context for auto-created templates in / and optimize traversal

This commit is contained in:
AI Engineer 2026-05-25 08:13:19 +08:00
parent b8652c357e
commit 9d14dbe233
5 changed files with 47 additions and 10 deletions

View File

@ -1,5 +1,10 @@
# CHANGELOG
## v1.0.12 (2026-05-25)
### 修复
- **上下文绑定**: 修复了在对非 `template` 节点使用 `$if``$each` 指令时,框架自动创建 `<template>` 会过早转移未初始化的父级上下文,并在脱离 DOM 树后跳过深度扫描,导致 `this` 指向丢失为自身 DOM 节点的严重 Bug。同时优化了向上寻找上下文的遍历路径避免了双重循环的性能开销。
## v1.0.11 (2026-05-21)
### 核心优化

21
dist/state.js vendored
View File

@ -470,6 +470,18 @@ const _scanTree = (node, scanObj = {}) => {
});
node._stTranslated = true;
}
let resolvedThisObj = node._thisObj;
let resolvedRef = node._ref;
if (resolvedThisObj === void 0 || resolvedRef === void 0) {
let curr = node;
while (curr && (resolvedThisObj === void 0 || resolvedRef === void 0)) {
if (resolvedThisObj === void 0 && curr._thisObj !== void 0) resolvedThisObj = curr._thisObj;
if (resolvedRef === void 0 && curr._ref !== void 0) resolvedRef = { ...curr._ref };
curr = curr.parentNode;
}
}
if (resolvedThisObj === void 0) resolvedThisObj = scanObj.thisObj;
if (resolvedRef === void 0) resolvedRef = scanObj.extendVars;
if (node.tagName !== "TEMPLATE" && (node.hasAttribute("$if") || node.hasAttribute("$each") || node.hasAttribute("st-if") || node.hasAttribute("st-each"))) {
const template = document.createElement("TEMPLATE");
const attrs = Array.from(node.attributes).filter((attr) => ["$if", "$each", "st-if", "st-each"].includes(attr.name) || (node.hasAttribute("$each") || node.hasAttribute("st-each")) && ["as", "index"].includes(attr.name));
@ -479,7 +491,9 @@ const _scanTree = (node, scanObj = {}) => {
});
node.parentNode.insertBefore(template, node);
template.content.appendChild(node);
template._ref = node._ref;
template._ref = resolvedRef;
template._thisObj = resolvedThisObj;
_scanTree(template, scanObj);
return;
}
if (node.tagName === "TEMPLATE" && (node.hasAttribute("$if") || node.hasAttribute("st-if")) && (node.hasAttribute("$each") || node.hasAttribute("st-each"))) {
@ -496,7 +510,8 @@ const _scanTree = (node, scanObj = {}) => {
}
Array.from(node.content.childNodes).forEach((child) => template.content.appendChild(child));
node.content.appendChild(template);
template._ref = node._ref;
template._ref = resolvedRef;
template._thisObj = resolvedThisObj;
}
if (node.tagName === "IMG" && (node.hasAttribute("src") || node.hasAttribute("_src") || node.hasAttribute("$src"))) {
const imgNode = node;
@ -526,7 +541,7 @@ const _scanTree = (node, scanObj = {}) => {
Object.assign(node._ref, node._refExt);
}
if (scanObj.extendVars) Object.assign(node._ref, scanObj.extendVars);
_parseNode(node, scanObj);
_parseNode(node, { ...scanObj });
const nodes = [...node.childNodes || []];
const nextScanObj = { thisObj: scanObj.thisObj, extendVars: { ...node._ref } };
nodes.forEach((child) => {

2
dist/state.min.js vendored

File diff suppressed because one or more lines are too long

View File

@ -1,7 +1,6 @@
{
"name": "@web/state",
"version": "1.0.10",
"version": "1.0.12",
"type": "module",
"main": "dist/state.js",
"module": "dist/state.js",
@ -19,4 +18,4 @@
"terser": "^5.47.1",
"vite": "^5.0.0"
}
}
}

View File

@ -309,13 +309,30 @@ export const _scanTree = (node, scanObj = {}) => {
node._stTranslated = true;
}
let resolvedThisObj = node._thisObj;
let resolvedRef = node._ref;
if (resolvedThisObj === undefined || resolvedRef === undefined) {
let curr = node;
while (curr && (resolvedThisObj === undefined || resolvedRef === undefined)) {
if (resolvedThisObj === undefined && curr._thisObj !== undefined) resolvedThisObj = curr._thisObj;
if (resolvedRef === undefined && curr._ref !== undefined) resolvedRef = { ...curr._ref };
curr = curr.parentNode;
}
}
if (resolvedThisObj === undefined) resolvedThisObj = scanObj.thisObj;
if (resolvedRef === undefined) resolvedRef = scanObj.extendVars;
if (node.tagName !== 'TEMPLATE' && (node.hasAttribute('$if') || node.hasAttribute('$each') || node.hasAttribute('st-if') || node.hasAttribute('st-each'))) {
const template = document.createElement('TEMPLATE');
const attrs = Array.from(node.attributes).filter(attr => ['$if', '$each', 'st-if', 'st-each'].includes(attr.name) || ((node.hasAttribute('$each') || node.hasAttribute('st-each')) && ['as', 'index'].includes(attr.name)));
attrs.forEach(attr => { template.setAttribute(attr.name, attr.value); node.removeAttribute(attr.name); });
node.parentNode.insertBefore(template, node);
template.content.appendChild(node);
template._ref = node._ref;
template._ref = resolvedRef;
template._thisObj = resolvedThisObj; // Crucial fix: Propagate context
_scanTree(template, scanObj);
return;
}
if (node.tagName === 'TEMPLATE' && (node.hasAttribute('$if') || node.hasAttribute('st-if')) && (node.hasAttribute('$each') || node.hasAttribute('st-each'))) {
@ -329,7 +346,8 @@ export const _scanTree = (node, scanObj = {}) => {
}
Array.from(node.content.childNodes).forEach(child => template.content.appendChild(child));
node.content.appendChild(template);
template._ref = node._ref;
template._ref = resolvedRef;
template._thisObj = resolvedThisObj; // Crucial fix: Propagate context
}
if (node.tagName === 'IMG' && (node.hasAttribute('src') || node.hasAttribute('_src') || node.hasAttribute('$src'))) {
@ -362,7 +380,7 @@ export const _scanTree = (node, scanObj = {}) => {
}
if (scanObj.extendVars) Object.assign(node._ref, scanObj.extendVars);
_parseNode(node, scanObj);
_parseNode(node, { ...scanObj });
const nodes = [...(node.childNodes || [])];
const nextScanObj = { thisObj: scanObj.thisObj, extendVars: { ...node._ref } };