@wsxjs/wsx-core 0.0.23 → 0.0.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -79,6 +79,9 @@ export abstract class WebComponent extends BaseComponent {
79
79
  const hasActualContent =
80
80
  allChildren.length > styleElements.length + slotElements.length;
81
81
 
82
+ // 调用子类的初始化钩子
83
+ this.onConnected?.();
84
+
82
85
  // 如果有错误元素,需要重新渲染以恢复正常
83
86
  // 如果有实际内容且没有错误,跳过渲染(避免重复元素)
84
87
  if (hasActualContent && !hasErrorElement) {
@@ -110,9 +113,6 @@ export abstract class WebComponent extends BaseComponent {
110
113
  // 初始化事件监听器
111
114
  this.initializeEventListeners();
112
115
 
113
- // 调用子类的初始化钩子
114
- this.onConnected?.();
115
-
116
116
  // 如果进行了渲染,调用 onRendered 钩子
117
117
  if (hasActualContent === false || hasErrorElement) {
118
118
  // 使用 requestAnimationFrame 确保 DOM 已完全更新
@@ -173,12 +173,21 @@ export abstract class WebComponent extends BaseComponent {
173
173
  const focusState = this.captureFocusState();
174
174
  this._pendingFocusState = focusState;
175
175
 
176
- // 2. 保存当前的 adopted stylesheets
176
+ // 2. 保存当前的 adopted stylesheets 并检测实际的样式状态
177
177
  const adoptedStyleSheets = this.shadowRoot.adoptedStyleSheets || [];
178
+ // 自动检测模式:检查实际的 ShadowRoot 样式状态,而不仅仅是保存的数组
179
+ // 这样可以更准确地检测样式是否真的已应用
180
+ const hasActualAdoptedStyles =
181
+ this.shadowRoot.adoptedStyleSheets && this.shadowRoot.adoptedStyleSheets.length > 0;
182
+ // 检查 fallback 模式的样式元素
183
+ const hasFallbackStyleElement = Array.from(this.shadowRoot.children).some(
184
+ (child) => child instanceof HTMLStyleElement
185
+ );
178
186
 
179
187
  try {
180
- // 3. 只有在没有 adopted stylesheets 时才重新应用样式
181
- if (adoptedStyleSheets.length === 0) {
188
+ // 3. 自动检测模式:只有在没有实际样式时才重新应用样式
189
+ // 检查 adoptedStyleSheets fallback 样式元素
190
+ if (!hasActualAdoptedStyles && !hasFallbackStyleElement) {
182
191
  const stylesToApply = this._autoStyles || this.config.styles;
183
192
  if (stylesToApply) {
184
193
  const styleName = this.config.styleName || this.constructor.name;
@@ -205,46 +214,68 @@ export abstract class WebComponent extends BaseComponent {
205
214
  }
206
215
  }
207
216
 
208
- // 6. 恢复 adopted stylesheets
209
- if (this.shadowRoot.adoptedStyleSheets) {
217
+ // 6. 执行 DOM 操作(同步,不使用 RAF,因为已经在 scheduleRerender 的 RAF 中)
218
+ // 关键修复 (RFC-0042):检查 content 是否已经在 shadowRoot 中(元素复用场景)
219
+ // 如果 content 已经在 shadowRoot 中,不需要再次添加
220
+ // 这样可以避免移动元素,导致文本节点更新丢失
221
+ const isContentAlreadyInShadowRoot = content.parentNode === this.shadowRoot;
222
+
223
+ if (!isContentAlreadyInShadowRoot) {
224
+ // 添加新内容(仅在不在 shadowRoot 中时)
225
+ this.shadowRoot.appendChild(content);
226
+ }
227
+
228
+ // 移除旧内容(保留样式元素和未标记元素)
229
+ // 关键修复:使用 shouldPreserveElement() 来保护第三方库注入的元素
230
+ const oldChildren = Array.from(this.shadowRoot.children).filter((child) => {
231
+ // 保留新添加的内容(或已经在 shadowRoot 中的 content)
232
+ if (child === content) {
233
+ return false;
234
+ }
235
+ // 保留样式元素
236
+ if (child instanceof HTMLStyleElement) {
237
+ return false;
238
+ }
239
+ // 保留未标记的元素(第三方库注入的元素、自定义元素)
240
+ // 这是 RFC 0037 Phase 5 的核心:保护未标记元素
241
+ if (shouldPreserveElement(child)) {
242
+ return false;
243
+ }
244
+ return true;
245
+ });
246
+ oldChildren.forEach((child) => child.remove());
247
+
248
+ // 7. 恢复 adopted stylesheets(在 DOM 操作之后,确保样式不被意外移除)
249
+ // 关键修复:在 DOM 操作之后恢复样式,防止样式在 DOM 操作过程中被意外清空
250
+ // 自动检测模式:检查实际的样式状态,确保样式正确恢复
251
+ const hasStylesAfterDOM =
252
+ this.shadowRoot.adoptedStyleSheets && this.shadowRoot.adoptedStyleSheets.length > 0;
253
+ const hasStyleElementAfterDOM = Array.from(this.shadowRoot.children).some(
254
+ (child) => child instanceof HTMLStyleElement
255
+ );
256
+
257
+ if (adoptedStyleSheets.length > 0) {
258
+ // 恢复保存的 adoptedStyleSheets
210
259
  this.shadowRoot.adoptedStyleSheets = adoptedStyleSheets;
260
+ } else if (!hasStylesAfterDOM && !hasStyleElementAfterDOM) {
261
+ // 自动检测模式:如果 DOM 操作后没有样式,自动重新应用(防止样式丢失)
262
+ // 关键修复:在元素复用场景中,如果 _autoStyles 存在但样式未应用,需要重新应用
263
+ const stylesToApply = this._autoStyles || this.config.styles;
264
+ if (stylesToApply) {
265
+ const styleName = this.config.styleName || this.constructor.name;
266
+ StyleManager.applyStyles(this.shadowRoot, styleName, stylesToApply);
267
+ }
211
268
  }
212
269
 
213
- // 7. 使用 requestAnimationFrame 批量执行 DOM 操作
214
- requestAnimationFrame(() => {
215
- // 添加新内容
216
- this.shadowRoot.appendChild(content);
270
+ // 8. 恢复焦点状态并清除渲染标志
271
+ this.restoreFocusState(focusState);
272
+ this._pendingFocusState = null;
217
273
 
218
- // 移除旧内容(保留样式元素和未标记元素)
219
- // 关键修复:使用 shouldPreserveElement() 来保护第三方库注入的元素
220
- const oldChildren = Array.from(this.shadowRoot.children).filter((child) => {
221
- // 保留新添加的内容
222
- if (child === content) {
223
- return false;
224
- }
225
- // 保留样式元素
226
- if (child instanceof HTMLStyleElement) {
227
- return false;
228
- }
229
- // 保留未标记的元素(第三方库注入的元素、自定义元素)
230
- // 这是 RFC 0037 Phase 5 的核心:保护未标记元素
231
- if (shouldPreserveElement(child)) {
232
- return false;
233
- }
234
- return true;
235
- });
236
- oldChildren.forEach((child) => child.remove());
274
+ // 9. 调用 onRendered 生命周期钩子
275
+ this.onRendered?.();
237
276
 
238
- // 恢复焦点状态
239
- requestAnimationFrame(() => {
240
- this.restoreFocusState(focusState);
241
- this._pendingFocusState = null;
242
- // 调用 onRendered 生命周期钩子
243
- this.onRendered?.();
244
- // 在 onRendered() 完成后清除渲染标志,允许后续的 scheduleRerender()
245
- this._isRendering = false;
246
- });
247
- });
277
+ // 10. 清除渲染标志,允许后续的 scheduleRerender()
278
+ this._isRendering = false;
248
279
  } catch (error) {
249
280
  logger.error("Error in _rerender:", error);
250
281
  this.renderError(error);