fix: 增强元素解析和挂载逻辑的健壮性

处理未定义document或无效元素的情况,避免抛出错误
仅在hostElement存在时执行挂载操作,防止空指针异常
This commit is contained in:
anghunk
2026-01-22 10:55:48 +08:00
parent 0a7dca07ed
commit 6dd6e2de52

View File

@@ -54,17 +54,23 @@ export class CWDComments {
* @private * @private
*/ */
_resolveElement(el) { _resolveElement(el) {
if (typeof document === 'undefined') {
return null;
}
if (!el) {
return null;
}
if (typeof el === 'string') { if (typeof el === 'string') {
const element = document.querySelector(el); const element = document.querySelector(el);
if (!element) { if (!element || !(element instanceof HTMLElement)) {
throw new Error(`元素未找到:${el}`); return null;
}
if (!(element instanceof HTMLElement)) {
throw new Error(`目标不是 HTMLElement: ${el}`);
} }
return element; return element;
} }
return el; if (el instanceof HTMLElement) {
return el;
}
return null;
} }
async _loadServerConfig() { async _loadServerConfig() {
@@ -98,27 +104,21 @@ export class CWDComments {
if (this._mounted) { if (this._mounted) {
return; return;
} }
if (this.hostElement) {
// 创建 Shadow DOM this.shadowRoot = this.hostElement.attachShadow({ mode: 'open' });
this.shadowRoot = this.hostElement.attachShadow({ mode: 'open' }); const styleElement = document.createElement('style');
if (typeof styles === 'string') {
// 注入样式 styleElement.textContent = styles;
const styleElement = document.createElement('style'); } else if (styles && typeof styles === 'object' && 'default' in styles) {
if (typeof styles === 'string') { styleElement.textContent = styles.default;
styleElement.textContent = styles; }
} else if (styles && typeof styles === 'object' && 'default' in styles) { this.shadowRoot.appendChild(styleElement);
styleElement.textContent = styles.default; this.mountPoint = document.createElement('div');
} this.mountPoint.className = 'cwd-comments-container';
this.shadowRoot.appendChild(styleElement); this.shadowRoot.appendChild(this.mountPoint);
if (this.config.theme) {
// 创建容器 this.mountPoint.setAttribute('data-theme', this.config.theme);
this.mountPoint = document.createElement('div'); }
this.mountPoint.className = 'cwd-comments-container';
this.shadowRoot.appendChild(this.mountPoint);
// 设置主题
if (this.config.theme) {
this.mountPoint.setAttribute('data-theme', this.config.theme);
} }
(async () => { (async () => {
@@ -139,11 +139,13 @@ export class CWDComments {
}); });
if (!isAllowed) { if (!isAllowed) {
this.mountPoint.innerHTML = ` if (this.mountPoint) {
this.mountPoint.innerHTML = `
<div style="padding: 20px; text-align: center; color: #666; font-size: 14px; border: 1px solid #eee; border-radius: 8px; background: #f9f9f9;"> <div style="padding: 20px; text-align: center; color: #666; font-size: 14px; border: 1px solid #eee; border-radius: 8px; background: #f9f9f9;">
当前域名 (${currentHostname}) 未获得评论组件调用授权 当前域名 (${currentHostname}) 未获得评论组件调用授权
</div> </div>
`; `;
}
return; return;
} }
} }
@@ -161,14 +163,20 @@ export class CWDComments {
const api = createApiClient(this.config); const api = createApiClient(this.config);
this.api = api; this.api = api;
this.store = createCommentStore(this.config, api.fetchComments.bind(api), api.submitComment.bind(api)); if (this.hostElement && this.mountPoint) {
this.store = createCommentStore(
this.config,
api.fetchComments.bind(api),
api.submitComment.bind(api)
);
this.unsubscribe = this.store.store.subscribe((state) => { this.unsubscribe = this.store.store.subscribe((state) => {
this._onStateChange(state); this._onStateChange(state);
}); });
this._render(); this._render();
this.store.loadComments(); this.store.loadComments();
}
if (this.api && typeof this.api.trackVisit === 'function') { if (this.api && typeof this.api.trackVisit === 'function') {
this.api.trackVisit(); this.api.trackVisit();