mcpField
I
inertia-coder

inertia-coder

github.com/majesticlabs-dev开发5048 次安装7 周前更新
代码仓库

关于 inertia-coder

inertia-coder 是用于开发 Inertia.js 单页应用的技能,搭配 Rails 后端以及 React、Vue 或 Svelte。内容涵盖安装、页面组件、表单、共享属性和文件上传,并沿用服务端路由。

主要功能

  1. 01Rails 与 Inertia.js 安装配置
  2. 02React、Vue 和 Svelte 页面模式
  3. 03使用 useForm 处理表单
  4. 04共享属性与 flash 消息
  5. 05带进度跟踪的文件上传

适用场景

  1. 01搭建 Inertia.js Rails 应用
  2. 02使用服务端提供的属性构建页面
  3. 03处理表单和文件上传

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/majesticlabs-dev/majestic-marketplace --skill inertia-coder

安装后在 Claude Code 中输入/inertia-coder确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
react-patterns.md
svelte-patterns.md
vue-patterns.md

详细介绍

Inertia.js + Rails

使用 Inertia.js、React/Vue/Svelte 和 Rails 后端构建现代单页应用。

何时使用此技能

  • 在 Rails 中设置 Inertia.js
  • 创建 Inertia 页面组件
  • 使用 useForm hook 处理表单
  • 管理共享 props 和闪存消息
  • 无需复杂 API 即可进行客户端路由
  • 跟踪文件上传进度

什么是 Inertia.js?

Inertia.js 允许你使用经典的服务端路由和控制器构建 SPA。

方案优点缺点
传统 Rails 视图简单,由服务器渲染交互性有限
Rails API + React SPA完整的 SPA 体验路由重复,状态复杂
Inertia.jsSPA + 服务端路由两全其美

快速设置

# Gemfile
gem 'inertia_rails'
gem 'vite_rails'
bundle install
rails inertia:install  # Choose: React, Vue, or Svelte
# config/initializers/inertia_rails.rb
InertiaRails.configure do |config|
  config.version = ViteRuby.digest

  config.share do |controller|
    {
      auth: {
        user: controller.current_user&.as_json(only: [:id, :name, :email])
      },
      flash: controller.flash.to_hash
    }
  end
end

控制器模式

class ArticlesController < ApplicationController
  def index
    articles = Article.published.order(created_at: :desc)

    render inertia: 'Articles/Index', props: {
      articles: articles.as_json(only: [:id, :title, :excerpt])
    }
  end

  def create
    @article = Article.new(article_params)

    if @article.save
      redirect_to article_path(@article), notice: 'Article created'
    else
      redirect_to new_article_path, inertia: { errors: @article.errors }
    end
  end
end

React 页面组件

// app/frontend/pages/Articles/Index.jsx
import { Link } from '@inertiajs/react'

export default function Index({ articles }) {
  return (
    <div>
      <h1>Articles</h1>
      {articles.map(article => (
        <Link key={article.id} href={`/articles/${article.id}`}>
          <h2>{article.title}</h2>
        </Link>
      ))}
    </div>
  )
}

使用 useForm 处理表单

import { useForm } from '@inertiajs/react'

export default function New() {
  const { data, setData, post, processing, errors } = useForm({
    title: '',
    body: ''
  })

  function handleSubmit(e) {
    e.preventDefault()
    post('/articles')
  }

  return (
    <form onSubmit={handleSubmit}>
      <input
        value={data.title}
        onChange={e => setData('title', e.target.value)}
      />
      {errors.title && <div className="error">{errors.title}</div>}

      <textarea
        value={data.body}
        onChange={e => setData('body', e.target.value)}
      />
      {errors.body && <div className="error">{errors.body}</div>}

      <button disabled={processing}>
        {processing ? 'Creating...' : 'Create'}
      </button>
    </form>
  )
}

共享布局

// app/frontend/layouts/AppLayout.jsx
import { Link, usePage } from '@inertiajs/react'

export default function AppLayout({ children }) {
  const { auth, flash } = usePage().props

  return (
    <div>
      <nav>
        <Link href="/">Home</Link>
        {auth.user ? (
          <Link href="/logout" method="delete">Logout</Link>
        ) : (
          <Link href="/login">Login</Link>
        )}
      </nav>

      {flash.success && <div className="alert-success">{flash.success}</div>}

      <main>{children}</main>
    </div>
  )
}

// Assign layout to page
Index.layout = page => <AppLayout>{page}</AppLayout>

文件上传

import { useForm } from '@inertiajs/react'

const { data, setData, post, progress } = useForm({
  avatar: null
})

<input
  type="file"
  onChange={e => setData('avatar', e.target.files[0])}
/>

{progress && <progress value={progress.percentage} max="100" />}

<button onClick={() => post('/profile/avatar', { forceFormData: true })}>
  Upload
</button>

最佳实践

应该做
  • 使用 <Link> 而不是 <a> 标签
  • 通过 config 共享常用数据(auth、flash)
  • 在服务器端进行验证——客户端仅用于提升用户体验
  • 使用 processing 显示加载状态
  • 使用布局保持导航一致
不应该做
  • 不要使用 window.location——这会破坏 SPA
  • 不要创建 REST API——Inertia 不需要它们
  • 不要在客户端获取数据——服务器会提供 props
  • 不要绕过 Inertia 路由器——这会破坏行为

详细参考

有关特定框架的模式:

  • references/react-patterns.md - React hooks、TypeScript 和高级模式
  • references/vue-patterns.md - Vue 3 Composition API 模式
  • references/svelte-patterns.md - Svelte stores 和响应式模式

评论

支持 Markdown