如何在SvelteKit应用中挂载Docusaurus静态子站点
在SvelteKit中通过命名路由托管Docusaurus静态文档
可行方案:利用SvelteKit的handle钩子处理静态资源
无需额外Express服务器,SvelteKit的钩子系统可以优雅拦截/doc路径下的所有请求,直接返回Docusaurus构建后的静态文件。
步骤1:配置Docusaurus基础路径
先修改Docusaurus的docusaurus.config.js,指定构建后的资源基础路径,避免和主应用路由冲突:
module.exports = { baseUrl: '/doc/', // 其他原有配置... };
步骤2:编写SvelteKit服务器钩子
在src/hooks.server.js中添加以下代码,拦截并处理/doc开头的请求:
import { resolve } from 'path'; import { readFileSync } from 'fs'; import { fileURLToPath } from 'url'; const __dirname = fileURLToPath(new URL('.', import.meta.url)); // 指向Docusaurus的静态构建目录 const docsBuildDir = resolve(__dirname, '../src/docs/build'); export async function handle({ event, resolve }) { if (event.url.pathname.startsWith('/doc')) { let filePath = event.url.pathname.replace('/doc', ''); // 处理/doc根路径,返回首页index.html if (filePath === '') filePath = '/index.html'; const fullPath = resolve(docsBuildDir, filePath.slice(1)); try { const content = readFileSync(fullPath); // 根据文件后缀设置正确的Content-Type let contentType = 'text/html'; if (fullPath.endsWith('.css')) contentType = 'text/css'; if (fullPath.endsWith('.js')) contentType = 'application/javascript'; if (fullPath.endsWith('.png') || fullPath.endsWith('.jpg')) contentType = `image/${fullPath.split('.').pop()}`; // 可根据需要补充其他文件类型 return new Response(content, { headers: { 'Content-Type': contentType } }); } catch (err) { return new Response('Not Found', { status: 404 }); } } // 非/doc路径按SvelteKit原有逻辑处理 return resolve(event); }
步骤3:开发模式下的便捷处理
如果开发时不想频繁构建Docusaurus,可以通过代理转发请求到Docusaurus的开发服务器。修改钩子代码,添加开发环境代理逻辑:
import { createProxyMiddleware } from 'http-proxy-middleware'; import { resolve } from 'path'; import { readFileSync } from 'fs'; import { fileURLToPath } from 'url'; const __dirname = fileURLToPath(new URL('.', import.meta.url)); const docsBuildDir = resolve(__dirname, '../src/docs/build'); const isDev = process.env.NODE_ENV === 'development'; // 代理到Docusaurus开发服务器(默认端口3001,可根据实际修改) const proxy = isDev ? createProxyMiddleware({ target: 'http://localhost:3001', changeOrigin: true, pathRewrite: { '^/doc': '' } }) : null; export async function handle({ event, resolve }) { if (isDev && event.url.pathname.startsWith('/doc')) { return new Promise((resolveProxy) => { proxy(event.request, event.response, (err) => { if (err) console.error(err); resolveProxy(new Response(null, { status: event.response.statusCode })); }); }); } // 生产环境静态文件处理逻辑(同步骤2的代码) if (event.url.pathname.startsWith('/doc')) { let filePath = event.url.pathname.replace('/doc', ''); if (filePath === '') filePath = '/index.html'; const fullPath = resolve(docsBuildDir, filePath.slice(1)); try { const content = readFileSync(fullPath); let contentType = 'text/html'; if (fullPath.endsWith('.css')) contentType = 'text/css'; if (fullPath.endsWith('.js')) contentType = 'application/javascript'; if (fullPath.endsWith('.png') || fullPath.endsWith('.jpg')) contentType = `image/${fullPath.split('.').pop()}`; return new Response(content, { headers: { 'Content-Type': contentType } }); } catch (err) { return new Response('Not Found', { status: 404 }); } } return resolve(event); }
注意需要安装依赖:npm install http-proxy-middleware --save-dev
为什么之前的方法无效?
- 放入
static目录:Docusaurus构建后的资源依赖相对路径引用,放入static后路径会混乱,且该目录设计初衷是存放独立静态资源,而非完整单页应用。 - 别名引入
index.html:仅能加载首页,但Docusaurus的客户端路由会被SvelteKit的路由系统拦截,导致文档内部跳转失效。
项目文档管理常见方式
- 独立部署:多数项目会将Docusaurus文档单独部署到Vercel、Netlify等平台,通过子域名(如
docs.your-app.com)访问,避免和主应用耦合。 - Monorepo结构:将主应用和文档放在同一个Monorepo中,分别构建部署,便于团队协作管理。
- 内嵌静态资源:即上述钩子方案,适合需要和主应用共享域名、权限体系的场景。
内容的提问来源于stack exchange,提问作者antoine david
相关产品推荐
相关产品推荐

