feat: use defineBasicLoader for vue-router data on demand load

This commit is contained in:
YunYouJun 2024-12-16 01:27:59 +08:00
parent b98cf26373
commit 3485803588
13 changed files with 120 additions and 32 deletions

View File

@ -32,6 +32,7 @@
"cSpell.words": [
"AGUI",
"algoliasearch",
"axios",
"beian",
"codepen",
"defu",

View File

@ -52,6 +52,10 @@ export default defineConfig({
sidebar: {
'/typedoc/': typedocSidebar,
'/notes': [
{
text: 'App Bundle Size',
link: '/notes/app-bundle-size',
},
{
text: 'Shiki 高亮耗时问题',
link: '/notes/shiki-performance',

View File

@ -0,0 +1,49 @@
# 包体积优化
## Data Loaders (Vue Routers)
- [Data Loaders](https://uvr.esm.is/data-loaders/)
使用 Vue Router 的 [Data Loaders](https://uvr.esm.is/data-loaders/),按需加载对应文件的 `frontmatter`。(支持并行的数据获取)
否则初始打包会合并在一个文件中。
```vue
<script>
import { defineBasicLoader } from 'unplugin-vue-router/data-loaders/basic'
export const usePageData = defineBasicLoader('/relativePath', async (_to) => {
// custom basic loader
}, {
lazy: true,
})
</script>
```
```ts
export function injectPageDataCode() {
const vueContextImports = [
`import { provide } from 'vue'`,
`import { useRoute } from 'vue-router'`,
'const { data: pageData } = usePageData()',
'const route = useRoute()',
// $frontmatter contain runtime added data
// for example, $frontmatter.partiallyEncryptedContents
`const $frontmatter = Object.assign(route.meta.frontmatter || {}, pageData.frontmatter || {})
route.meta.frontmatter = $frontmatter
provide('pageData', pageData)
provide('valaxy:frontmatter', $frontmatter)
`,
]
return vueContextImports
}
const loaderVuePath = path.resolve(options.pkgRoot, 'node/templates/loader.vue')
let loaderVue = fs.readFileSync(loaderVuePath, 'utf-8')
loaderVue = loaderVue
.replace('/relativePath', pageData.relativePath.slice('/pages'.length - 1, -'.md'.length))
.replace('// custom basic loader', `return ${transformObject(pageData)}`)
code = loaderVue + code
```

View File

@ -90,6 +90,7 @@ declare module 'vue-router/auto-routes' {
'/sponsors/': RouteRecordInfo<'/sponsors/', '/sponsors', Record<never, never>, Record<never, never>>,
'/tags/': RouteRecordInfo<'/tags/', '/tags', Record<never, never>, Record<never, never>>,
'/test/deadlinks': RouteRecordInfo<'/test/deadlinks', '/test/deadlinks', Record<never, never>, Record<never, never>>,
'/test/define-basic-loader': RouteRecordInfo<'/test/define-basic-loader', '/test/define-basic-loader', Record<never, never>, Record<never, never>>,
'/test/footnotes': RouteRecordInfo<'/test/footnotes', '/test/footnotes', Record<never, never>, Record<never, never>>,
'/test/markdown-file-inclusion': RouteRecordInfo<'/test/markdown-file-inclusion', '/test/markdown-file-inclusion', Record<never, never>, Record<never, never>>,
'/test/special-character': RouteRecordInfo<'/test/special-character', '/test/special-character', Record<never, never>, Record<never, never>>,

View File

@ -0,0 +1,10 @@
<script setup lang="ts">
// @ts-expect-error not work
const pageData = usePageData()
</script>
<template>
<div>
PageData: {{ pageData }}
</div>
</template>

View File

@ -0,0 +1,8 @@
---
title: Define Basic Loader
---
[Defining Data Loaders](https://uvr.esm.is/data-loaders/defining-loaders.html#defining-data-loaders)
<!-- not work -->
<!-- <DefineBasicLoaderVue /> -->

View File

@ -4,7 +4,7 @@ import axios from 'axios'
import consola from 'consola'
import { ref, toRaw } from 'vue'
import { pageData } from '../composables/app'
import { activePath, pageData } from '../composables/app'
const props = defineProps<{
frontmatter: PostFrontMatter
@ -18,6 +18,7 @@ async function saveNewFm() {
// })
const res = await axios.post('/valaxy-devtools-api/frontmatter', {
path: activePath.value,
pageData: pageData.value,
frontmatter: toRaw(newFm.value),
})

View File

@ -53,7 +53,7 @@ onMounted(async () => {
</script>
<template>
<div id="yun-banner" :style="bannerStyles">
<div id="yun-banner" border="b-1px b-solid b-$banner-line-color" :style="bannerStyles">
<div class="banner-line-container">
<div
class="banner-line vertical-line-top"

View File

@ -1,26 +1,27 @@
import type { ViteSSGContext } from 'vite-ssg'
import { DataLoaderPlugin } from 'unplugin-vue-router/data-loaders'
import { initValaxyConfig, valaxyConfigSymbol } from 'valaxy'
import { setupLayouts } from 'virtual:generated-layouts'
import { ViteSSG } from 'vite-ssg'
import { routes } from 'vue-router/auto-routes'
import { routes } from 'vue-router/auto-routes'
// import App from '/@valaxyjs/App.vue'
import App from './App.vue'
import AppLink from './components/AppLink.vue'
import setupMain from './setup/main'
import { setupValaxyDevTools } from './utils/dev'
import { setupValaxyDevTools } from './utils/dev'
// reset styles, load css before app
// import '@unocss/reset/tailwind.css'
// https://unocss.dev/guide/style-reset#tailwind-compat
// minus the background color override for buttons to avoid conflicts with UI frameworks
import '@unocss/reset/tailwind-compat.css'
// css
import './styles/css/css-vars.css'
import './styles/css/main.css'
// generate user styles
import '/@valaxyjs/styles'
import 'uno.css'
@ -78,7 +79,10 @@ export const createApp = ViteSSG(
},
(ctx) => {
// app-level provide
const { app } = ctx
const { app, router } = ctx
// Register the plugin before the router
app.use(DataLoaderPlugin, { router })
app.provide(valaxyConfigSymbol, valaxyConfig)

View File

@ -1,23 +1,23 @@
import type { PageData } from 'valaxy/types'
import type { ResolvedValaxyOptions } from '../../../options'
import path from 'node:path'
import fs from 'fs-extra'
import { transformObject } from '../../../utils'
import { getValaxyMain } from '../../markdown/markdownToVue'
export function injectPageDataCode(
data: PageData,
) {
export function injectPageDataCode() {
const vueContextImports = [
`import { provide } from 'vue'`,
`import { useRoute } from 'vue-router'`,
`
const data = ${transformObject(data)}
const route = useRoute()`,
'const { data: pageData } = usePageData()',
'const route = useRoute()',
// $frontmatter contain runtime added data
// for example, $frontmatter.partiallyEncryptedContents
`const $frontmatter = data.frontmatter || {}
route.meta.frontmatter = Object.assign(route.meta.frontmatter || {}, data.frontmatter || {})
provide('pageData', data)
`const $frontmatter = Object.assign(route.meta.frontmatter || {}, pageData.frontmatter || {})
route.meta.frontmatter = $frontmatter
provide('pageData', pageData)
provide('valaxy:frontmatter', $frontmatter)
`,
]
@ -27,21 +27,24 @@ export function injectPageDataCode(
export function createTransformMarkdown(options: ResolvedValaxyOptions) {
return (code: string, id: string, pageData: PageData) => {
const dataCode = injectPageDataCode(pageData)
const isDev = options.mode === 'dev'
if (!isDev) {
// do not build path in production
delete pageData.filePath
}
const dataCode = injectPageDataCode()
const imports = [
...dataCode,
isDev
? `
window.$pageData = data
`
: '',
isDev ? `globalThis.$pageData = pageData` : '',
'globalThis.$frontmatter = $frontmatter',
]
// const loaderVuePath = path.resolve(options.pkgRoot, 'node/templates/loader.vue')
// const loaderVue = fs.readFileSync(loaderVuePath)
// code = loaderVue + code
const loaderVuePath = path.resolve(options.pkgRoot, 'node/templates/loader.vue')
let loaderVue = fs.readFileSync(loaderVuePath, 'utf-8')
loaderVue = loaderVue
.replace('/relativePath', pageData.relativePath.slice('/pages'.length - 1, -'.md'.length))
.replace('// custom basic loader', `return ${transformObject(pageData)}`)
code = loaderVue + code
// inject imports to <script setup>
const scriptSetupStart = code.indexOf('<script setup>')

View File

@ -38,7 +38,7 @@ export async function generatePageData(code: string, id: string, options: Resolv
// not be used
headers: options.env.headers || [],
relativePath,
path: id,
filePath: id,
}
// if (includeLastUpdatedData)

View File

@ -1,7 +1,10 @@
<script lang="ts">
// import { defineBasicLoader } from 'unplugin-vue-router/data-loaders/basic'
<script>
import { defineBasicLoader } from 'unplugin-vue-router/data-loaders/basic'
// export const useUserData = defineBasicLoader('/users/[id]', async (to) => {
// // return getUserById(to.params.id)
// })
export const usePageData = defineBasicLoader('/relativePath', async (_to) => {
// custom basic loader
}, {
// @see https://uvr.esm.is/data-loaders/defining-loaders.html#non-blocking-loaders-with-lazy
lazy: (to, from) => to.name === from.name,
})
</script>

View File

@ -7,8 +7,12 @@ export type CleanUrlsMode =
| 'with-subfolders'
export interface PageData {
path: string
relativePath: string
/**
* differs from relativePath in case of path rewrites
* absolute file path
*/
filePath?: string
title: string
titleTemplate?: string
description: string