Skip to content

参考

基于 SolidStart 1.x(@solidjs/start / @solidjs/router / @solidjs/meta) —— API 速查 / 内置组件 / 文件约定 / app.config.ts 全部选项 / Adapter Preset 列表 / 命名约定 / 常见 import 来源

包结构

用途必需
solid-jsSolid 核心(信号 / store / JSX runtime)
@solidjs/startSolidStart 运行时(createHandler / StartServer / StartClient / mount / clientOnly / HttpStatusCode / HttpHeader
@solidjs/start/configdefineConfig —— v1 配置文件用v1
@solidjs/start/router<FileRoutes /> 自动路由表
@solidjs/start/serverSSR 服务端入口(createHandler / StartServer
@solidjs/start/client客户端 hydration 入口(mount / StartClient
@solidjs/start/middlewarecreateMiddleware可选
@solidjs/start/env环境变量类型(v2 必需)v2
@solidjs/router路由 + query / action / createAsync / redirect / 等99% 项目用
@solidjs/metahead meta 标签管理(Title / Meta / LinkSEO 时
vinxiv1 底层构建/运行时v1
vinxi/httpv1 HTTP 工具(useSession / getCookie / setCookiev1
@solidjs/vite-plugin-nitro-2v2 底层 Nitro pluginv2
vite底层构建工具

核心 API(@solidjs/router

数据加载

API签名说明
queryquery<Args, R>(fn: (...args: Args) => Promise<R>, key: string): QueryFn创建可缓存数据加载函数
createAsynccreateAsync<T>(fn: () => Promise<T>, options?: { initialValue, deferStream }): Accessor<T | undefined>订阅 query 结果
revalidaterevalidate(key?: string | string[], force?: boolean): Promise<void>手动刷新 query 缓存
reloadreload(): Response重新加载当前路由
redirectredirect(url: string, init?: ResponseInit): Response重定向(throw 或 return)
jsonjson<T>(data: T, init?: ResponseInit): Response返回自定义 JSON 响应
tsx
import { query, createAsync, revalidate, redirect, reload, json } from "@solidjs/router";

// 定义 query
const getPosts = query(async () => {
  "use server";
  return await db.posts.findMany();
}, "posts");

// 订阅 query
const posts = createAsync(() => getPosts(), { deferStream: true });

// 手动 revalidate
await revalidate("posts");           // 刷新指定 key
await revalidate(["posts", "user"]); // 刷新多个 key
await revalidate();                   // 刷新全部

// redirect / reload
throw redirect("/login");
throw reload();
return json({ ok: true }, { status: 200, headers: { "X-Custom": "v" } });

副作用

API签名说明
actionaction<Args, R>(fn: (...args: Args) => Promise<R>, key: string): ActionFn创建副作用函数
useSubmissionuseSubmission(action): { pending, result, error, input, clear, retry }订阅单次提交
useSubmissionsuseSubmissions(action): Array<Submission>订阅所有提交(数组)
useActionuseAction(action): (...args) => Promise<R>拿到 action 的程序化调用版
tsx
import { action, useSubmission, useSubmissions, useAction } from "@solidjs/router";

const createPost = action(async (formData: FormData) => {
  "use server";
  return await db.posts.create(/* ... */);
}, "createPost");

// 1. 表单提交
<form action={createPost} method="post">...</form>

// 2. 程序化调用
const submit = useAction(createPost);
await submit(formData);

// 3. .with() 预填参数
const addComment = action(
  async (postId: string, formData: FormData) => { /* ... */ },
  "addComment"
);
<form action={addComment.with(postId)} method="post">...</form>

// 4. 状态订阅
const submission = useSubmission(createPost);
// submission.pending / submission.result / submission.error / submission.input
// submission.clear() / submission.retry()

const submissions = useSubmissions(createPost);
// 数组形式,所有进行中的提交

路由

API签名说明
RouterRouter({ root, base }: RouterProps): JSX.Element根 Router 组件
RouteRoute({ path, component, load, ... })单个路由(手写时用)
<A>A(props: { href, activeClass, inactiveClass, end, ... })SPA 风格链接
useLocationuseLocation(): Location当前 URL / pathname / query
useNavigateuseNavigate(): NavigateFn编程式导航
useParamsuseParams<T>(): T动态段(getter)
useSearchParamsuseSearchParams<T>(): [T, SetterFn]URL 查询参数
useMatchuseMatch(path): () => MatchResult | undefined路径匹配检查
useBeforeLeaveuseBeforeLeave(handler): void离开前钩子
useResolvedPathuseResolvedPath(path: string): () => string解析相对路径
tsx
import {
  Router, A, useLocation, useNavigate, useParams, useSearchParams,
} from "@solidjs/router";

// 1. Router 配置
<Router base="/app" root={(props) => <Layout>{props.children}</Layout>}>
  <FileRoutes />
</Router>

// 2. 链接
<A href="/about" activeClass="active" inactiveClass="muted" end={true}>
  About
</A>

// 3. 编程式导航
const navigate = useNavigate();
navigate("/dashboard");                    // 跳转
navigate("/login", { replace: true });    // 替换 history
navigate(-1);                              // 回退

// 4. URL 解析
const location = useLocation();
// location.pathname / location.search / location.hash / location.state

// 5. 动态段
const params = useParams<{ id: string }>();
console.log(params.id);  // ← getter,不能解构

// 6. 查询参数
const [search, setSearch] = useSearchParams<{ q?: string }>();
setSearch({ q: "hello" });  // ?q=hello

<FileRoutes>@solidjs/start/router

tsx
import { FileRoutes } from "@solidjs/start/router";

// 用法:放在 <Router> 内
<Router>
  <FileRoutes />
</Router>

行为

  • 自动扫描 src/routes/ 下的所有文件
  • 根据约定(见下方「文件约定」)生成 Route 配置
  • 支持 dynamic / catch-all / route group / nested layout

RouteDefinition:路由元数据

ts
import type { RouteDefinition } from "@solidjs/router";

export const route = {
  // 路由级 preload(路由切换时立即执行)
  preload: ({ params, location, intent }) => {
    getPost(params.id);
    getComments(params.id);
  },
  // 离开前钩子
  preventLeave: () => confirm("Unsaved changes, leave?"),
  // 路由匹配模式
  matchFilters: {
    id: /^\d+$/,
  },
} satisfies RouteDefinition;

RouteSectionProps:路由组件的 props 类型

ts
import type { RouteSectionProps } from "@solidjs/router";

export default function PostPage(props: RouteSectionProps<unknown, "id">) {
  // props.params.id(typed)
  // props.location
  // props.children(嵌套 layout 的子内容)
  // props.data(旧 API,已废弃,用 query + createAsync 替代)
}

服务端 API(@solidjs/start/server

createHandler

ts
function createHandler(
  fn: (event: PageEvent) => JSX.Element,
  options?: {
    mode?: "sync" | "async" | "stream";
    onCompleteShell?(): void;
    onCompleteAll?(): void;
  },
  load?: (event: PageEvent) => Record<string, unknown>
): RequestHandler;
tsx
// src/entry-server.tsx
import { createHandler, StartServer } from "@solidjs/start/server";

export default createHandler((event) => (
  <StartServer
    document={({ assets, children, scripts }) => (
      <html>
        <head>{assets}</head>
        <body>
          <div id="app">{children}</div>
          {scripts}
        </body>
      </html>
    )}
  />
));

// 带 mode + load
export default createHandler(
  (event) => <StartServer document={Document} />,
  { mode: "async" },
  (event) => ({ nonce: event.locals.nonce })  // 注入到 props.data
);
Mode含义
syncrenderToString — 无 Suspense
asyncrenderToStringAsync — 等所有 Suspense 完成再发响应
stream默认 — Suspense 准备好就发,未准备好稍后流式补

<StartServer>

tsx
import { StartServer } from "@solidjs/start/server";

type DocumentComponentProps = {
  assets: JSX.Element;
  scripts: JSX.Element;
  children?: JSX.Element;
};

<StartServer document={(props) => <html>...</html>} />

APIEvent:API 路由参数

ts
import type { APIEvent } from "@solidjs/start/server";

interface APIEvent {
  request: Request;
  params: Record<string, string>;
  fetch(input: RequestInfo, init?: RequestInit): Promise<Response>;
  nativeEvent?: H3Event;  // 平台特定
  locals: Record<string, unknown>;
}

// 使用
export async function GET(event: APIEvent) {
  const id = event.params.id;
  return Response.json({ id });
}

<HttpStatusCode>

tsx
import { HttpStatusCode } from "@solidjs/start";

<HttpStatusCode code={404} text="Not Found" />
Prop类型说明
codenumberHTTP 状态码(必填)
textstring状态文本(可选)

仅 SSR 时影响响应;client 端 navigation 时该组件不影响任何东西。

<HttpHeader>

tsx
import { HttpHeader } from "@solidjs/start";

<HttpHeader name="Cache-Control" value="max-age=3600" />
<HttpHeader name="Set-Cookie" value="theme=dark" append />
Prop类型说明
namestringheader 名(必填)
valuestringheader 值(必填)
appendbooleantrue 时追加;false(默认)覆盖

仅 SSR 时生效。

客户端 API(@solidjs/start/client

mount + <StartClient>

tsx
// src/entry-client.tsx
import { mount, StartClient } from "@solidjs/start/client";

mount(() => <StartClient />, document.getElementById("app")!);
API签名说明
mountmount(fn: () => JSX.Element, el: MountableElement): (() => void) | undefinedhydration 入口
StartClient() => JSX.Element客户端应用根

clientOnly:仅客户端组件

tsx
import { clientOnly } from "@solidjs/start";

// 服务器端渲染 fallback;客户端 hydration 后加载真实组件
const Map = clientOnly(() => import("./Map"), { lazy: true });

export default function Page() {
  return <Map fallback={<p>加载地图中...</p>} />;
}
选项类型说明
lazybooleantrue 时不立即 import,仅在组件渲染时加载

Middleware API

ts
import { createMiddleware } from "@solidjs/start/middleware";

export default createMiddleware({
  onRequest: (event) => {
    // 请求开始
    event.locals.startTime = Date.now();
  },
  onBeforeResponse: (event) => {
    // 响应前
    const duration = Date.now() - event.locals.startTime;
    event.response.headers.set("Server-Timing", `total;dur=${duration}`);
  },
});

// 也可以传数组(顺序执行)
export default createMiddleware({
  onRequest: [authMiddleware, loggingMiddleware],
});

FetchEvent 接口

ts
interface FetchEvent {
  request: Request;
  response: { headers: Headers; status: number; statusText: string };
  locals: Record<string, unknown>;
  nativeEvent: H3Event;
}

Session API(vinxi/http

ts
import {
  useSession, getSession, updateSession, clearSession,
  getCookie, setCookie, deleteCookie,
  getHeader, getHeaders,
  appendResponseHeader, setResponseHeader,
  parseCookies,
} from "vinxi/http";

// 在 server function 中使用
async function exampleSession() {
  "use server";

  // Session
  const session = await useSession<{ userId?: number }>({
    password: process.env.SESSION_SECRET!,   // 必须 32+ 字符
    name: "session",
    cookie: {
      httpOnly: true,
      secure: true,
      sameSite: "lax",
      maxAge: 60 * 60 * 24 * 7,
    },
  });
  // session.data / session.update(...) / session.clear()

  // Cookies
  const theme = getCookie("theme");
  setCookie("theme", "dark", { maxAge: 60 * 60 * 24 * 365 });
  deleteCookie("theme");

  // Headers
  const ua = getHeader("user-agent");
  setResponseHeader("X-Custom", "value");
}

@solidjs/meta:SEO 标签

tsx
import { MetaProvider, Title, Meta, Link, Style } from "@solidjs/meta";

// 在根挂载 MetaProvider
<MetaProvider>
  <Title>默认标题</Title>
  <Meta name="description" content="..." />
  <Link rel="canonical" href="https://example.com" />
  <App />
</MetaProvider>
组件用途
<MetaProvider>上下文 provider(根挂载)
<Title>设置 <title>
<Meta>设置 <meta>
<Link>设置 <link>
<Style>设置 <style>

覆盖规则

  • 多个 <Title> 同时挂载时,最后挂载的生效(通常是嵌套最深的子组件)
  • <Meta> / <Link>name / property / rel 等键去重,后挂载覆盖前者

文件约定

路由文件

文件URL含义
src/routes/index.tsx/首页
src/routes/about.tsx/about静态路由
src/routes/blog/index.tsx/blog索引页
src/routes/users/[id].tsx/users/:id动态段
src/routes/users/[id]/posts.tsx/users/:id/posts嵌套
src/routes/files/[...rest].tsx/files/*通配
src/routes/(group)/about.tsx/about路由组(括号不映射)
src/routes/blog.tsx + routes/blog/...-嵌套 layout
src/routes/api/hello.ts/api/helloAPI endpoint
src/routes/[...404].tsx-404 兜底

入口与配置文件

文件用途
src/app.tsx应用根(Router + FileRoutes)
src/entry-server.tsxSSR 入口(createHandler + StartServer)
src/entry-client.tsx客户端 hydration 入口(mount + StartClient)
src/middleware/index.ts全局 middleware(需在 app.config.ts 声明路径)
app.config.tsSolidStart 配置(v1)
vite.config.tsVite 配置(v2) / 可选(v1 时)
tsconfig.jsonTypeScript 配置
package.jsonscripts(dev / build / start)

静态资源

  • public/ 目录的所有文件直接复制到生产输出(/favicon.icohttps://site/favicon.ico
  • src/ 内的资源 import 后经 Vite 处理(hash 命名 / 优化)

app.config.ts 全部选项

ts
import { defineConfig } from "@solidjs/start/config";

export default defineConfig({
  // 应用根目录(默认 src)
  appRoot: "./src",

  // 路由目录(默认 ./routes,相对于 appRoot)
  routeDir: "./routes",

  // 路由文件扩展名
  extensions: ["tsx", "ts", "jsx", "js"],

  // 中间件文件路径(可选)
  middleware: "./src/middleware/index.ts",

  // 是否启用 SSR(默认 true)
  ssr: true,

  // 是否开发覆盖层(默认 true)
  devOverlay: true,

  // Solid 编译选项(vite-plugin-solid)
  solid: {
    // hydratable: true,
    // ssr: true,
    // ...
  },

  // 服务器配置(Nitro)
  server: {
    // 部署预设
    preset: "node-server",

    // 预渲染(SSG)
    prerender: {
      crawlLinks: false,
      routes: ["/", "/about"],
      // failOnError: true,
    },

    // 路由规则
    routeRules: {
      "/api/**": { cors: true },
      "/blog/**": { isr: 60 },
    },

    // 平台特定配置
    rollupConfig: {
      external: ["__STATIC_CONTENT_MANIFEST", "node:async_hooks"],
    },

    // 实验特性
    experimental: {
      asyncContext: true,
    },
  },

  // Vite 配置(透传)
  vite: {
    plugins: [],
    resolve: { alias: { /* ... */ } },
    server: { port: 3000 },
  },

  // 实验特性
  experimental: {
    islands: false, // islands 模式(实验)
  },

  // 序列化模式
  serialization: {
    mode: "js", // "json" | "js"(默认 js,更小但 CSP 弱)
  },
});

serialization.mode

模式优点缺点
js (默认)payload 更小、性能更好需要 unsafe-eval CSP
json更严格的 CSP 兼容payload 略大
ts
defineConfig({
  serialization: { mode: "json" },  // 严格 CSP 站点用
});

Nitro Preset 列表

可用值(server.preset):

Node.js

Preset说明
node-serverExpress-style HTTP server(默认)
node-cluster多进程 Node.js
node-listener仅 listener(嵌入用)

主流 PaaS

Preset平台
vercelVercel Serverless Functions
vercel_edgeVercel Edge Functions
vercel_staticVercel 纯静态
netlifyNetlify Functions
netlify_edgeNetlify Edge Functions
netlify_staticNetlify 纯静态
cloudflare-pagesCloudflare Pages
cloudflare_moduleCloudflare Workers (ESM)
cloudflareCloudflare Workers (Service Worker syntax,旧)

AWS

Preset平台
aws-lambdaAWS Lambda
aws-amplifyAWS Amplify
cloudfrontLambda@Edge

其他

Preset平台
bunBun runtime
deno-serverDeno 自托管
deno-deployDeno Deploy
firebaseFirebase Functions
azureAzure Static Web Apps
digital-oceanDO App Platform
herokuHeroku Dynos

SSG / 静态

Preset说明
static完全静态(生成 .output/public/
github_pagesGitHub Pages 静态
nitro-prerender仅 prerender

完整最新列表见 Nitro Preset 文档

命名约定

类型推荐示例
组件PascalCaseCounter / UserCard
信号camelCase[count, setCount] / [user, setUser]
Queryget / fetch + 名词getPosts / fetchUser
Query keycamelCase 名词"posts" / "user" / "userPosts"
Action动词 + 名词createPost / updateUser / deleteComment
Action key与函数名一致"createPost" / "deleteComment"
Server function动词为主getCurrentUser / sendEmail
Context IDcamelCase + Context 后缀ThemeContext / UserContext
文件名kebab-case 或 PascalCaseuser-card.tsxUserCard.tsx
路由文件kebab-case + 段(小写)users/[id].tsx / (marketing)/about.tsx
API 文件kebab-caseapi/posts/[id].ts
Middlewarekebab-casemiddleware/index.ts

常见 import 来源速查

ts
// === Solid 核心 ===
import {
  // 响应式
  createSignal, createMemo, createEffect, createResource, createComputed,
  // Store
  createStore, produce, unwrap, reconcile,
  // 控制流
  For, Show, Switch, Match, Index, Dynamic, Portal, ErrorBoundary, Suspense, SuspenseList,
  // Context / 生命周期
  createContext, useContext, onMount, onCleanup,
  // 工具
  batch, untrack, lazy, splitProps, mergeProps, children,
} from "solid-js";

// === Solid Web(DOM)===
import { render, hydrate, isServer, getRequestEvent } from "solid-js/web";

// === SolidStart 运行时 ===
import {
  HttpStatusCode,
  HttpHeader,
  clientOnly,
} from "@solidjs/start";

// === SolidStart Server ===
import {
  createHandler,
  StartServer,
} from "@solidjs/start/server";
import type { APIEvent } from "@solidjs/start/server";

// === SolidStart Client ===
import {
  mount,
  StartClient,
} from "@solidjs/start/client";

// === SolidStart Router 组件 ===
import { FileRoutes } from "@solidjs/start/router";

// === SolidStart Middleware ===
import { createMiddleware } from "@solidjs/start/middleware";

// === SolidStart 配置 ===
import { defineConfig } from "@solidjs/start/config";

// === Solid Router ===
import {
  // 路由
  Router, Route, A,
  // Hooks
  useLocation, useNavigate, useParams, useSearchParams, useMatch, useBeforeLeave,
  // 数据流
  query, action, createAsync, useSubmission, useSubmissions, useAction,
  // 工具
  revalidate, reload, redirect, json,
} from "@solidjs/router";
import type {
  RouteDefinition,
  RouteSectionProps,
  Location,
  Navigator,
} from "@solidjs/router";

// === Solid Meta ===
import {
  MetaProvider,
  Title,
  Meta,
  Link,
  Style,
} from "@solidjs/meta";

// === Vinxi HTTP(v1)===
import {
  useSession,
  getCookie, setCookie, deleteCookie, parseCookies,
  getHeader, getHeaders,
  setResponseHeader, appendResponseHeader,
  getRequestURL, getRequestIP,
  readBody, readMultipartFormData,
} from "vinxi/http";

数据加载完整模式速查

模式 1:基础 query + Suspense

tsx
const getX = query(async () => {
  "use server";
  return await db.x.findMany();
}, "x");

export default function Page() {
  const x = createAsync(() => getX());
  return (
    <Suspense fallback={<Loading />}>
      <For each={x()}>{(item) => <Item {...item} />}</For>
    </Suspense>
  );
}

模式 2:preload + ErrorBoundary

tsx
const getX = query(async (id: string) => {
  "use server";
  return await db.x.findUnique({ where: { id } });
}, "x");

export const route = {
  preload: ({ params }) => getX(params.id),
} satisfies RouteDefinition;

export default function Page(props: RouteSectionProps) {
  const x = createAsync(() => getX(props.params.id));
  return (
    <ErrorBoundary fallback={<Error />}>
      <Suspense fallback={<Loading />}>
        <Detail value={x()} />
      </Suspense>
    </ErrorBoundary>
  );
}

模式 3:受保护路由(deferStream)

tsx
const getUser = query(async () => {
  "use server";
  const session = await getSession();
  if (!session.data.userId) throw redirect("/login");
  return await db.user.findUnique({ where: { id: session.data.userId } });
}, "currentUser");

export const route = {
  preload: () => getUser(),
} satisfies RouteDefinition;

export default function Protected() {
  // deferStream 让 redirect 在 streaming 开始前生效
  const user = createAsync(() => getUser(), { deferStream: true });
  return <h1>欢迎,{user()?.name}</h1>;
}

模式 4:action + 自动 revalidate

tsx
const getPosts = query(async () => {
  "use server";
  return await db.posts.findMany();
}, "posts");

const createPost = action(async (formData: FormData) => {
  "use server";
  await db.posts.create({ title: formData.get("title") as string });
  // 自动 revalidate getPosts
}, "createPost");

export const route = {
  preload: () => getPosts(),
} satisfies RouteDefinition;

export default function Page() {
  const posts = createAsync(() => getPosts());
  return (
    <>
      <For each={posts()}>{(p) => <li>{p.title}</li>}</For>
      <form action={createPost} method="post">
        <input name="title" />
        <button>添加</button>
      </form>
    </>
  );
}

模式 5:Optimistic UI(乐观更新)

tsx
const addItem = action(async (formData: FormData) => {
  "use server";
  await db.items.create({ /* ... */ });
}, "addItem");

export default function Page() {
  const items = createAsync(() => getItems());
  const submission = useSubmission(addItem);

  return (
    <>
      <ul>
        <For each={items()}>{(item) => <li>{item.name}</li>}</For>
        <Show when={submission.pending}>
          <li style={{ opacity: 0.5 }}>
            {submission.input?.[0]?.get("name")?.toString()}
          </li>
        </Show>
      </ul>
      <form action={addItem} method="post">
        <input name="name" />
        <button>添加</button>
      </form>
    </>
  );
}

与 Solid 的差异速查

维度裸 Solid(Vite + vite-plugin-solid)SolidStart
路由手动装配 <Router> + <Route><FileRoutes /> 自动
数据加载无内置query + createAsync
副作用无内置action + useSubmission
SSR需要手写 renderToStringcreateHandler + <StartServer>
Server function"use server" 指令
部署 adapterNitro 17+ 预设
Meta 管理需要 @solidjs/meta 自配同(推荐安装)
SessionuseSession from vinxi/http
文件结构自由约定(routes/ / entry-*.tsx

与其他元框架对比

维度SolidStart 1.xNext.js 15Nuxt 4SvelteKit 2Remix 2 / RR v7
UI 库SolidReactVueSvelteReact
文件路由src/routes/app/pages/src/routes/app/routes/
Server functions"use server" 指令Server Actions(同名)defineEventHandlerform actionsloader / action
RSC 概念(整树 client 渲染)有(默认 Server Components)
数据加载query + createAsyncRSC fetch / use()useFetch / useAsyncDataload()loader
表单action + <form>Server Actions + <form>useSubmitform actionsForm
渲染模式CSR / SSR / Streaming / SSGRSC + Streaming + ISRSSR / SSG / SPA / ISRSSR / SSG / SPASSR / SPA
底层 serverNitroNode / EdgeNitroNode / EdgeNode / Edge
Bundle 体积小(~20KB)较大(~80KB+)中(~30KB)较小(~20KB)较大(基于 React)
心智模型细粒度响应式 + 服务器函数RSC 边界 + 重渲染响应式代理 + Composablesrune + signalloader/action

调试与开发工具

工具用途
Solid Devtools 浏览器扩展看响应式图 / signal 依赖 / 组件树
Vite DevServerHMR + 错误覆盖层
Vinxi CLIvinxi dev / vinxi build / vinxi start
Browser DevToolsNetwork 面板看 /_server 端点(server function 调用)
Vitest + vite-plugin-solid单元测试
PlaywrightE2E 测试(与 SolidStart 兼容良好)

学习资源