readonly 详解
readonly() 创建一个只读代理对象,防止数据被意外修改。这在保护组件内部状态、向子组件传递不可变数据时非常有用。
readonly()
readonly() 创建深度只读代理——所有层级的属性都不允许修改。
tsx
import { readonly } from 'vitarx'
const original = { name: '张三', profile: { age: 25 } }
const state = readonly(original)
// 读取正常
console.log(state.name) // '张三'
console.log(state.profile.age) // 25
// ❌ 修改直接属性——控制台输出警告,修改不生效
state.name = '李四'
// ❌ 修改嵌套属性——控制台输出警告,修改不生效(深度只读)
state.profile.age = 30WARNING
在开发环境下,尝试修改 readonly 对象会在控制台输出警告信息。修改操作不会生效,但不会抛出错误。
shallowReadonly()
shallowReadonly() 创建浅层只读代理——只有直接属性是只读的,嵌套对象仍然可以修改。
tsx
import { shallowReadonly } from 'vitarx'
const original = { name: '张三', profile: { age: 25 } }
const state = shallowReadonly(original)
// ❌ 修改直接属性——控制台输出警告,修改不生效
state.name = '李四'
// ✅ 修改嵌套属性——允许修改(浅层只读)
state.profile.age = 30
console.log(state.profile.age) // 30readonly vs shallowReadonly
| 特性 | readonly() | shallowReadonly() |
|---|---|---|
| 直接属性 | 只读 | 只读 |
| 嵌套属性 | 只读 | 可修改 |
| 性能 | 略低(需要深度代理) | 略高(只代理一层) |
与 reactive 结合使用
readonly() 常与 reactive() 结合使用——内部使用 reactive 维护可变状态,对外暴露 readonly 版本:
tsx
import { reactive, readonly, createApp } from 'vitarx'
function useCounter() {
// 内部可变状态
const state = reactive({ count: 0 })
// 修改方法
const increment = () => state.count++
const decrement = () => state.count--
// 对外暴露只读版本——外部无法直接修改
return {
state: readonly(state),
increment,
decrement
}
}
function App() {
const { state, increment, decrement } = useCounter()
return (
<div>
<p>计数: {state.count}</p>
<button onClick={increment}>+1</button>
<button onClick={decrement}>-1</button>
</div>
)
}
createApp(App).mount('#app')这种模式确保了外部只能通过提供的方法修改状态,不能直接篡改数据。
防止外部修改组件内部状态
在组件开发中,你可能需要将内部状态传递给子组件,但不希望子组件修改它。使用 readonly 可以有效防止这种情况:
tsx
import { reactive, readonly, createApp } from 'vitarx'
// 父组件
function Parent() {
const config = reactive({
theme: 'dark',
fontSize: 14
})
return (
<div>
<p>当前主题: {config.theme}</p>
<Child config={readonly(config)} />
</div>
)
}
// 子组件——接收到的 config 是只读的
function Child({ config }) {
// ✅ 可以读取
console.log(config.theme)
// ❌ 无法修改——控制台输出警告
// config.theme = 'light'
return <p>子组件收到的主题: {config.theme}</p>
}
createApp(Parent).mount('#app')集合类型的只读代理
readonly() 同样支持 Map、Set、WeakMap、WeakSet 等集合类型:
tsx
import { readonly } from 'vitarx'
const originalMap = new Map([['name', '张三']])
const readonlyMap = readonly(originalMap)
// ✅ 读取正常
console.log(readonlyMap.get('name')) // '张三'
// ❌ 修改操作——控制台输出警告
readonlyMap.set('name', '李四')
readonlyMap.delete('name')
readonlyMap.clear()isReadonly()
isReadonly() 判断一个值是否为只读代理对象:
tsx
import { readonly, shallowReadonly, reactive, isReadonly } from 'vitarx'
const state = reactive({ count: 0 })
const readonlyState = readonly(state)
const shallowReadonlyState = shallowReadonly(state)
isReadonly(readonlyState) // true
isReadonly(shallowReadonlyState) // true
isReadonly(state) // false
isReadonly({}) // falseINFO
GetterRef(通过 toRef(() => ...) 创建)也是只读的,isReadonly() 对它返回 true。
完整示例
下面是一个综合使用 readonly 相关 API 的示例:
tsx
import { reactive, readonly, shallowReadonly, isReadonly, createApp } from 'vitarx'
function App() {
// 内部可变状态
const internalState = reactive({
name: '张三',
profile: { age: 25, city: '北京' }
})
// 深度只读——对外暴露
const publicState = readonly(internalState)
// 浅层只读——嵌套属性仍可修改
const semiState = shallowReadonly(internalState)
const changeName = () => {
// 内部可以正常修改
internalState.name = '李四'
}
const changeCity = () => {
// 内部可以修改嵌套属性
internalState.profile.city = '上海'
}
return (
<div style={{ padding: '20px' }}>
<h2>readonly 详解示例</h2>
<div style={{ marginTop: '16px' }}>
<h3>内部状态(可修改)</h3>
<p>姓名: {internalState.name}</p>
<p>城市: {internalState.profile.city}</p>
<button onClick={changeName}>修改姓名</button>
<button onClick={changeCity}>修改城市</button>
</div>
<div style={{ marginTop: '16px' }}>
<h3>只读代理(不可修改)</h3>
<p>是否为只读: {String(isReadonly(publicState))}</p>
<p>姓名: {publicState.name}</p>
<p>城市: {publicState.profile.city}</p>
</div>
<div style={{ marginTop: '16px' }}>
<h3>浅层只读代理</h3>
<p>是否为只读: {String(isReadonly(semiState))}</p>
<p>姓名: {semiState.name}</p>
<p>城市: {semiState.profile.city}</p>
</div>
</div>
)
}
createApp(App).mount('#app')下一步
- computed 详解 — 学习计算属性的懒计算和脏标记机制