Nuxt/Vue计算属性报`slice` is not a function错误求助
Nuxt/Vue 用户列表分页过滤报错问题解决
问题描述
开发Nuxt/Vue应用时,从后端获取用户列表可正常展示,但实现分页与过滤功能时,出现filteredList.value的slice或length不是函数的错误。即使调试时看到计算属性是数组,错误仍存在。简化版组件代码如下:
<script setup lang="ts"> interface UserDTO { id: Number, name: String, email: String, roles: String, created_at: String, updated_at: String } // const data = shallowRef<UserDTO[]>([ // { // "id": 1, // "email": "admin@example.com", // "name": "Admin", // "roles": "admin", // "created_at": "2024-11-01T04:44:41.000Z", // "updated_at": "2024-11-01T04:44:41.251Z" // } // ]) const { data } = await useFetch<UserDTO[]>('/api/users') const search = ref('') const page = ref(1) const itemsPerPage = ref(10) const filteredList = computed(() => { const users = data.value if (!users) { return [] } const query = search.value.toLowerCase() if (query.trim() === "") { return data } return users.filter((user) => { return ( user.name.toLowerCase().includes(query) || user.email.toLowerCase().includes(query) || user.roles.toLowerCase().includes(query) ) }) }) const displayList = computed(() => { return filteredList.value.slice(startIndex.value, endIndex.value) }) const displayStartIndex = computed(() => { if (filteredList.value.length === 0) { return 0 } return startIndex.value + 1 }) const startIndex = computed(() => { return (page.value - 1) * itemsPerPage.value }) const endIndex = computed(() => { return Math.min( filteredList.value.length, startIndex.value + itemsPerPage.value ) }) const pageTotal = computed(() => { return Math.max( 1, Math.ceil(filteredList.value.length / itemsPerPage.value) ) }) </script> <template> <div> <input type="text" class="form-control" placeholder="Search…" v-model="search" autocomplete="off" /> <select class="form-select" v-model="itemsPerPage"> <option>10</option> <option>20</option> <option>50</option> <option>100</option> </select> entries <table> <tbody> <tr v-for="(user, index) in displayList" :key="index"> <td> {{ user.name }} </td> <td class="text-secondary"> {{ user.email }} </td> <td class="text-secondary"> {{ user.roles }} </td> <td class="text-secondary"> {{ user.created_at }} </td> <td class="text-secondary"> {{ user.updated_at }} </td> </tr> <tr v-if="filteredList.length === 0"> <td colspan="5"> No users to show </td> </tr> </tbody> </table> <p> Showing {{ displayStartIndex }} to {{ endIndex }} of {{ filteredList.length }} entries </p> </div> </template>
问题原因
- 核心错误:
filteredList计算属性中,当搜索框为空时返回的是data(NuxtuseFetch返回的Ref响应式对象),而非实际的数组data.value。后续依赖filteredList的计算属性调用数组方法时,实际操作的是Ref对象,自然会报错。 - 次要问题:
UserDTO接口中使用了大写的Number/String(构造函数类型),正确的TypeScript基本类型应该是小写的number/string。
修复方案
1. 修正filteredList的返回值
将搜索为空时的返回值从data改为users(即data.value),确保始终返回数组类型:
const filteredList = computed(() => { const users = data.value if (!users) { return [] } const query = search.value.toLowerCase() if (query.trim() === "") { return users // 替换原返回的data(Ref对象)为实际数组 } return users.filter((user) => { return ( user.name.toLowerCase().includes(query) || user.email.toLowerCase().includes(query) || user.roles.toLowerCase().includes(query) ) }) })
2. 修正接口类型定义
把接口中的大写类型改为小写基本类型:
interface UserDTO { id: number, name: string, email: string, roles: string, created_at: string, updated_at: string }
完整修复后的代码
<script setup lang="ts"> interface UserDTO { id: number, name: string, email: string, roles: string, created_at: string, updated_at: string } const { data } = await useFetch<UserDTO[]>('/api/users') const search = ref('') const page = ref(1) const itemsPerPage = ref(10) const filteredList = computed(() => { const users = data.value if (!users) { return [] } const query = search.value.toLowerCase() if (query.trim() === "") { return users } return users.filter((user) => { return ( user.name.toLowerCase().includes(query) || user.email.toLowerCase().includes(query) || user.roles.toLowerCase().includes(query) ) }) }) const displayList = computed(() => { return filteredList.value.slice(startIndex.value, endIndex.value) }) const displayStartIndex = computed(() => { if (filteredList.value.length === 0) { return 0 } return startIndex.value + 1 }) const startIndex = computed(() => { return (page.value - 1) * itemsPerPage.value }) const endIndex = computed(() => { return Math.min( filteredList.value.length, startIndex.value + itemsPerPage.value ) }) const pageTotal = computed(() => { return Math.max( 1, Math.ceil(filteredList.value.length / itemsPerPage.value) ) }) </script> <template> <div> <input type="text" class="form-control" placeholder="Search…" v-model="search" autocomplete="off" /> <select class="form-select" v-model="itemsPerPage"> <option>10</option> <option>20</option> <option>50</option> <option>100</option> </select> entries <table> <tbody> <tr v-for="(user, index) in displayList" :key="index"> <td> {{ user.name }} </td> <td class="text-secondary"> {{ user.email }} </td> <td class="text-secondary"> {{ user.roles }} </td> <td class="text-secondary"> {{ user.created_at }} </td> <td class="text-secondary"> {{ user.updated_at }} </td> </tr> <tr v-if="filteredList.length === 0"> <td colspan="5"> No users to show </td> </tr> </tbody> </table> <p> Showing {{ displayStartIndex }} to {{ endIndex }} of {{ filteredList.length }} entries </p> </div> </template>
补充说明
调试时看似filteredList是数组,可能是因为Vue DevTools会自动展开Ref对象的value属性,导致你误以为返回的是数组,但实际代码中操作的是未解包的Ref对象,从而引发错误。
内容的提问来源于stack exchange,提问作者Yonic Surny
相关产品推荐
相关产品推荐

