如何在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页面响应,是独立组件的最优解。
- 修改路由为API路由(放到
routes/api.php中):
Route::get('user-count', UserCountController::class)->name('user-count');
- 创建专用控制器返回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]); } }
- 子组件改用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的页面驱动理念,由父页面获取数据后传递给子组件:
- 父页面处理数据获取:
<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>
- 子组件接收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
相关产品推荐
相关产品推荐

