- Published on
ssg - 约定式路由
前言
最近开了一个新坑, 搭建简易 ssg,对标 vitepress 正在研发核心功能,约定式路由,此篇介绍一下具体实现
Convention Routing
首先会读取用户提供的 config,在结合一些通用的配置,返回一个新的 config, 这样可以做到开箱即用。
const resolveConfig = async (...) => {
// 导入用户配置文件 mmc.config.ts
const userConfig = await resolveUserConfig(...)
const {
extensions = ['js', 'jsx', 'ts', 'tsx', 'md', 'mdx'],
exclude = [],
include = []
} = userConfig.route ?? {}
// 获取根目录下指定文件
const files = (
await fg([`**/*.{${extensions.join(',')}}`, ...include], {
cwd: root,
absolute: true,
ignore: [...DEFAULT_EXCLUDE, ...exclude],
})
).sort()
// 做一些路径上的处理 形成约定式路由
const pages = files.map((file) => ...)
}
这里用到了几个关键的函数
// 获取用户提供的 config.ts 配置文件
resolveUserConfig
// 这是一个非常快速和高效的Node.js glob库。
// https://github.com/mrmlnc/fast-glob
import fg from 'fast-glob'
Plugins
在服务启动时,将获取到的 config 传入 每个 plugin
这里介绍 路由插件,在服务启动时,获取约定式路由,计算一次,在任何地方使用
// vite
export const pluginRoutes = (config: SiteConfig): Plugin => {
const virtualModuleId = 'virtual:routes'
const resolvedVirtualModuleId = `\0${virtualModuleId}`
return {
name: 'vite-plugin-routes',
resolveId(id) {
if (id === virtualModuleId) return resolvedVirtualModuleId
},
load(id, options) {
if (id === resolvedVirtualModuleId) {
// 做不同的处理
console.log(options?.ssr ? 'ssr' : 'not ssr')
return {
// 转换 文件内容
code: generateRoutesCode(config),
moduleSideEffects: false,
}
}
},
}
}
我这里将是读取了文件,并且转换成 react 组件的形式 (需要对应的 loader),可以直接渲染到页面中
export const generateRoutesCode = (options: SiteConfig) => {
const { pages = [] } = options
const namePrefix = 'Page'
return `
import React from 'react'
${pages
.map(
(route, index) =>
`const ${namePrefix}${index} = React.lazy(() => import('${route.filePath}'))`
)
.join('\n')}
export const routes = [
${pages
.map(
(route, index) => `{
path: '${route.path}',
element: React.createElement(${`${namePrefix}${index}`}),
}`
)
.join(',\n')}
]`
}
在其他地方使用
import routes from 'virtual:routes'