You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Inertia+Vue3中构建自带数据的独立可复用组件?

问题:构建独立可复用的用户统计组件(Inertia + Vue3)

需求

  • 开发可复用组件,展示指定时间范围的用户总数
  • 支持按年份筛选并重载数据
  • 组件完全独立,自带数据获取逻辑(不依赖父组件传递props)

现有实现代码

父视图

<script setup>
import MyView from '@/Pages/MyView.vue'
</script>
<template>
    <MyView />
</template>

子组件(MyView)

<script setup>
import {ref, onMounted} from 'vue'
import {router} from '@inertiajs/vue3'

const count = ref(0)

const getData = (year) => {
    router.get(
        route('view-data'),
        {date: `${year}-01-01`},
        {
            preserveState: true,
            onSuccess: (data) => {
                count.value = data.props.count
            },
            onError: (error) => {
                console.log({error})
            },
        }
    )
}

onMounted(() => {
    getData(2022)
})
</script>

<template>
    {{count}}<br>
    <button @click="getData(2023)">2023</button>
</template>

路由

Router::get('view-data', ViewDataController::class)->name('view-data');

控制器

class ViewDataController extends Controller
{
    public function __invoke(Request $request)
    {
        return inertia()->render('MyView', [
            'count' => (int) User::whereDate('created_at', '>', $request->input('date'))->count(),
        ]);
        /* return response()->json([
            'count' => (int) User::whereDate('created_at', '>', $request->input('date'))->count(),
        ]);*/
    }
}

遇到的问题

  • 返回JSON响应时,页面被渲染成包含计数的iframe,不符合Inertia的访问逻辑
  • 纠结是否使用API路由+Axios请求,因为曾看到“使用Inertia无需API路由”的说法
  • 当前用Inertia页面响应的方式可运行,但组件会被渲染为完整页面,无法作为父组件内的子组件嵌入

解决方案与疑问解答

正确实现方式

根据组件的使用场景,有两种推荐方案:

方案1:API路由 + Axios(适合完全独立的可复用组件)

这种方式让组件自主发起数据请求,不依赖父组件或Inertia页面响应,是独立组件的最优解。

  1. 修改路由为API路由(放到routes/api.php中):
Route::get('user-count', UserCountController::class)->name('user-count');
  1. 创建专用控制器返回JSON:
class UserCountController extends Controller
{
    public function __invoke(Request $request)
    {
        $count = User::whereDate('created_at', '>', $request->input('date'))->count();
        return response()->json(['count' => (int)$count]);
    }
}
  1. 子组件改用Axios请求数据:
<script setup>
import { ref, onMounted } from 'vue'
import axios from 'axios'

const count = ref(0)

const getData = async (year) => {
    try {
        const response = await axios.get(route('user-count'), {
            params: { date: `${year}-01-01` }
        })
        count.value = response.data.count
    } catch (error) {
        console.error(error)
    }
}

onMounted(() => {
    getData(2022)
})
</script>

<template>
    {{ count }}<br>
    <button @click="getData(2023)">2023</button>
</template>

方案2:父组件传props(适合与页面数据联动的组件)

如果组件是页面的一部分,需要和页面其他数据交互,推荐遵循Inertia的页面驱动理念,由父页面获取数据后传递给子组件:

  1. 父页面处理数据获取:
<script setup>
import { ref, onMounted } from 'vue'
import { router } from '@inertiajs/vue3'
import UserCount from '@/Components/UserCount.vue'

const count = ref(0)

const getData = (year) => {
    router.get(route('view-data'), { date: `${year}-01-01` }, {
        preserveState: true,
        onSuccess: (data) => {
            count.value = data.props.count
        }
    })
}

onMounted(() => {
    getData(2022)
})
</script>

<template>
    <UserCount :count="count" @update="getData" />
</template>
  1. 子组件接收props并触发更新事件:
<script setup>
defineProps(['count'])
const emit = defineEmits(['update'])
</script>

<template>
    {{ count }}<br>
    <button @click="emit('update', 2023)">2023</button>
</template>

关键疑问解答

  • 是否推荐父组件传props?
    取决于组件的定位:如果组件需要和所在页面的其他数据联动,推荐用父组件传props,符合Inertia的页面驱动模式;如果组件需要完全独立(可在任意页面嵌入,无需页面数据支持),则API+Axios的方式更合适。

  • Inertia项目里可以用API路由吗?
    完全可以!“使用Inertia无需API路由”指的是页面跳转和页面级渲染不需要依赖API,而是通过Inertia的页面响应完成,但组件级的独立数据请求用API+Axios是常规且合理的做法,两者没有冲突。

  • 返回JSON出现iframe的原因:
    你直接在浏览器地址栏访问了API路由的URL,浏览器会把JSON内容渲染成页面;而通过Axios发起XHR请求时,会正常接收JSON数据,不会出现iframe问题。


内容的提问来源于stack exchange,提问作者DirkReuter

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 00:04:55