Sveltekit能否通过编程方式创建路由?复刻React-admin式CRUD路由生成
SvelteKit 编程式创建路由方案(适配CRUD框架需求)
SvelteKit 虽然默认采用文件系统路由,但完全支持通过代码生成或动态路由钩子实现编程式路由创建,足以复刻 React-admin 这类声明式CRUD框架的功能。以下是两种核心实现方案:
一、代码生成方案(推荐,贴合SvelteKit原生机制)
通过Node.js脚本读取你声明的资源配置,自动在src/routes目录下生成对应路由文件,完全兼容SvelteKit的SSR、预渲染等特性。
步骤示例
- 定义资源配置文件
创建src/lib/admin/resources.js,声明需要管理的资源及对应视图:
export const resources = [ { name: 'users', listView: './components/UserList.svelte', editView: './components/UserEdit.svelte', createView: './components/UserCreate.svelte' }, { name: 'posts', listView: './components/PostList.svelte', editView: './components/PostEdit.svelte' } ];
- 编写路由生成脚本
创建scripts/generate-admin-routes.js,读取配置并生成路由文件:
import fs from 'fs/promises'; import path from 'path'; import { resources } from '../src/lib/admin/resources.js'; const routesDir = path.join(process.cwd(), 'src/routes/admin'); await fs.rm(routesDir, { recursive: true, force: true }); await fs.mkdir(routesDir, { recursive: true }); for (const resource of resources) { const resourceDir = path.join(routesDir, resource.name); await fs.mkdir(resourceDir, { recursive: true }); // 生成列表页路由 if (resource.listView) { const content = ` <script> import ListView from '${resource.listView}'; import { page } from '$app/stores'; </script> <ListView resource="${resource.name}" params={$page.params} /> `.trim(); await fs.writeFile(path.join(resourceDir, '+page.svelte'), content); } // 生成编辑页路由 if (resource.editView) { const editDir = path.join(resourceDir, '[id]'); await fs.mkdir(editDir, { recursive: true }); const content = ` <script> import EditView from '${resource.editView}'; import { page } from '$app/stores'; </script> <EditView resource="${resource.name}" id={$page.params.id} /> `.trim(); await fs.writeFile(path.join(editDir, '+page.svelte'), content); } // 生成创建页路由 if (resource.createView) { const createDir = path.join(resourceDir, 'create'); await fs.mkdir(createDir, { recursive: true }); const content = ` <script> import CreateView from '${resource.createView}'; </script> <CreateView resource="${resource.name}" /> `.trim(); await fs.writeFile(path.join(createDir, '+page.svelte'), content); } } console.log('Admin routes generated successfully!');
- 添加生成命令
在package.json中添加脚本,方便快速生成路由:
{ "scripts": { "generate:admin": "node scripts/generate-admin-routes.js" } }
每次修改资源配置后,运行npm run generate:admin即可更新路由。
二、动态路由钩子方案(适合运行时动态加载)
利用SvelteKit的通配符路由和load函数,在运行时根据请求路径匹配资源视图,无需提前生成文件。
实现示例
- 创建通配符路由
创建src/routes/admin/[...path]/+page.server.js,处理路由匹配:
import { resources } from '$lib/admin/resources.js'; import { error } from '@sveltejs/kit'; export async function load({ params }) { const [resourceName, action, id] = params.path.split('/'); const resource = resources.find(r => r.name === resourceName); if (!resource) throw error(404, '资源不存在'); let componentPath; switch (action) { case 'edit': componentPath = resource.editView; break; case 'create': componentPath = resource.createView; break; default: componentPath = resource.listView; break; } if (!componentPath) throw error(404, '视图不存在'); return { resource, componentPath, id, action: action || 'list' }; }
- 创建视图渲染页
对应+page.svelte文件,动态加载匹配的视图组件:
<script> export let data; const View = await import(data.componentPath); </script> <svelte:component this={View.default} resource={data.resource} id={data.id} action={data.action} />
三、通用后端对接逻辑封装
可以封装一个统一的API工具,根据资源名称自动拼接接口路径,减少重复代码:
// src/lib/admin/api.js export async function api(resource, action, data = {}) { const baseUrl = '/api/admin'; let url = `${baseUrl}/${resource}`; if ((action === 'get' || action === 'edit') && data.id) { url += `/${data.id}`; } const options = { method: action === 'list' ? 'GET' : action === 'create' ? 'POST' : action === 'edit' ? 'PUT' : 'DELETE', headers: { 'Content-Type': 'application/json' }, body: ['POST', 'PUT', 'DELETE'].includes(action) ? JSON.stringify(data) : undefined }; const res = await fetch(url, options); if (!res.ok) throw new Error(await res.text()); return res.json(); }
在视图组件中直接调用即可,比如用户列表页:
<script> import { api } from '$lib/admin/api.js'; import { onMount } from 'svelte'; export let resource; let items = []; onMount(async () => { items = await api(resource, 'list'); }); </script> <h1>{resource} 列表</h1> <ul> {#each items as item} <li><a href="/admin/{resource}/{item.id}/edit">{item.name}</a></li> {/each} </ul>
内容的提问来源于stack exchange,提问作者DjebbZ
相关产品推荐
相关产品推荐

