feat(theme): 新增 markdown render 缓存,优化开发服务启动时间

This commit is contained in:
pengzhanbo 2024-06-24 00:10:58 +08:00
parent 4789f1e104
commit 8c3e5f0f06
3 changed files with 115 additions and 1 deletions

View File

@ -2,7 +2,7 @@ import config from '@pengzhanbo/eslint-config-vue'
export default config({
// todo: 正则校验
// 当前项目中的 正则 海冰不能完全通过 规则,存在 53 个问题
// 当前项目中的 正则 还并不能完全通过 规则,存在 53 个问题
// 但处理起来比较麻烦,因此将会作为一项比较长期的工作来完成。
regexp: false,
ignores: [

View File

@ -0,0 +1,111 @@
/**
* 使 shiki + twoslash markdown
* markdown render
* markdown render content hash
*
*
* vuepress/ecosystem
* vuepress/core
*
*
* 使 13s 1.2s
* vuepress shiki 0.5s
*/
import { createHash } from 'node:crypto'
import type { App } from 'vuepress'
import type { Markdown, MarkdownEnv } from 'vuepress/markdown'
import { fs } from 'vuepress/utils'
interface CacheContent {
content: string
env: MarkdownEnv
}
const cacheDir = 'markdown/render'
const metaFile = '_metadata.json'
export async function extendsMarkdown(md: Markdown, app: App): Promise<void> {
// 如果是在 构建阶段,且缓存文件夹不存在,则不进行缓存
// 因为构建阶段仅一次性产物,生成缓存资源反而会带来额外的开销
if (app.env.isBuild && !fs.existsSync(app.dir.cache())) {
return
}
await fs.ensureDir(app.dir.cache(cacheDir))
const metadata = await readMetadata(app)
const writeCache = (filepath: string, cache: CacheContent) => {
const cachePath = app.dir.cache(cacheDir, filepath)
const content = JSON.stringify(cache)
fs.writeFileSync(cachePath, content, 'utf-8')
}
const readCache = (filepath: string): CacheContent | null => {
const cachePath = app.dir.cache(cacheDir, filepath)
try {
const content = fs.readFileSync(cachePath, 'utf-8')
return JSON.parse(content) as CacheContent
}
catch {}
return null
}
const rawRender = md.render
md.render = (input, env: MarkdownEnv) => {
const filepath = env.filePathRelative
if (!filepath) {
return rawRender(input, env)
}
const hash = getContentHash(input)
const cachePath = normalizePath(filepath)
if (metadata[filepath] === hash) {
const cache = readCache(cachePath)
if (cache) {
Object.assign(env, cache.env)
return cache.content
}
}
metadata[filepath] = hash
const renderedContent = rawRender(input, env)
writeCache(cachePath, { content: renderedContent, env })
updateMetadata(app, metadata)
return renderedContent
}
}
async function readMetadata(app: App): Promise<Record<string, string>> {
const filepath = app.dir.cache(cacheDir, metaFile)
try {
const content = await fs.readFile(filepath, 'utf-8')
return JSON.parse(content)
}
catch {}
return {}
}
let timer: NodeJS.Timeout | null = null
function updateMetadata(app: App, metadata: Record<string, string>) {
const filepath = app.dir.cache(cacheDir, metaFile)
timer && clearTimeout(timer)
timer = setTimeout(
async () => await fs.writeFile(filepath, JSON.stringify(metadata), 'utf-8'),
200,
)
}
function normalizePath(filepath: string) {
return getContentHash(filepath)
}
function getContentHash(content: string): string {
const hash = createHash('md5')
hash.update(content)
return hash.digest('hex')
}

View File

@ -13,6 +13,7 @@ import {
templateBuildRenderer,
} from './config/index.js'
import { setupPrepare, watchPrepare } from './prepare/index.js'
import { extendsMarkdown } from './extendsMarkdown.js'
export function plumeTheme(options: PlumeThemeOptions = {}): Theme {
const {
@ -55,6 +56,8 @@ export function plumeTheme(options: PlumeThemeOptions = {}): Theme {
resolvePageHead(page, localeOptions)
},
extendsMarkdown,
extendsBundlerOptions,
templateBuildRenderer,