Vue: readonly的代理坑
最近寫composable要 return 響應式變數,因為希望外部在使用這個composable的時候不要去改動到這個響應式變數,故使用 readonly 去包覆這個響應式變數:
// useComposable.js
import { readonly } from "vue";
export default useComposable(){
const nested = ref({
states: [
{
id: 1,
isChecked: false,
/* ... */
},
{
id: 2,
isChecked: false,
/* ... */
},
]
});
// ...
const toggleState = (val, state) => {
state.isChecked = val;
}
return {
nested: readonly(nested),
toggleState
}
}
我在外部檔案使用這個composable,取得到nested這個巢狀物件,但希望改變其中的 nested.states[0].isChecked 屬性值:
// outer.vue
<template>
<checkbox
v-for="state in states"
:key="state.id"
:model-value="state.isChecked"
@update:modelValue="\(evt => toggleState(\)evt, state)"
/>
</template>
<script>
import useComposable from "./useComposable";
const { states, toggleState } = useComposable();
</script>
結果發現無法改變nested.states[0].isChecked 屬性值,後來看文件:
A readonly proxy is deep: any nested property accessed will be readonly as well.
經 readonly 包裹的響應式變數會連同其內部屬性都一起設定為唯讀(readonly),所以即使我在 toggleState 會是傳入唯讀的 state、無法修改其值,所以正確 toggleState 的正確作法應該是傳入 id 去改變原始的響應式變數:
const toggleState = (val, stateId) => {
const state = nested.states.find(s => s.id === stateId);
state.isChecked = val;
}
References