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";
7575const DATA_ATTR = " data-unlock-target" ;
7676
7777const pageData = usePageData ();
78+ const isClientReady = ref (false );
7879const isUnlocked = ref (false );
7980const inputCode = ref (" " );
8081const showError = ref (false );
8182const showDialog = ref (false );
82- const teleportTarget = ref <HTMLElement | null >(null );
83+ const teleportTargetSelector = ref <string | null >(null );
8384const globalUnlockKey = ` javaguide_site_unlocked_${config .unlockVersion ?? " v1" } ` ;
8485
8586const normalizePath = (path : string ) =>
@@ -155,13 +156,13 @@ const buildLockCSS = (height: string) => `
155156` ;
156157
157158const 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
190200const handleUnlock = () => {
@@ -205,14 +215,20 @@ const handleUnlock = () => {
205215};
206216
207217onMounted (() => {
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
213228watch (
214229 () => pageData .value .path ,
215230 async () => {
231+ if (! isClientReady .value ) return ;
216232 readUnlockState ();
217233 showDialog .value = false ;
218234 await applyLockStyle ();
0 commit comments