From 4c8d8da506ae6a8366cce63bf75711fe83308bc1 Mon Sep 17 00:00:00 2001 From: bsdayo Date: Sun, 5 May 2024 08:46:30 +0800 Subject: [PATCH] fix: hack into vitepress to get naiveui ssr style working --- .prettierrc.json | 5 +++++ .vitepress/config.ts | 30 +++++++++++++++++++++++++++++- .vitepress/theme/Layout.vue | 18 +++--------------- .vitepress/theme/custom.scss | 4 ++-- .vitepress/theme/index.ts | 5 +++++ package.json | 1 + pnpm-lock.yaml | 15 +++++++++++++++ 7 files changed, 60 insertions(+), 18 deletions(-) create mode 100644 .prettierrc.json diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..f021489 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,5 @@ +{ + "printWidth": 100, + "semi": false, + "singleQuote": true +} \ No newline at end of file diff --git a/.vitepress/config.ts b/.vitepress/config.ts index db0d54a..badde08 100644 --- a/.vitepress/config.ts +++ b/.vitepress/config.ts @@ -1,7 +1,12 @@ -import { defineConfigWithTheme } from 'vitepress' +import { EnhanceAppContext, defineConfigWithTheme } from 'vitepress' +import { setup } from '@css-render/vue3-ssr' import { createContainer } from './utils' import { ThemeConfig } from './theme' import tags from './tags' +import { renderToString } from 'vue/server-renderer' +import { Mutex } from 'async-mutex' + +const transformHtmlMutex = new Mutex() export default defineConfigWithTheme({ title: 'SynBlog', @@ -39,6 +44,29 @@ export default defineConfigWithTheme({ ignoreDeadLinks: 'localhostLinks', + async transformHtml(html, id, ctx) { + // VitePress 似乎是对所有页面同时进行渲染,但因为限制只能拿到一个固定的 App 实例,所以需要加互斥锁,防止抢 router + const release = await transformHtmlMutex.acquire() + try { + // ./theme/index.ts 中存了一个全局的 App 实例下来 + const { app, router } = (globalThis as any).__EnhanceAppContext__ as EnhanceAppContext + const { collect } = setup(app) + + // 随后仿照 VitePress 走 SSR 渲染流程 + // https://github.com/vuejs/vitepress/blob/main/src/client/app/ssr.ts + const path = (this.base || '/') + ctx.page.replace(/\.md$/, '.html') + await router.go(path) + await renderToString(app) + + // 此时可以收集到所有的 CSS 了! + const styles = collect() + return html.replace('', `${styles}`) + } finally { + // 释放互斥锁 + release() + } + }, + themeConfig: { tags, diff --git a/.vitepress/theme/Layout.vue b/.vitepress/theme/Layout.vue index 3955220..ff2cea7 100644 --- a/.vitepress/theme/Layout.vue +++ b/.vitepress/theme/Layout.vue @@ -11,11 +11,7 @@ - + @@ -39,14 +35,7 @@ import { ref, computed, nextTick, watch, onMounted } from 'vue' import { useData } from 'vitepress' import DefaultTheme from 'vitepress/theme' import { data as posts } from '../posts.data' -import { - NConfigProvider, - NThing, - NDivider, - NFlex, - lightTheme, - darkTheme, -} from 'naive-ui' +import { NConfigProvider, NThing, NDivider, NFlex, lightTheme, darkTheme } from 'naive-ui' import Giscus from '@giscus/vue' import mediumZoom from 'medium-zoom' import { ThemeConfig } from '.' @@ -72,8 +61,7 @@ const mdImgSelector = '.vp-doc img' function setRandomTagline() { const taglineElement = document.querySelector('.tagline') if (!taglineElement) return - taglineElement.innerHTML = - taglines[Math.floor(Math.random() * taglines.length)] + taglineElement.innerHTML = taglines[Math.floor(Math.random() * taglines.length)] } function appendImgAlt() { diff --git a/.vitepress/theme/custom.scss b/.vitepress/theme/custom.scss index ebe052a..66b5baa 100644 --- a/.vitepress/theme/custom.scss +++ b/.vitepress/theme/custom.scss @@ -14,8 +14,8 @@ $custom-blocks: ( ); :root { - --vp-font-family-mono: 'JetBrains Mono', ui-monospace, 'Menlo', 'Monaco', - 'Consolas', 'Liberation Mono', 'Courier New', monospace; + --vp-font-family-mono: 'JetBrains Mono', ui-monospace, 'Menlo', 'Monaco', 'Consolas', + 'Liberation Mono', 'Courier New', monospace; --vp-c-brand-1: #0d47a1; --vp-c-brand-2: #1565c0; diff --git a/.vitepress/theme/index.ts b/.vitepress/theme/index.ts index 4042798..49410a2 100644 --- a/.vitepress/theme/index.ts +++ b/.vitepress/theme/index.ts @@ -7,6 +7,11 @@ import './custom.scss' const theme: Theme = { extends: DefaultTheme, Layout, + async enhanceApp(ctx) { + if (!(globalThis as any).__EnhanceAppContext__) { + ;(globalThis as any).__EnhanceAppContext__ = ctx + } + }, } export default theme diff --git a/package.json b/package.json index a2b2611..7475e1c 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "@types/markdown-it-container": "^2.0.10", "@types/node": "^20.12.8", "@types/webfontloader": "^1.6.38", + "async-mutex": "^0.5.0", "js-convert-case": "^4.2.0", "markdown-it-container": "^4.0.0", "medium-zoom": "^1.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 693dd91..1727280 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -38,6 +38,9 @@ importers: '@types/webfontloader': specifier: ^1.6.38 version: 1.6.38 + async-mutex: + specifier: ^0.5.0 + version: 0.5.0 js-convert-case: specifier: ^4.2.0 version: 4.2.0 @@ -593,6 +596,9 @@ packages: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} + async-mutex@0.5.0: + resolution: {integrity: sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==} + async-validator@4.2.5: resolution: {integrity: sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==} @@ -804,6 +810,9 @@ packages: treemate@0.3.11: resolution: {integrity: sha512-M8RGFoKtZ8dF+iwJfAJTOH/SM4KluKOKRJpjCMhI8bG3qB74zrFoArKZ62ll0Fr3mqkMJiQOmWYkdYgDeITYQg==} + tslib@2.6.2: + resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} + undici-types@5.26.5: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} @@ -1365,6 +1374,10 @@ snapshots: normalize-path: 3.0.0 picomatch: 2.3.1 + async-mutex@0.5.0: + dependencies: + tslib: 2.6.2 + async-validator@4.2.5: {} binary-extensions@2.3.0: {} @@ -1606,6 +1619,8 @@ snapshots: treemate@0.3.11: {} + tslib@2.6.2: {} + undici-types@5.26.5: {} vdirs@0.1.8(vue@3.4.26):