基于 inertia-go + Svelte 的简单示例
基于 inertia-go + Svelte 的简单示例
这个示例实现一个基础的 "欢迎页面 + 计数器" 功能,包含后端 Go 服务(inertia-go)和前端 Svelte 页面,完整流程如下:
一、项目结构
inertia-svelte-demo/
├── go.mod
├── go.sum
├── main.go # Go 后端(inertia-go)
├── frontend/ # Svelte 前端
│ ├── package.json
│ ├── vite.config.js
│ ├── src/
│ │ ├── app.js
│ │ ├── pages/
│ │ │ ├── Welcome.svelte # 欢迎页(含计数器)
│ │ ├── inertia.js # Inertia Svelte 适配器
二、后端实现(Go + inertia-go)
1. 初始化 Go 模块
mkdir inertia-svelte-demo && cd inertia-svelte-demo
go mod init inertia-svelte-demo
go get github.com/petaki/inertia-go
go get github.com/labstack/echo/v4 # 用 Echo 作为 Web 框架(inertia-go 兼容任意框架)
2. main.go 代码
package main
import (
"embed"
"strconv"
"github.com/labstack/echo/v4"
"github.com/petaki/inertia-go"
)
//go:embed frontend/dist
var templates embed.FS
func main() {
// 初始化 Echo
e := echo.New()
// 配置 Inertia
assetVersion := "1.0.0" // 前端资源版本(更新时触发重新加载)
rootTemplate := "frontend/dist/index.html" // Inertia Go 模板(使用 Go template 语法)
assetsPath := "./frontend/dist" // 前端静态资源目录
// 创建 Inertia 实例(适配 Echo 框架)
// New(url, rootTemplate, version string, templateFS ...fs.FS)
inertiaManager := inertia.New("http://localhost:3000", rootTemplate, assetVersion, templates)
// 静态文件服务(提供前端构建后的 JS/CSS)
e.Static("/assets", assetsPath+"/assets")
// 测试端点
e.GET("/test", func(c echo.Context) error {
return c.String(200, "Hello from test endpoint!")
})
// 核心:Inertia 处理器(处理前端页面渲染 + 数据传输)
e.GET("/", func(c echo.Context) error {
// 从请求参数获取 count(默认 0)
countStr := c.QueryParam("count")
count, _ := strconv.Atoi(countStr)
if count < 0 {
count = 0
}
// 向前端传递数据(Inertia 共享数据)
err := inertiaManager.Render(c.Response(), c.Request(), "Welcome", map[string]any{
"message": "Hello Inertia + Svelte!",
"count": count,
})
if err != nil {
c.Logger().Error("Inertia Render error:", err)
}
return err
})
// 启动服务
e.Logger.Fatal(e.Start(":3000"))
}
三、前端实现(Svelte + Inertia)
1. 初始化 Svelte 项目
mkdir -p frontend && cd frontend
npm init vite@latest . -- --template svelte
npm install @inertiajs/svelte @inertiajs/core
2. 配置 Vite(vite.config.js)
确保构建路径匹配后端的 assetsPath:
import path from "node:path";
import { svelte } from "@sveltejs/vite-plugin-svelte";
import { defineConfig } from "vite";
export default defineConfig({
plugins: [svelte()],
build: {
outDir: "./dist",
assetsDir: "assets",
manifest: true,
},
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
},
},
server: {
proxy: {
"/": {
target: "http://localhost:3000",
changeOrigin: true,
},
},
},
});
3. Inertia 入口(src/inertia.js)
import "./app.css";
import { createInertiaApp } from "@inertiajs/svelte";
import { mount } from "svelte";
// 使用Vite的import.meta.glob动态导入所有页面组件
const pages = import.meta.glob("./pages/**/*.svelte");
createInertiaApp({
resolve: async (name) => {
const pagePath = `./pages/${name}.svelte`;
if (!(pagePath in pages)) {
console.error(
`Page not found: ${name}. Available pages:`,
Object.keys(pages).map((p) =>
p.replace("./pages/", "").replace(".svelte", ""),
),
);
throw new Error(`Page not found: ${name}`);
}
const page = await pages[pagePath]();
return page;
},
setup({ el, App, props }) {
mount(App, { target: el, props });
},
});
5. Svelte 页面(src/pages/Welcome.svelte)
实现计数器功能,通过 Inertia 与后端交互:
<script>
import { router } from "@inertiajs/svelte";
// 接收后端传递的 props
export let message;
export let count;
// 计数器增加
function increment() {
// 通过 Inertia 导航(不刷新页面)
router.visit(`/?count=${count + 1}`, {
preserveState: false,
});
}
// 计数器减少
function decrement() {
router.visit(`/?count=${Math.max(0, count - 1)}`, {
preserveState: false,
});
}
</script>
<main class="container">
<h1>{message}</h1>
<div class="counter">
<button on:click={decrement}>-</button>
<span>Count: {count}</span>
<button on:click={increment}>+</button>
</div>
</main>
<style>
.container {
max-width: 800px;
margin: 50px auto;
text-align: center;
font-family: Arial, sans-serif;
}
.counter {
margin-top: 30px;
display: flex;
align-items: center;
justify-content: center;
gap: 20px;
font-size: 24px;
}
button {
padding: 10px 20px;
font-size: 24px;
cursor: pointer;
border: none;
border-radius: 5px;
background: #007bff;
color: white;
}
button:hover {
background: #0056b3;
}
</style>
6. 调整 index.html(根目录)
修改前端根模板,适配 Inertia 渲染:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/assets/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Inertia + Svelte + Go</title>
</head>
<body>
<!-- Inertia 挂载点 -->
<div id="app" data-page="{{ .page | marshal }}"></div>
<!-- 前端构建后的 JS -->
<script type="module" src="/src/main.js"></script>
</body>
</html>
四、运行项目
1. 构建前端
cd frontend
npm run build # 生成 dist 目录(后端会读取此目录)
2. 启动后端
cd .. # 回到项目根目录
go run main.go
3. 访问页面
打开浏览器访问 http://localhost:3000,即可看到:
- 欢迎语(来自后端传递的
message) - 计数器(点击 +/- 按钮,通过 Inertia 无刷新更新 count,数据由后端处理)
核心说明
-
Inertia 核心逻辑:
- 后端通过
inertiaManager.Render传递页面名称(Welcome)和 props(message/count) - 前端通过
createInertiaApp解析页面组件,并接收后端 props - 前端导航使用
router.visit实现无刷新页面跳转,替代传统的页面刷新
- 后端通过
-
开发优化:
- 前端开发时可运行
npm run dev,通过 Vite 代理请求到后端,实现热更新 - 后端修改后重启
go run main.go即可
- 前端开发时可运行
-
扩展方向:
- 添加更多页面(如
About.svelte),后端新增路由并渲染对应页面 - 使用 Inertia 共享数据(
inertiaManager.Share)实现全局 props - 集成表单提交(
router.post/router.put)实现数据提交
- 添加更多页面(如
注意:
前端的@inertiajs/core、@inertiajs/progress、@inertiajs/svelte版本不能太高,否则总是运行失败!我调试了大半天才找出问题所在。
{
"name": "frontend",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"devDependencies": {
"@sveltejs/vite-plugin-svelte": "^7.0.0",
"@tailwindcss/vite": "4.2.2",
"svelte": "^5.55.3",
"tailwindcss": "4.2.2",
"vite": "^8.0.8"
},
"dependencies": {
"@inertiajs/core": "^1.0.13",
"@inertiajs/progress": "^0.2.7",
"@inertiajs/svelte": "^1.0.9"
},
"packageManager": "pnpm@10.33.0+sha512.10568bb4a6afb58c9eb3630da90cc9516417abebd3fabbe6739f0ae795728da1491e9db5a544c76ad8eb7570f5c4bb3d6c637b2cb41bfdcdb47fa823c8649319"
}

浙公网安备 33010602011771号