大对象必须用 Object.freeze 而非简单赋值,因 Vue 默认对其递归建立 getter/setter 实现响应式,1000+ 条数据初始化可卡顿 100ms 以上;冻结后 Vue 跳过响应式转换,提升性能并节省内存,但仅浅冻结第一层,嵌套对象需递归冻结或确保不变。

为什么大对象必须用 Object.freeze 而不是简单赋值
直接把一个大型配置对象或静态字典(比如城市列表、枚举映射表)赋给 data,Vue 会默认对其做响应式代理——这意味着每个属性都要建立 getter/setter,触发依赖收集。1000+ 条数据的对象,光是初始化响应式系统就可能卡顿 100ms 以上,尤其在微信小程序低端机上明显卡顿或白屏。
冻结后,Vue 会跳过响应式转换,既省内存又提速。但注意:Object.freeze 是浅冻结,只冻第一层属性;嵌套对象仍会被 Vue 响应式处理,除非你递归冻结或确保嵌套结构本身也不变。
Object.freeze 放在哪?data 还是 computed 还是外部常量
推荐放在组件外部定义为常量,再导入使用。不要在 data() 里调用 Object.freeze:
- 在
data()中每次实例化都执行一次Object.freeze,浪费 CPU,且无法复用 - 放在
computed里更糟——每次响应式依赖变化都会重新冻结,毫无意义 - 正确做法:新建
constants.js或staticData.js,导出已冻结对象
示例:
// utils/staticData.js
export const CITY_LIST = Object.freeze([
{ value: 'bj', label: '北京' },
{ value: 'sh', label: '上海' },
// ... 500+ 条
])
export const CONFIG_MAP = Object.freeze({
theme: Object.freeze({ primary: '#409eff', radius: '8rpx' }),
api: Object.freeze({ timeout: 10000, base: '/v2/' })
})
冻结后修改属性会怎样?开发阶段怎么快速发现错误
冻结对象的属性无法被修改(包括增删改),但在非严格模式下,静默失败;严格模式下会抛 TypeError: Cannot assign to read only property。uni-app 默认非严格模式,所以你可能根本不知道改失败了。
建议在开发时主动加一层防护:
- 在 HBuilderX 的项目设置中开启「ESLint + strict mode」插件,让编辑器提前标红
- 对关键冻结对象做运行时校验(仅开发环境):
if (process.env.NODE_ENV === 'development' && !Object.isFrozen(CITY_LIST)) console.warn('CITY_LIST 未冻结') - 避免在
mounted或onLoad里试图给冻结对象赋新值,例如this.CITY_LIST = [...old]—— 这不会生效,还可能引发 Vue 的响应式警告
和 uni-app 的 provide/inject 或 Pinia 冻结策略冲突吗
不冲突,但要注意层级。如果你把冻结对象通过 provide 注入子组件,子组件拿到的是同一引用,依然不可变;但如果在 Pinia store 的 state 里直接放冻结对象,要注意:
-
state: () => ({ options: CITY_LIST })✅ 安全,冻结对象被直接引用 -
state: () => ({ options: {...CITY_LIST} })❌ 错误,解构后失去冻结,且触发响应式代理 - Pinia 的
readonlyhelper(如defineStore(...).$state = readonly(...))是另一层保护,不能替代Object.freeze,两者可共存,但优先冻结原始数据源
真正容易被忽略的点是:冻结必须发生在数据首次创建时。一旦被 Vue 的响应式系统接管过,再 Object.freeze 就无效了——所以别想着“先响应式再冻结”。


















