本指南將幫助您瞭解 TanStack Start 的基本運作原理,無論您如何設定專案。
TanStack Start(目前*)由 Vinxi、Nitro 和 TanStack Router 提供技術支援。
[!注意] Vinxi 將在 1.0.0 版本發布前移除,TanStack 將僅依賴 Vite 和 Nitro。使用 Vinxi 的指令和 API 可能會被 Vite 插件取代。
這個檔案將決定 TanStack Start 內部使用的 TanStack Router 行為。在這裡,您可以配置所有內容,從預設的預載功能到快取過期設定。
// app/router.tsx
import { createRouter as createTanStackRouter } from '@tanstack/solid-router'
import { routeTree } from './routeTree.gen'
export function createRouter() {
const router = createTanStackRouter({
routeTree,
scrollRestoration: true,
})
return router
}
declare module '@tanstack/solid-router' {
interface Register {
router: ReturnType<typeof createRouter>
}
}
// app/router.tsx
import { createRouter as createTanStackRouter } from '@tanstack/solid-router'
import { routeTree } from './routeTree.gen'
export function createRouter() {
const router = createTanStackRouter({
routeTree,
scrollRestoration: true,
})
return router
}
declare module '@tanstack/solid-router' {
interface Register {
router: ReturnType<typeof createRouter>
}
}
routeTree.gen.ts 檔案會在首次執行 TanStack Start(透過 npm run dev 或 npm run start)時生成。此檔案包含生成的路由樹和一些 TypeScript 工具,使 TanStack Start 完全具備型別安全。
雖然 TanStack Start 設計為客戶端優先的 API,但它主要是一個全端框架。這意味著所有使用情境,包括動態和靜態,都依賴伺服器或建置時入口來渲染應用程式的初始 HTML 內容。
這透過 app/ssr.tsx 檔案完成:
// app/ssr.tsx
import {
createStartHandler,
defaultStreamHandler,
} from '@tanstack/solid-start/server'
import { getRouterManifest } from '@tanstack/solid-start/router-manifest'
import { createRouter } from './router'
export default createStartHandler({
createRouter,
getRouterManifest,
})(defaultStreamHandler)
// app/ssr.tsx
import {
createStartHandler,
defaultStreamHandler,
} from '@tanstack/solid-start/server'
import { getRouterManifest } from '@tanstack/solid-start/router-manifest'
import { createRouter } from './router'
export default createStartHandler({
createRouter,
getRouterManifest,
})(defaultStreamHandler)
無論我們是靜態生成應用程式還是動態提供服務,ssr.tsx 檔案都是執行所有 SSR 相關工作的入口點。
將 HTML 傳送到客戶端只是成功的一半。到達後,我們需要在路由解析到客戶端時水合 (hydrate) 客戶端 JavaScript。我們透過使用 StartClient 元件水合應用程式的根來實現這一點:
// app/client.tsx
/// <reference types="vinxi/types/client" />
import { hydrate } from 'solid-js/web'
import { StartClient } from '@tanstack/solid-start'
import { createRouter } from './router'
const router = createRouter()
hydrate(() => <StartClient router={router} />, document)
// app/client.tsx
/// <reference types="vinxi/types/client" />
import { hydrate } from 'solid-js/web'
import { StartClient } from '@tanstack/solid-start'
import { createRouter } from './router'
const router = createRouter()
hydrate(() => <StartClient router={router} />, document)
這使我們能夠在使用者的初始伺服器請求完成後啟動客戶端路由。
除了客戶端入口點外,應用程式的 __root 路由是應用程式的入口點。此檔案中的程式碼將包裹應用程式中的所有其他路由,包括首頁。它的行為類似於整個應用程式的無路徑佈局路由 (pathless layout route)。
由於它總是渲染,因此是構建應用程式外殼 (shell) 和處理任何全域邏輯的理想位置。
// app/routes/__root.tsx
import {
Outlet,
createRootRoute,
HeadContent,
Scripts,
} from '@tanstack/solid-router'
export const Route = createRootRoute({
head: () => ({
meta: [
{
charSet: 'utf-8',
},
{
name: 'viewport',
content: 'width=device-width, initial-scale=1',
},
{
title: 'TanStack Start Starter',
},
],
}),
component: RootComponent,
})
function RootComponent() {
return <Outlet />
}
// app/routes/__root.tsx
import {
Outlet,
createRootRoute,
HeadContent,
Scripts,
} from '@tanstack/solid-router'
export const Route = createRootRoute({
head: () => ({
meta: [
{
charSet: 'utf-8',
},
{
name: 'viewport',
content: 'width=device-width, initial-scale=1',
},
{
title: 'TanStack Start Starter',
},
],
}),
component: RootComponent,
})
function RootComponent() {
return <Outlet />
}
路由是 TanStack Router 的廣泛功能,並在路由指南中有詳細介紹。簡要說明:
// app/routes/index.tsx
import * as fs from 'node:fs'
import { createFileRoute, useRouter } from '@tanstack/solid-router'
import { createServerFn } from '@tanstack/solid-start'
const filePath = 'count.txt'
async function readCount() {
return parseInt(
await fs.promises.readFile(filePath, 'utf-8').catch(() => '0'),
)
}
const getCount = createServerFn({
method: 'GET',
}).handler(() => {
return readCount()
})
const updateCount = createServerFn({ method: 'POST' })
.validator((d: number) => d)
.handler(async ({ data }) => {
const count = await readCount()
await fs.promises.writeFile(filePath, `${count + data}`)
})
export const Route = createFileRoute('/')({
component: Home,
loader: async () => await getCount(),
})
function Home() {
const router = useRouter()
const state = Route.useLoaderData()
return (
<button
type="button"
onClick={() => {
updateCount({ data: 1 }).then(() => {
router.invalidate()
})
}}
>
Add 1 to {state}?
</button>
)
}
// app/routes/index.tsx
import * as fs from 'node:fs'
import { createFileRoute, useRouter } from '@tanstack/solid-router'
import { createServerFn } from '@tanstack/solid-start'
const filePath = 'count.txt'
async function readCount() {
return parseInt(
await fs.promises.readFile(filePath, 'utf-8').catch(() => '0'),
)
}
const getCount = createServerFn({
method: 'GET',
}).handler(() => {
return readCount()
})
const updateCount = createServerFn({ method: 'POST' })
.validator((d: number) => d)
.handler(async ({ data }) => {
const count = await readCount()
await fs.promises.writeFile(filePath, `${count + data}`)
})
export const Route = createFileRoute('/')({
component: Home,
loader: async () => await getCount(),
})
function Home() {
const router = useRouter()
const state = Route.useLoaderData()
return (
<button
type="button"
onClick={() => {
updateCount({ data: 1 }).then(() => {
router.invalidate()
})
}}
>
Add 1 to {state}?
</button>
)
}
TanStack Start 100% 建立在 TanStack Router 之上,因此 TanStack Router 的所有導航功能都可供您使用。簡要說明:
以下是使用 Link 元件導航到新路由的快速範例:
import { Link } from '@tanstack/solid-router'
function Home() {
return <Link to="/about">About</Link>
}
import { Link } from '@tanstack/solid-router'
function Home() {
return <Link to="/about">About</Link>
}
有關導航的更多深入資訊,請查看導航指南。
您可能已經注意到我們使用 createServerFn 建立的伺服器函式。這是 TanStack 最強大的功能之一,允許您建立可以從 SSR 期間的伺服器和客戶端呼叫的伺服器端函式!
以下是伺服器函式運作方式的快速概述:
以下是使用伺服器函式從伺服器獲取並返回資料的快速範例:
import { createServerFn } from '@tanstack/solid-start'
import * as fs from 'node:fs'
import { z } from 'zod'
const getUserById = createServerFn({ method: 'GET' })
// 始終驗證傳送到函式的資料,這裡我們使用 Zod
.validator(z.string())
// 處理函式是執行伺服器端邏輯的地方
.handler(async ({ data }) => {
return db.query.users.findFirst({ where: eq(users.id, data) })
})
// 在應用程式的其他地方
const user = await getUserById({ data: '1' })
import { createServerFn } from '@tanstack/solid-start'
import * as fs from 'node:fs'
import { z } from 'zod'
const getUserById = createServerFn({ method: 'GET' })
// 始終驗證傳送到函式的資料,這裡我們使用 Zod
.validator(z.string())
// 處理函式是執行伺服器端邏輯的地方
.handler(async ({ data }) => {
return db.query.users.findFirst({ where: eq(users.id, data) })
})
// 在應用程式的其他地方
const user = await getUserById({ data: '1' })
要了解更多關於伺服器函式的資訊,請查看伺服器函式指南。
伺服器函式也可用於在伺服器上執行變更。這同樣使用 createServerFn 函式完成,但額外要求您使客戶端上受變更影響的任何資料失效。
以下是使用伺服器函式在伺服器上執行變更並使客戶端資料失效的快速範例:
import { createServerFn } from '@tanstack/solid-start'
const UserSchema = z.object({
id: z.string(),
name: z.string(),
})
const updateUser = createServerFn({ method: 'POST' })
.validator(UserSchema)
.handler(async ({ data }) => {
return db
.update(users)
.set({ name: data.name })
.where(eq(users.id, data.id))
})
// 在應用程式的其他地方
await updateUser({ data: { id: '1', name: 'John' } })
import { createServerFn } from '@tanstack/solid-start'
const UserSchema = z.object({
id: z.string(),
name: z.string(),
})
const updateUser = createServerFn({ method: 'POST' })
.validator(UserSchema)
.handler(async ({ data }) => {
return db
.update(users)
.set({ name: data.name })
.where(eq(users.id, data.id))
})
// 在應用程式的其他地方
await updateUser({ data: { id: '1', name: 'John' } })
要了解更多關於變更的資訊,請查看變更指南。
TanStack Router 的另一個強大功能是資料載入。這允許您為 SSR 獲取資料並在路由渲染前預載路由資料。這透過路由的 loader 函式完成。
以下是資料載入運作方式的快速概述:
要了解更多關於資料載入的資訊,請查看資料載入指南。
Your weekly dose of JavaScript news. Delivered every Monday to over 100,000 devs, for free.