Skip to content

Commit 4baa23d

Browse files
committed
fix: 修改 GlobalUnlock.vue 解决 SSR 水合阶段 + 动态 DOM 操作冲突。
1 parent 9f2eca9 commit 4baa23d

File tree

2 files changed

+25
-9
lines changed

2 files changed

+25
-9
lines changed

docs/.vuepress/components/unlock/GlobalUnlock.vue

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<template>
2-
<div v-if="isLockedPage && !isUnlocked">
3-
<Teleport v-if="teleportTarget" :to="teleportTarget">
2+
<div v-if="isClientReady && isLockedPage && !isUnlocked">
3+
<Teleport v-if="teleportTargetSelector" :to="teleportTargetSelector">
44
<div class="read-more-anchor">
55
<div class="read-more-mask" />
66
<button class="read-more-btn" @click="showDialog = true">
@@ -75,11 +75,12 @@ const STYLE_ID = "unlock-global-style";
7575
const DATA_ATTR = "data-unlock-target";
7676
7777
const pageData = usePageData();
78+
const isClientReady = ref(false);
7879
const isUnlocked = ref(false);
7980
const inputCode = ref("");
8081
const showError = ref(false);
8182
const showDialog = ref(false);
82-
const teleportTarget = ref<HTMLElement | null>(null);
83+
const teleportTargetSelector = ref<string | null>(null);
8384
const globalUnlockKey = `javaguide_site_unlocked_${config.unlockVersion ?? "v1"}`;
8485
8586
const normalizePath = (path: string) =>
@@ -155,13 +156,13 @@ const buildLockCSS = (height: string) => `
155156
`;
156157
157158
const applyLockStyle = async () => {
158-
if (typeof document === "undefined") return;
159+
if (typeof document === "undefined" || !isClientReady.value) return;
159160
160161
document.querySelectorAll(`[${DATA_ATTR}]`).forEach((el) => {
161162
el.removeAttribute(DATA_ATTR);
162163
});
163164
164-
teleportTarget.value = null;
165+
teleportTargetSelector.value = null;
165166
const styleEl = ensureStyleEl();
166167
167168
if (!isLockedPage.value || isUnlocked.value) {
@@ -176,6 +177,12 @@ const applyLockStyle = async () => {
176177
return;
177178
}
178179
180+
// 路由切换期间节点可能已卸载,避免 hydration 阶段异常
181+
if (!document.contains(contentEl)) {
182+
styleEl.innerHTML = "";
183+
return;
184+
}
185+
179186
// 内容不够长时不加锁、不展示按钮
180187
if (contentEl.scrollHeight <= toPx(visibleHeight.value)) {
181188
styleEl.innerHTML = "";
@@ -184,7 +191,10 @@ const applyLockStyle = async () => {
184191
185192
contentEl.setAttribute(DATA_ATTR, "true");
186193
styleEl.innerHTML = buildLockCSS(visibleHeight.value);
187-
teleportTarget.value = contentEl;
194+
if (!contentEl.id) {
195+
contentEl.id = "unlock-content-root";
196+
}
197+
teleportTargetSelector.value = `#${contentEl.id}`;
188198
};
189199
190200
const handleUnlock = () => {
@@ -205,14 +215,20 @@ const handleUnlock = () => {
205215
};
206216
207217
onMounted(() => {
218+
isClientReady.value = true;
208219
readUnlockState();
209-
applyLockStyle();
210-
setTimeout(applyLockStyle, 300);
220+
nextTick(() => {
221+
applyLockStyle();
222+
// 再补一帧,等主题异步渲染完成
223+
requestAnimationFrame(() => applyLockStyle());
224+
setTimeout(applyLockStyle, 300);
225+
});
211226
});
212227
213228
watch(
214229
() => pageData.value.path,
215230
async () => {
231+
if (!isClientReady.value) return;
216232
readUnlockState();
217233
showDialog.value = false;
218234
await applyLockStyle();

docs/.vuepress/features/unlock/config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export const unlockConfig = {
1010
// 版本号变更可强制用户重新验证
1111
unlockVersion: "v1",
1212
// 调试用:设为 true 时无视本地已解锁状态,始终触发限制
13-
forceLock: true,
13+
forceLock: false,
1414
code: "8888",
1515
// 使用相对路径,图片放在 docs/.vuepress/public/images 下
1616
qrCodeUrl: "/images/qrcode-javaguide.jpg",

0 commit comments

Comments
 (0)