在組合式 API 中,推薦使用 ref() 函數來聲明響應式狀態: ref() 接收參數,并將其包裹在一個帶有 .value 屬性的 ref 對象中返回:
import { ref } from 'vue'
const count = ref(0)
console.log(count) // { value: 0 }
console.log(count.value) // 0
count.value++
console.log(count.value) // 1
<script lang="ts" >
import { ref } from 'vue'
export default {
setup() {
const count = ref(0)
function increment() {
// 在 JavaScript 中需要 .value
count.value++
}
// 不要忘記同時暴露 increment 函數
return {
count,
increment
}
}
}
</script>
<template>
<div class="container">
<div>{{ count }}</div>
<button @click="count++">
{{ count }}
</button>
</div>
</template>
<style scoped>
.container {}
</style>
在模板渲染上下文中,只有頂級的 ref 屬性才會被解包。
在下面的例子中,count 和 object 是頂級屬性,但 object.id 不是:
const count = ref(0)
const object = { id: ref(1) }
//模版正常渲染執行
{{ count + 1 }}
//模版不會正常渲染非頂級不會被解包仍然是一個ref
{{ object.id + 1 }} 對象
//我們可以將 id 解構為一個頂級屬性
const { id } = object
{{ id + 1 }} //模版正常渲染并執行
//模版自動解包
{{ object.id }}
該特性僅僅是文本插值的一個便利特性,等價于 {{ object.id.value }}
<script setup lang="ts">
import { ref } from 'vue'
const count = ref(0)
function increment() {
count.value++
}
</script>
<template>
<button @click="increment">
{{ count }}
</button>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const count = ref(0)
const obj = ref({
nested: { count: 0 },
arr: ['foo', 'bar']
})
function mutateDeeply() {
// 以下都會按照期望工作
obj.value.nested.count++
obj.value.arr.push('baz')
}
function increment() {
count.value++
}
</script>
<template>
{{ obj.arr }}
<button @click="mutateDeeply">
{{ obj.nested.count + 1 }}
</button>
</template>
可以通過 shallow ref 來放棄深層響應性
要等待 DOM 更新完成后再執行額外的代碼,可以使用 nextTick() 全局 API:
先來一個用于測試的Demo頁:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ref active toRef toRefs</title>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/3.1.1/vue.global.js"></script>
</head>
<body>
<div id="app">
<span> ref: </span>
<input type="text" v-model="refValue" @input="inputRefHander" />
{{ refValue }}
<hr>
<span> reactive: (顯示age的值)</span>
<input type="text" v-model="reactiveValue.age" @input="inputReactiveHander" />
{{ reactiveValue.age }}
<hr>
<span> toRef: (使age有響應)</span>
<input type="text" v-model="toRefValue" @input="inputToRefValueHander" />
{{ toRefValue}}
<hr>
<span> toRefs: </span>
<input type="text" v-model="toRefsValue.name.value" @input="inputToRefsValueHander" />
{{ toRefsValue.name.value}}
</div>
</body>
</html>
<script>
const { createApp, reactive, toRefs, ref, toRef } = Vue;
const app = createApp({
setup() {
let name = 'chj';
const obj = { name: 'chj', gender: 'male', age: 18 }
const refValue = ref(name); // => reactive({value:'chj'})
const reactiveValue = reactive(obj) // => reactive({ name: 'chj', gender: 'male', age: 18 })
const toRefValue = toRef(obj, 'age') // => reactive({value:18})
const toRefsValue = toRefs(obj)
function inputRefHander() {
console.log(`ref:::refValue:`);
console.log(refValue);
console.log(`原始數據`);
console.log(name);
}
function inputReactiveHander() {
console.log(`reactive:::reactiveValue`);
console.log(reactiveValue);
console.log(`原始數據`);
console.log(obj);
}
function inputToRefValueHander() {
console.log(`toRef:::toRefValue: `);
console.log(toRefValue);
console.log(`原始數據:`);
console.log(obj);
}
function inputToRefsValueHander() {
console.log(`toRefs:::toRefsValue: `);
console.log(toRefsValue);
console.log(`原始數據:`);
console.log(obj);
}
return { refValue, inputRefHander, reactiveValue, inputReactiveHander, toRefValue, inputToRefValueHander, toRefsValue, inputToRefsValueHander }
}
})
app.mount("#app")
</script>
隨意輸入內容,發現ref“包裹的”數據變化如下:
對于ref:
原始數據沒有變化,而ref“包裹的數據”變成了一個新的對象,而且模板有變化。
對于reactive:
reactive處理的數據無論是原始數據,“包裹后的數據”,還是模板,都有變化。
對于toRef:
toRef處理的數據會有變化,而原始數據也有變化,但是模板沒有變化
對于toRefs:wtoRefs處理的數據響應變化,原始數據也響應變化,但是模板并沒有變化
總結
ef、isRef、toRef、toRefs、toRaw 看著一堆類似的東西,一個頭兩個大,今天整理一篇文章詳細介紹它們的功能及區別。
ref 屬性除了能夠獲取元素外,也可以使用 ref 函數,創建一個響應式數據,當數據值發生改變時,視圖自動更新。
<script lang="ts" setup>
import { ref } from 'vue'
let str: string = ref('我是張三')
const chang = () => {
str.value = '我是鉆石王老五'
console.log(str.value)
}
</script>
<template>
<div>
{{ str }}
<button type="button" @click="chang">修改值</button>
</div>
</template>
檢查變量是否為一個被 ref 包裝過的對象,如果是返回 true ,否則返回 false。
import { ref, isRef, reactive } from 'vue'
let str: string = ref('我是張三')
let num: number = 1
let per = reactive({ name: '代碼女神', work: '程序媛' })
console.log('strRes', isRef(str)) //true
console.log('numRes', isRef(num)) //false
console.log('perRes', isRef(per)) //false
創建一個 ref 對象,其 value 值指向另一個對象中的某個屬性。
toRef(obj, key) 將對象中的某個值轉化為響應式數據,分為兩種情況:
<script>
import { ref, isRef, toRef, reactive } from 'vue'
let obj = {
name: '姓名',
age: 18,
}
let name: string = toRef(obj, 'name')
const chang = () => {
obj.name = '鉆石王老五'
name.value = '李四'
console.log(obj.name) // 李四
console.log('name', name) // 李四
}
//chang() //DOM掛載前調用
</script>
<template>
<div>
{{ obj.name }} ------- {{ name }}
<button type="button" @click="chang">修改值</button>
</div>
</template>
注意:如果是在 DOM 掛載之前調用 chang 方法,改變數值,此時數據和視圖都會發生改變。
<script>
import { ref, isRef, toRef, reactive } from 'vue'
let obj = reactive({
name: '姓名',
age: 18,
})
let name: string = toRef(obj, 'name')
const chang = () => {
obj.name = '鉆石王老五'
name.value = '李四'
}
</script>
<template>
<div>
{{ obj.name }} ------- {{ name }}
<button type="button" @click="chang">修改值</button>
</div>
</template>
最終值為 “李四”。
toRefs 用來解構 ref、reactive 包裹的響應式數據。接收一個對象作為參數,遍歷對象上的所有屬性,將對象上的所有屬性變成響應式數據。
let obj = reactive({
name: '姓名',
age: 18,
})
let { name, age } = toRefs(obj)
const chang = () => {
name.value = '鉆石王老五'
age.value++
}
</script>
<template>
<div>
{{ name }} ------- {{ age }}
<button type="button" @click="chang">修改值</button>
</div>
</template>
toRefs 解構數據時,如果某些參數作為可選參數,可選參數不存在時就會報錯,如:
let obj = reactive({
name: '姓名',
age: 18,
})
let { name, age, work } = toRefs(obj)
const chang = () => {
name.value = '鉆石王老五'
age.value++
console.log('work', work.value)
work.value = '程序媛'
}
此時可以使用 toRef 解決此問題,使用 toRef 解構對象某個屬性時,先檢查對象上是否存在該屬性,如果存在就繼承對象上的屬性值,如果不存在就會創建一個。
修改上邊的代碼為:
let obj = reactive({
name: '姓名',
age: 18,
})
let { name, age } = toRefs(obj)
let work = toRef(obj, 'work')
const chang = () => {
name.value = '鉆石王老五'
age.value++
console.log('work', work.value)
work.value = '程序媛'
}
將響應式對象轉為原始對象。做一些不想被監聽的事情,從 ref 或 reactive 得到原始數據。
修改原響應式數據時,toRaw 轉換得到的數據會被修改,視圖也會更新,如:
<script lang="ts" setup>
import { ref, isRef, toRef, toRefs, reactive, toRaw } from 'vue'
let obj = reactive({
name: '姓名',
age: 18,
})
let newObj = toRaw(obj)
const chang = () => {
obj.name = '鉆石王老五'
obj.age++
}
</script>
<template>
<div>
{{ obj.name }} ------- {{ obj.age }}
<button type="button" @click="chang">修改值</button>
<br />
{{ newObj }}
</div>
</template>
如果修改 toRaw 得到的原始數據,原數據也會被修改,但是視圖不更新。如:
*請認真填寫需求信息,我們會在24小時內與您取得聯系。