Vue 3 中使用?ref
?創建的響應式對象數據轉換為普通(非響應式)的數據,有以下幾種方法:
1. 訪問?.value
?屬性:
這是最直接、最常見的方法。 由于?ref
?對象的值存儲在其?.value
?屬性中,直接訪問該屬性即可獲得普通數據。
<script setup>import { ref } from 'vue';
const myRef = ref({ name: 'John', age: 30 });// 獲取普通對象
const plainObject = myRef.value;console.log(plainObject); // { name: 'John', age: 30 }
console.log(typeof plainObject); // object// 修改 plainObject 不會影響 myRef
plainObject.name = 'Jane';console.log(myRef.value.name); // "John" (myRef 仍然是響應式的,沒有被修改)
console.log(plainObject.name); // "Jane"</script>
- 優點:?簡單直接,易于理解。
- 缺點:?如果你需要的是深拷貝,此方法只進行淺拷貝,修改?
plainObject
?的嵌套對象屬性仍然會影響?myRef
。
2. 使用展開運算符 (...
) 進行淺拷貝:
如果你需要的是一個新的對象,并且避免修改原始的?ref
?對象,可以使用展開運算符創建一個淺拷貝。
<script setup>import { ref } from 'vue';const myRef = ref({ name: 'John', age: 30, address: { city: 'New York' } });// 使用展開運算符進行淺拷貝const plainObject = { ...myRef.value };console.log(plainObject); // { name: 'John', age: 30, address: { city: 'New York' } }// 修改淺拷貝對象的屬性
plainObject.name = 'Jane';
console.log(myRef.value.name); // "John"
console.log(plainObject.name); // "Jane"// 修改嵌套對象屬性會影響 myRef,因為是淺拷貝
plainObject.address.city = 'Los Angeles';
console.log(myRef.value.address.city); // "Los Angeles" (仍然會影響 ref 對象)
console.log(plainObject.address.city); // "Los Angeles"</script>
- 優點:?創建了一個新的對象,修改拷貝后的對象不會直接影響原始的?
ref
?對象。 - 缺點:?仍然是淺拷貝。如果?
ref
?對象包含嵌套的對象或數組,修改拷貝后的對象的嵌套屬性仍然會影響原始的?ref
?對象。
3. 使用?JSON.parse(JSON.stringify(obj))
?進行深拷貝 (不推薦用于包含函數或 Symbol 的對象):
這是一種常用的創建深拷貝的方法,但不適用于包含函數、Symbol、循環引用的對象。
<script setup>import { ref } from 'vue';const myRef = ref({ name: 'John', age: 30, address: { city: 'New York' } });// 使用 JSON.parse(JSON.stringify()) 進行深拷貝const plainObject = JSON.parse(JSON.stringify(myRef.value));console.log(plainObject); // { name: 'John', age: 30, address: { city: 'New York' } }// 修改深拷貝對象的屬性plainObject.name = 'Jane';plainObject.address.city = 'Los Angeles';console.log(myRef.value.name); // "John"
console.log(plainObject.name); // "Jane"
console.log(myRef.value.address.city); // "New York"
console.log(plainObject.address.city); // "Los Angeles"</script>
- 優點:?可以創建深拷貝,完全隔離原始?
ref
?對象和拷貝對象。 - 缺點:
- 效率相對較低。
- 無法復制函數、Symbol、
undefined
?和循環引用的對象。?函數會被忽略,Symbol 會丟失,undefined 會變成 null, 循環引用會報錯。 - Date對象會被轉換成字符串。
4. 使用?lodash
?的?_.cloneDeep()
?函數 (推薦):
lodash
?是一個流行的 JavaScript 工具庫,提供了很多實用的函數,包括深拷貝。
<script setup>
import { ref } from 'vue';
import _ from 'lodash'; // 需要安裝 lodash: npm install lodashconst myRef = ref({ name: 'John', age: 30, address: { city: 'New York' }, func: () => {} });// 使用 lodash 的 _.cloneDeep() 函數進行深拷貝const plainObject = _.cloneDeep(myRef.value);console.log(plainObject); // { name: 'John', age: 30, address: { city: 'New York' }, func: () => {} }// 修改深拷貝對象的屬性plainObject.name = 'Jane';plainObject.address.city = 'Los Angeles';plainObject.func = () => { console.log('new func') }; // 修改函數console.log(myRef.value.name); // "John"
console.log(plainObject.name); // "Jane"
console.log(myRef.value.address.city); // "New York"
console.log(plainObject.address.city); // "Los Angeles"
</script>
- 優點:
- 可以創建真正的深拷貝,包括函數和 Symbol (取決于 lodash 版本)。
- 能夠處理循環引用。
- 經過優化,性能通常比?
JSON.parse(JSON.stringify())
?更好。
- 缺點:?需要安裝額外的庫 (
lodash
)。
5. 使用?structuredClone()
?(現代瀏覽器):
structuredClone()
?是一個現代瀏覽器提供的全局函數,用于創建深拷貝。
<script setup>import { ref } from 'vue';const myRef = ref({ name: 'John', age: 30, address: { city: 'New York' }, func: () => {} });// 使用 structuredClone() 進行深拷貝
const plainObject = structuredClone(myRef.value);console.log(plainObject);
// 修改深拷貝對象的屬性plainObject.name = 'Jane';
plainObject.address.city = 'Los Angeles';console.log(myRef.value.name); // "John"
console.log(plainObject.name); // "Jane"
console.log(myRef.value.address.city); // "New York"
console.log(plainObject.address.city); // "Los Angeles"</script>
-
優點:
- 內置于瀏覽器,無需安裝額外的庫。
- 可以處理循環引用。
- 支持多種數據類型,包括?
Date
、RegExp
、Map
、Set
?等。
-
缺點:
- 瀏覽器兼容性:不是所有瀏覽器都支持?
structuredClone()
?(舊版本的瀏覽器可能不支持). 需要 polyfill 。 - 性能可能不如?
lodash
?的?_.cloneDeep()
,具體取決于數據結構和瀏覽器實現。 - 無法拷貝函數。?如果對象包含函數,函數屬性將會被設置為?
null
。
- 瀏覽器兼容性:不是所有瀏覽器都支持?
總結:
- 如果只需要訪問?
ref
?對象的值,直接使用?.value
?即可。 - 如果需要創建一個新的對象,并且不需要深拷貝,可以使用展開運算符 (
...
)。 - 如果需要深拷貝,且對象不包含函數、Symbol、循環引用,可以使用?
JSON.parse(JSON.stringify())
?(但通常不推薦)。 - 最推薦使用?
lodash
?的?_.cloneDeep()
?函數進行深拷貝。?它功能強大,能夠處理多種情況,性能也不錯。 - 如果你的目標環境都是現代瀏覽器,并且不需要拷貝函數,可以考慮使用?
structuredClone()
。
選擇哪種方法取決于你的具體需求:
- 是否需要創建一個新的對象?
- 是否需要深拷貝?
- 對象是否包含函數、Symbol、循環引用?
- 性能要求如何?
- 是否可以引入額外的庫?
- 目標瀏覽器環境是什么?
根據這些因素,選擇最適合你的方法。 在大多數情況下,使用?lodash
?的?_.cloneDeep()
?是一個安全可靠的選擇。