Mongoose获取的MongoDB数组调用.sort()报错问题排查
问题原因与解决办法
这个问题我之前排查过好几次,核心原因很明确:你通过Mongoose+GraphQL拿到的数组,不是原生JavaScript数组,而是被封装/处理成了只读(不可变)的数组对象,而sort()是原地排序方法,会直接修改原数组的元素位置(本质是修改数组的索引属性,比如把索引1的元素赋值到索引0的位置),这就触发了只读属性的修改限制,所以报错。而硬编码的数组是原生可写数组,自然能正常执行sort()。
具体原因拆解
- Mongoose的封装特性:Mongoose为了实现ODM的变更追踪、字段验证等功能,会把文档中的数组包装成
DocumentArray实例——它虽然看起来像数组,但本质是带自定义逻辑的类数组对象,某些属性是只读的;如果后端没有把它转成纯JS对象就返回给GraphQL,前端拿到的就是这种非原生数组。 - GraphQL的不可变性处理:很多GraphQL客户端(比如Apollo Client)会把返回的数据包装成不可变对象,目的是防止意外修改缓存数据,这时候数组的索引属性会被设为只读,原地修改的
sort()自然会抛出错误。
解决办法(按场景选)
1. 后端提前转成原生数组(推荐)
在Mongoose查询后,把数组转换成纯原生JS数组再返回给GraphQL,从根源避免问题:
// 后端Mongoose查询示例 const targetDoc = await YourModel.findById(docId); // 用扩展运算符或Array.from复制出原生数组 const pureArray = [...targetDoc.yourArrayField]; // 或者用Mongoose的toObject()转成纯对象后提取数组 const plainDoc = targetDoc.toObject(); const pureArray = plainDoc.yourArrayField; // 再把pureArray作为GraphQL返回字段的值
2. 前端复制数组后再排序
如果后端没法修改,就在前端拿到数据后,先复制出一份原生数组再排序,避免修改原只读数组:
// 前端处理示例 const immutableArray = graphQLData.yourArray; // 用扩展运算符复制后排序 const sortedArray = [...immutableArray].sort((a, b) => a.sortField - b.sortField); // 或者用Array.from复制 const sortedArray = Array.from(immutableArray).sort((a, b) => { // 自定义排序逻辑,比如字符串排序 return a.name.localeCompare(b.name); });
3. 使用非原地排序的新API
ES2023新增了toSorted()方法,它会返回一个新的排序后的数组,完全不会修改原数组,适合处理只读数组:
const sortedArray = immutableArray.toSorted((a, b) => a.sortField - b.sortField);
内容的提问来源于stack exchange,提问作者user6198643
相关产品推荐
相关产品推荐

