前一篇文章介紹了模板語法,今天我們將介紹Vue3的響應式基礎。
在Vue3中,我們可以通過ref
和reactive
兩種響應式API創建響應式數據。ref
和reactive
都是非常重要的響應式編程方法,但是它們的適用場景和使用方式有一定的區別。在等等的文章即會討論到兩者的差異。
Ref 可以持有任何类型的值,包括深层嵌套的对象、数组或者 JavaScript 内置的数据结构,比如 Map。
來源:官方文件
RefImpl
的實例對象).value
屬性訪問或修改源值<script>
import { ref } from "vue";
export default {
setup() {
let name = ref("小明");
console.log(name);
return {
name
};
}
};
</script>
<template>
<p>{{ name }}</p>
<button @click="name = '小美'">換名字</button>
</template>
藉由console.log
出name
我們可以明顯看出ref將接收對象變成一個RefImpl實例
,且能透過.value
屬性訪問對象值。
<template>
<div>{{count}}</div>
<button @click='updateCount'>增加</button>
</template>
<script setup>
import { ref } from 'vue'
const count = ref(0)
console.log(count)
function updateCount() {
count.value++
}
</script>
在這裡我們使用了script setup
的方法,簡化程式碼並增加可閱讀性。
Vue 的響應式系統是通過依賴追蹤實現的。當組件首次渲染時,Vue 會記錄模板中使用的所有 ref。之後當 ref 的值發生變化時,Vue 可以檢測到並觸發組件重新渲染。
在普通的 JavaScript 變量中,無法檢測到什麼時候變量被訪問或修改。但是對於對象的屬性,我們可以通過 getter 和 setter 方法來攔截它的讀寫操作。
ref 返回的就是一個擁有 getter 和 setter 的對象,其中 .value 屬性包含了實際的值。當我們訪問 .value 時會觸發 getter 進行依賴追蹤,當我們修改 .value 時會觸發 setter 進行響應式觸發。
簡單來說:使用 ref 是為了讓 Vue 能夠追蹤依賴、檢測變化並觸發響應。ref 對對象進行了包裝,使其支持響應式系統的工作機制。
该 .value 属性给予了 Vue 一个机会来检测 ref 何时被访问或修改。在其内部,Vue 在它的 getter 中执行追踪,在它的 setter 中执行触发。从概念上讲,你可以将 ref 看作是一个像这样的对象:
來源:官方文件
// 伪代码,不是真正的实现
const myRef = {
_value: 0,
get value() {
track()
return this._value
},
set value(newValue) {
this._value = newValue
trigger()
}
}
proxy
代理對象<template>
<h3>姓名:{{user.name}}</h3>
<h3>年齡:{{user.age}}</h3>
<h3>wife:{{user.wife}}</h3>
<button @click="updateUser">更新</button>
</template>
<script>
import { reactive } from "vue";
export default {
setup() {
const user = reactive({
name: '大衛',
age: 18,
wife: {
name: '珍妮',
age: 18,
books: ['罪與罰', '小王子', '龍與地下城'],
},
});
const updateUser = () => {
user.name = '琳達';
user.age += 2;
user.wife.books[0] = '論語';
};
console.log(user)
return {
user,
updateUser,
};
},
};
</script>
reactive的響應式轉換是深層的,可以影響對象內部所有嵌套的屬性,使整個對象數據都變為響應式。這避免了層層包裝對象的麻煩。ref內部對對象的處理就是調用reactive進行響應式代理。ref需要訪問.value屬性是因為ref返回的是包裝對象,而reactive直接返回代理對象。
reactive
看似很方便,但同時官方文件也指出了他的侷限性,並且推薦開發人員使用ref
。
有限的值类型:它只能用于对象类型 (对象、数组和如 Map、Set 这样的集合类型)。它不能持有如 string、number 或 boolean 这样的原始类型。
不能替换整个对象:
let state = reactive({ count: 0 }) // 上面的 ({ count: 0 }) 引用将不再被追踪 // (响应性连接已丢失!) state = reactive({ count: 1 })
- 对解构操作不友好:当我们将响应式对象的原始类型属性解构为本地变量时,或者将该属性传递给函数时,我们将丢失响应性连接:
const state = reactive({ count: 0 }) // 当解构时,count 已经与 state.count 断开连接 let { count } = state // 不会影响原始的 state count++ // 该函数接收到的是一个普通的数字 // 并且无法追踪 state.count 的变化 // 我们必须传入整个对象以保持响应性 callSomeFunction(state.count)
來源:官方文件
今天的文章就到這裡。ref適用於單值屬性,reactive適用於對象和數組。兩者可以配合使用,以實現響應式基礎。