Merge pull request #430 from nagisa77/codex/add-server-side-rendering-for-index.vue

Enable SSR initial render for home and post pages
This commit is contained in:
Tim
2025-08-08 12:58:05 +08:00
committed by GitHub
2 changed files with 36 additions and 30 deletions

View File

@@ -107,7 +107,7 @@
</template> </template>
<script> <script>
import { ref, onMounted, watch } from 'vue' import { ref, watch } from 'vue'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
import { useScrollLoadMore } from '~/utils/loadMore' import { useScrollLoadMore } from '~/utils/loadMore'
import { stripMarkdown } from '~/utils/markdown' import { stripMarkdown } from '~/utils/markdown'
@@ -131,7 +131,7 @@ export default {
SearchDropdown, SearchDropdown,
ClientOnly: () => import('vue').then(m => m.defineAsyncComponent(() => import('vue').then(() => ({ template: '<slot />' })))) ClientOnly: () => import('vue').then(m => m.defineAsyncComponent(() => import('vue').then(() => ({ template: '<slot />' }))))
}, },
setup() { async setup() {
const route = useRoute() const route = useRoute()
/** /**
@@ -170,14 +170,11 @@ export default {
const allLoaded = ref(false) const allLoaded = ref(false)
/** /**
* -------- 2. CLIENTSIDE ONLY: LDRS REGISTER -------- * -------- 2. INIT FETCH FOR SSR --------
* 这里使用动态 import 避免 SSR 阶段触发 HTMLElement 未定义错误 * 服务端渲染阶段也需要获取首页内容和选项
*/ */
onMounted(async () => { await loadOptions()
// 首次加载 await fetchContent()
fetchContent()
await loadOptions()
})
/** /**
* -------- 3. FETCH OPTION HELPERS -------- * -------- 3. FETCH OPTION HELPERS --------

View File

@@ -126,7 +126,7 @@ import Dropdown from '../components/Dropdown.vue'
export default { export default {
name: 'PostPageView', name: 'PostPageView',
components: { CommentItem, CommentEditor, BaseTimeline, ArticleTags, ArticleCategory, ReactionsGroup, DropdownMenu, VueEasyLightbox, Dropdown }, components: { CommentItem, CommentEditor, BaseTimeline, ArticleTags, ArticleCategory, ReactionsGroup, DropdownMenu, VueEasyLightbox, Dropdown },
setup() { async setup() {
const route = useRoute() const route = useRoute()
const postId = route.params.id const postId = route.params.id
const router = useRouter() const router = useRouter()
@@ -150,27 +150,35 @@ export default {
const commentSort = ref('NEWEST') const commentSort = ref('NEWEST')
const isFetchingComments = ref(false) const isFetchingComments = ref(false)
// record default metadata from the main document // record default metadata from the main document (client only)
const defaultTitle = document.title const defaultTitle = process.client ? document.title : ''
const metaDescriptionEl = document.querySelector('meta[name="description"]') const metaDescriptionEl = process.client
const defaultDescription = metaDescriptionEl ? metaDescriptionEl.getAttribute('content') : '' ? document.querySelector('meta[name="description"]')
const headerHeight = parseFloat(getComputedStyle(document.documentElement).getPropertyValue('--header-height')) || 0 : null
const defaultDescription = process.client && metaDescriptionEl
? metaDescriptionEl.getAttribute('content')
: ''
const headerHeight = process.client
? parseFloat(getComputedStyle(document.documentElement).getPropertyValue('--header-height')) || 0
: 0
watch(title, t => { if (process.client) {
document.title = `OpenIsle - ${t}` watch(title, t => {
}) document.title = `OpenIsle - ${t}`
})
watch(postContent, c => { watch(postContent, c => {
if (metaDescriptionEl) { if (metaDescriptionEl) {
metaDescriptionEl.setAttribute('content', stripMarkdownLength(c, 400)) metaDescriptionEl.setAttribute('content', stripMarkdownLength(c, 400))
} }
}) })
onBeforeUnmount(() => { onBeforeUnmount(() => {
document.title = defaultTitle document.title = defaultTitle
if (metaDescriptionEl) metaDescriptionEl.setAttribute('content', defaultDescription) if (metaDescriptionEl) metaDescriptionEl.setAttribute('content', defaultDescription)
window.removeEventListener('scroll', updateCurrentIndex) window.removeEventListener('scroll', updateCurrentIndex)
}) })
}
const lightboxVisible = ref(false) const lightboxVisible = ref(false)
const lightboxIndex = ref(0) const lightboxIndex = ref(0)
@@ -294,7 +302,7 @@ export default {
}) })
isWaitingFetchingPost.value = false; isWaitingFetchingPost.value = false;
if (!res.ok) { if (!res.ok) {
if (res.status === 404) { if (res.status === 404 && process.client) {
router.replace('/404') router.replace('/404')
} }
return return
@@ -581,10 +589,11 @@ export default {
router.push(`/users/${author.value.id}`) router.push(`/users/${author.value.id}`)
} }
await fetchPost()
onMounted(async () => { onMounted(async () => {
const hash = location.hash const hash = location.hash
const id = hash.startsWith('#comment-') ? hash.substring('#comment-'.length) : null const id = hash.startsWith('#comment-') ? hash.substring('#comment-'.length) : null
await fetchPost()
if (id) expandCommentPath(id) if (id) expandCommentPath(id)
updateCurrentIndex() updateCurrentIndex()
window.addEventListener('scroll', updateCurrentIndex) window.addEventListener('scroll', updateCurrentIndex)