文章目录

Vue3 readonly与shallowReadonly保护状态不被修改

发布于 2026-04-28 17:23:25 · 浏览 4 次 · 评论 0 条

Vue3 readonly与shallowReadonly保护状态不被修改

Vue3提供了多种状态管理方式,其中readonlyshallowReadonly是保护状态不被修改的重要工具。理解这两个API的工作原理和适用场景,可以帮助开发者更好地控制应用状态,避免意外的状态变更。


1. 认识readonly

readonly是Vue3提供的响应式API,用于将响应式对象或数组转换为只读版本。应用readonly后,任何对返回对象属性的修改操作都会在开发环境下发出警告。

创建一个只读对象非常简单:

import { reactive, readonly } from 'vue'

const original = reactive({
  count: 0,
  nested: {
    value: 10
  }
})

const readOnlyOriginal = readonly(original)

使用readonly后,以下操作会触发警告:

readOnlyOriginal.count++ // 警告:Set operation on key "count" failed: target is readonly.
readOnlyOriginal.nested.value = 20 // 警告:Set operation on key "value" failed: target is readonly.

2. readonly的特性

了解readonly的核心特性有助于更好地使用它:

  1. 深度只读:readonly会创建一个完全不可变的代理,包括嵌套对象和数组
  2. 保持响应式:只读对象仍然是响应式的,可以跟踪依赖变化
  3. 警告提示:在开发环境下尝试修改会发出警告,生产环境下静默失败
  4. 引用保持:返回的对象与原始对象保持相同的引用,可以追踪变化

检查对象是否为只读:

import { isReadonly } from 'vue'

console.log(isReadonly(readOnlyOriginal)) // true

3. 认识shallowReadonly

shallowReadonly是readonly的"浅层"版本。应用shallowReadonly后,只有顶层属性是只读的,嵌套对象的属性仍然可以被修改。

创建一个浅层只读对象:

import { reactive, shallowReadonly } from 'vue'

const original = reactive({
  count: 0,
  nested: {
    value: 10
  }
})

const shallowReadOnlyOriginal = shallowReadonly(original)

使用shallowReadonly后,以下行为会不同:

shallowReadOnlyOriginal.count++ // 警告:Set operation on key "count" failed: target is readonly.
shallowReadOnlyOriginal.nested.value = 20 // 不会警告,修改成功

4. readonly与shallowReadonly对比

了解两者的差异有助于在不同场景下做出正确选择:

特性 readonly shallowReadonly
深度 深度只读 仅顶层只读
嵌套修改 禁止 允许
性能 较高开销(遍历所有嵌套属性) 较低开销(仅代理顶层属性)
适用场景 需要完全保护的数据结构 需要保护顶层但允许修改嵌套属性的场景

5. 实际应用场景

5.1 使用readonly

场景:保护从API获取的不应该被修改的配置数据。

// 在store或组件中
import { readonly } from 'vue'

export const useConfigStore = defineStore('config', () => {
  const config = reactive({
    apiEndpoints: {
      user: '/api/users',
      product: '/api/products'
    },
    settings: {
      timeout: 5000,
      retries: 3
    }
  })

  // 暴露为只读,防止意外修改
  return readonly(config)
})

5.2 使用shallowReadonly

场景:保护组件props的顶层属性,但允许修改深层状态。

// 父组件
const userProfile = reactive({
  userInfo: {
    name: 'John',
    age: 30
  },
  preferences: {
    theme: 'dark',
    notifications: true
  }
})

const userProfileReadOnly = shallowReadonly(userProfile)

// 传递给子组件
<ChildComponent :user-profile="userProfileReadOnly" />

// 子组件中
props: {
  userProfile: Object
}

// 子组件内尝试修改
props.userProfile.userInfo.name = 'Jane' // 成功
props.userProfile.preferences.theme = 'light' // 成功

props.userProfile = {} // 警告:失败,顶层属性是只读的

6. 在组合式API中的应用

保护组合式API中的状态是一个常见需求:

import { ref, readonly, shallowReadonly } from 'vue'

export function useUser() {
  const user = ref({
    name: 'John',
    age: 30,
    preferences: {
      theme: 'dark',
      notifications: true
    }
  })

  // 如果需要完全保护
  const readonlyUser = readonly(user)

  // 如果需要保护顶层但允许修改preferences
  const shallowReadOnlyUser = shallowReadonly(user)

  return { user: shallowReadOnlyUser, updateUser }
}

7. 在Pinia状态管理中的应用

结合Pinia使用readonly可以有效保护状态不被意外修改:

import { defineStore } from 'pinia'
import { readonly, shallowReadonly } from 'vue'

export const useUserStore = defineStore('user', () => {
  const user = reactive({
    id: 1,
    name: 'John',
    preferences: {
      theme: 'dark',
      notifications: true
    }
  })

  const updateUser = (newUserData) => {
    Object.assign(user, newUserData)
  }

  // 返回浅层只读状态
  return {
    // 如果完全保护,使用 readonly(user)
    // 如果允许修改嵌套属性,使用 shallowReadonly(user)
    user: shallowReadonly(user),
    updateUser
  }
})

8. 最佳实践

遵循以下原则可以有效使用readonly和shallowReadonly:

  1. 选择合适的保护级别

    • 如果数据需要完全不可变,使用readonly
    • 如果需要保护顶层结构但允许修改嵌套数据,使用shallowReadonly
  2. 在组件中保护props

    import { shallowReadonly } from 'vue'
    
    export default {
      setup(props) {
        // 保护props不被修改
        return {
          readonlyProps: shallowReadonly(props)
        }
      }
    }
  3. 在状态库中保护状态

    // 在store/index.js
    import { reactive, readonly } from 'vue'
    
    export const state = reactive({
      // state data
    })
    
    export const readonlyState = readonly(state)
  4. 性能考虑:对于深层嵌套对象,优先使用shallowReadonly以减少性能开销

记住,readonly和shallowReadonly在开发模式下会提供警告,帮助识别意外修改,但在生产模式下这些警告会被禁用,修改操作会静默失败。

评论 (0)

暂无评论,快来抢沙发吧!

扫一扫,手机查看

扫描上方二维码,在手机上查看本文