关于 inertia-rails-ssr
这是一份使用 Vite Ruby 和 Node.js 为 Inertia Rails 页面配置服务端渲染的指南,涵盖 Vite 插件与手动配置、生产部署及故障排查。
主要功能
- 01通过 Vite 插件或手动配置 SSR
- 02设置 React、Vue 3 和 Svelte 入口
- 03在开发与生产环境构建和运行 SSR
- 04配置缓存、集群和条件渲染
- 05管理标题与元标签
- 06排查水合与服务端错误
适用场景
- 01在服务器端渲染 Rails 公共页面
- 02准备 SEO 和社交分享元数据
- 03部署和管理 SSR 进程
- 04诊断水合不匹配或 SSR 故障
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ cole-robertson/ inertia-rails-skills --skill inertia-rails-ssr安装后在 Claude Code 中输入/inertia-rails-ssr确认已加载。
详细介绍
Inertia Rails 服务端渲染(SSR)
服务端渲染会在服务器上预渲染 JavaScript 页面,并向访客提供完整渲染后的 HTML。这有助于提升 SEO、加快页面初次加载,并让用户即使在禁用 JavaScript 时也能进行基本导航。
何时使用 SSR
- SEO 至关重要的页面 - 着陆页、博客文章、产品页面
- 社交分享 - 需要正确 Open Graph 元标签的页面
- 网络连接缓慢 - 用户可在 JavaScript 加载前看到内容
- 无障碍访问 - 无需 JavaScript 即可使用基本功能
要求
- 服务器上必须能够使用 Node.js
- 用于构建配置的 Vite Ruby
@inertiajs/vite插件(v3 推荐)
推荐:Vite 插件配置(v3)
@inertiajs/vite 插件可大幅简化 SSR。在开发环境中,SSR 会自动通过 Vite 开发服务器运行,无需单独的 Node.js 进程。
1. 安装 Vite 插件
npm install @inertiajs/vite
2. 配置 Vite
更新 vite.config.js:
React:
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import inertia from '@inertiajs/vite'
export default defineConfig({
plugins: [
inertia(),
react(),
],
})
Vue 3:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import inertia from '@inertiajs/vite'
export default defineConfig({
plugins: [
inertia(),
vue(),
],
})
Svelte:
import { defineConfig } from 'vite'
import { svelte } from '@sveltejs/vite-plugin-svelte'
import inertia from '@inertiajs/vite'
export default defineConfig({
plugins: [
inertia(),
svelte(),
],
})
3. 更新入口点以支持激活
启用 SSR 后,客户端入口点应使用激活,而不是完整渲染。
React:
import { createInertiaApp } from '@inertiajs/react'
import { hydrateRoot } from 'react-dom/client'
createInertiaApp({
resolve: (name) => {
const pages = import.meta.glob('../pages/**/*.tsx', { eager: true })
return pages[`../pages/${name}.tsx`]
},
setup({ el, App, props }) {
hydrateRoot(el, <App {...props} />)
},
})
Vue 3:
import { createInertiaApp } from '@inertiajs/vue3'
import { createSSRApp, h } from 'vue'
createInertiaApp({
resolve: (name) => {
const pages = import.meta.glob('../pages/**/*.vue', { eager: true })
return pages[`../pages/${name}.vue`]
},
setup({ el, App, props, plugin }) {
createSSRApp({
render: () => h(App, props),
})
.use(plugin)
.mount(el)
},
})
4. 在 Rails 中启用 SSR
# config/initializers/inertia_rails.rb
InertiaRails.configure do |config|
config.ssr_enabled = true
# In development, SSR is handled by the Vite dev server automatically.
# Set ssr_url to nil to auto-detect from Vite.
config.ssr_url = nil
# Optional: only enable SSR when the bundle exists (production)
# config.ssr_bundle = Rails.root.join('public/vite-ssr/ssr.js')
# Optional: cache SSR responses
# config.ssr_cache = true
# config.ssr_cache = { expires_in: 1.hour }
# Optional: handle SSR errors gracefully
config.ssr_raise_on_error = !Rails.env.production?
config.on_ssr_error = ->(error, page) {
Rails.logger.error "SSR error for #{page[:component]}: #{error.message}"
}
end
5. 启动开发环境
# Terminal 1: Rails server
bin/rails server
# Terminal 2: Vite dev server (handles SSR automatically)
bin/vite dev
就这么简单!开发环境中无需单独的 SSR 服务进程。
手动配置 SSR(不使用 Vite 插件)
如果无法使用 Vite 插件,可以通过单独的入口点手动配置 SSR。
1. 创建 SSR 入口点
创建 app/frontend/ssr/ssr.js:
Vue 3:
import { createInertiaApp } from '@inertiajs/vue3'
import createServer from '@inertiajs/vue3/server'
import { renderToString } from '@vue/server-renderer'
import { createSSRApp, h } from 'vue'
const pages = import.meta.glob('../pages/**/*.vue', { eager: true })
createServer((page) =>
createInertiaApp({
page,
render: renderToString,
resolve: (name) => {
const page = pages[`../pages/${name}.vue`]
if (!page) throw new Error(`Page not found: ${name}`)
return page
},
setup({ App, props, plugin }) {
return createSSRApp({
render: () => h(App, props),
}).use(plugin)
},
})
)
React:
import { createInertiaApp } from '@inertiajs/react'
import createServer from '@inertiajs/react/server'
import ReactDOMServer from 'react-dom/server'
const pages = import.meta.glob('../pages/**/*.jsx', { eager: true })
createServer((page) =>
createInertiaApp({
page,
render: ReactDOMServer.renderToString,
resolve: (name) => {
const page = pages[`../pages/${name}.jsx`]
if (!page) throw new Error(`Page not found: ${name}`)
return page
},
setup: ({ App, props }) => <App {...props} />,
})
)
2. 配置 Vite Ruby
更新 config/vite.json:
{
"all": {
"sourceCodeDir": "app/frontend",
"entrypointsDir": "entrypoints"
},
"development": {
"autoBuild": true
},
"production": {
"ssrBuildEnabled": true
}
}
3. 在 Rails 中启用 SSR
InertiaRails.configure do |config|
config.ssr_enabled = ViteRuby.config.ssr_build_enabled
config.ssr_url = 'http://localhost:13714'
end
4. 构建并运行
bin/vite build
bin/vite build --ssr
bin/vite ssr
生产环境部署
在生产环境中,仍需构建 SSR 包并运行 Node.js 服务(即使使用了 Vite 插件)。
为生产环境构建
bin/vite build
bin/vite build --ssr
进程管理器(systemd)
创建 /etc/systemd/system/inertia-ssr.service:
[Unit]
Description=Inertia SSR Server
After=network.target
[Service]
Type=simple
User=deploy
WorkingDirectory=/var/www/myapp/current
ExecStart=/usr/bin/node public/vite-ssr/ssr.js
Restart=on-failure
Environment=NODE_ENV=production
[Install]
WantedBy=multi-user.target
sudo systemctl enable inertia-ssr
sudo systemctl start inertia-ssr
进程管理器(PM2)
pm2 start public/vite-ssr/ssr.js --name inertia-ssr
pm2 save
Docker
CMD ["sh", "-c", "node public/vite-ssr/ssr.js & bundle exec puma"]
高级配置
SSR 响应缓存
缓存 SSR 响应以降低 Node.js 负载:
InertiaRails.configure do |config|
# Simple boolean — uses default Rails cache store
config.ssr_cache = true
# Or with options
config.ssr_cache = { expires_in: 1.hour }
end
SSR 包检测
仅当包存在时才尝试 SSR:
InertiaRails.configure do |config|
config.ssr_bundle = Rails.root.join('public/vite-ssr/ssr.js')
# Can also be an array of paths to check
# config.ssr_bundle = [
# Rails.root.join('public/vite-ssr/ssr.js'),
# Rails.root.join('public/vite-ssr/ssr.mjs'),
# ]
end
集群
在多核系统上提升性能:
// ssr/ssr.js
createServer(
(page) => createInertiaApp({ /* ... */ }),
{ cluster: true }
)
自定义端口
createServer(
(page) => createInertiaApp({ /* ... */ }),
{ port: 13715 }
)
条件式 SSR
仅对特定路由启用 SSR:
class ApplicationController < ActionController::Base
# Disable SSR for admin pages
inertia_config(ssr_enabled: false)
end
class PagesController < ApplicationController
# Enable SSR for public pages
inertia_config(ssr_enabled: Rails.env.production?)
end
# Per-action with a lambda
class PostsController < ApplicationController
inertia_config(ssr_enabled: ->(request) {
request.path.start_with?('/blog')
})
end
标题和元标签
服务端管理的元标签(v3)
使用 inertia_meta 辅助方法或 meta: 渲染选项,从控制器设置元标签:
class PostsController < ApplicationController
def show
post = Post.find(params[:id])
render inertia: {
post: post.as_json(only: [:id, :title, :content])
}, meta: {
title: post.title,
description: post.excerpt,
og_image: post.cover_image_url
}
end
end
使用 Head 组件
Vue 3:
<script setup>
import { Head } from '@inertiajs/vue3'
defineProps(['post'])
</script>
<template>
<Head>
<title>{{ post.title }}</title>
<meta name="description" :content="post.excerpt" />
<meta property="og:title" :content="post.title" />
<meta property="og:image" :content="post.coverImageUrl" />
</Head>
<article>
<h1>{{ post.title }}</h1>
</article>
</template>
React:
import { Head } from '@inertiajs/react'
export default function Show({ post }) {
return (
<>
<Head>
<title>{post.title}</title>
<meta name="description" content={post.excerpt} />
<meta property="og:title" content={post.title} />
</Head>
<article>
<h1>{post.title}</h1>
</article>
</>
)
}
默认标题模板
createInertiaApp({
title: (title) => title ? `${title} - My App` : 'My App',
// ...
})
故障排除
SSR 服务无响应
- 检查 SSR 服务是否正在运行:
curl http://localhost:13714 - 查看日志:
journalctl -u inertia-ssr -f - 确认端口与 Rails 配置一致
- 如果在开发环境中使用 Vite 插件,请确保 Vite 开发服务器正在运行
激活不匹配错误
- 确保客户端和服务端渲染的内容相同
- 避免在初次渲染时使用仅限浏览器的 API(使用
onMounted/useEffect) - 检查日期/时间格式是否存在差异
// Bad - different on server vs client
const time = new Date().toLocaleTimeString()
// Good - render after mount
const time = ref(null)
onMounted(() => {
time.value = new Date().toLocaleTimeString()
})
内存泄漏
- 避免在 SSR 中修改全局状态
- 仅使用请求作用域的状态
- 监控 Node.js 内存使用情况
SSR 静默失败
配置错误处理,以捕获并记录 SSR 故障:
InertiaRails.configure do |config|
config.ssr_raise_on_error = !Rails.env.production?
config.on_ssr_error = ->(error, page) {
Rails.logger.error "SSR error: #{error.message}"
Sentry.capture_exception(error) if defined?(Sentry)
}
end
性能提示
- 保持 SSR 轻量 - 将繁重的计算延后到客户端
- 使用缓存 - 为静态/半静态页面启用
ssr_cache - 使用集群 - 在生产环境中启用多工作进程模式
- 监控延迟 - 跟踪 SSR 渲染耗时
- 检测包 - 使用
ssr_bundle,以便在必要时平稳回退到 CSR
# Log slow SSR renders
around_action :track_ssr_time, if: -> { request.inertia? }
def track_ssr_time
start = Time.current
yield
duration = Time.current - start
Rails.logger.info "SSR render: #{duration.round(3)}s" if duration > 0.1
end
评论
支持 Markdown