下述介绍仅针对组合式API~
reactive是一个函数,接收一个对象,把对象数据转化为响应式对象并返回。(需要从vue中导入reactive函数)
<template>
<div>{{ state.name }}</div>
<div>{{ state.age }}</div>
<button @click="state.name = '小华'">修改姓名</button>
</template>
<script>
import { reactive } from 'vue'
export default {
setup () {
const state = reactive({
name: '小明',
age: 18
})
return {
state
}
}
}
</script>
我们也可以在同一个作用域下定义一个更新 state 的函数,并作为一个方法与 state 一起暴露出去:
<button @click="increment">
{{ state.count }}
</button>
<script>
import { reactive } from 'vue'
export default {
setup() {
const state = reactive({ count: 0 })
function increment() {
state.count++
}
// 不要忘记同时暴露 increment 函数
return {
state,
increment
}
}
}
</script>
在 setup() 函数中手动暴露大量的状态和方法是繁琐的。我们可以通过使用构建工具来简化该操作。当使用单文件组件(SFC)时,我们可以使用 < script setup > 来大幅度地简化代码。
<template>
<button @click="increment">
{{ state.count }}
</button>
</template>
<script setup>
import { reactive } from 'vue'
const state = reactive({ count: 0 })
function increment() {
state.count++
}
</script>
仅对对象类型有效(对象、数组和 Map、Set 这样的集合类型),而对 string、number 和 boolean 这样的 原始类型 无效。
因为 Vue 的响应式系统是通过属性访问进行追踪的,因此我们必须始终保持对该响应式对象的相同引用,不可以随意地“替换”一个响应式对象,因为这将导致对初始引用的响应性连接丢失
let state = reactive({ count: 0 })
// 上面的引用 ({ count: 0 }) 将不再被追踪(响应性连接已丢失!)
state = reactive({ count: 1 })
这也意味着我们将响应式对象的属性赋值或解构至本地变量时,或是将该属性传入一个函数时,也会失去响应性:
const state = reactive({ count: 0 })
// n 是一个局部变量,同 state.count
// 失去响应性连接
let n = state.count
// 不影响原始的 state
n++
// count 也和 state.count 失去了响应性连接
let { count } = state
// 不会影响原始的 state
count++
// 该函数接收一个普通数字,并且将无法跟踪 state.count 的变化
callSomeFunction(state.count)
ref函数,接受一个简单类型或者复杂类型的传入并返回一个响应式且可变的 ref 对象(需要从vue中导入ref函数)注意:在setup函数中使用ref结果,需要通过.value 访问,模板中使用不需要加.value
<script setup>
import { ref } from 'vue'
const count = ref(0)
const objectRef = ref({ count: 0 })
function increment() {
count.value++
}
</script>
<template>
<button @click="increment">
// 无需 .value
{{ count }}
</button>
</template>
仍处于实验性阶段,在最终提案落地前仍可能发生改动。
相对于普通的 JavaScript 变量,我们不得不用相对繁琐的 .value 来获取 ref 的值。这是一个受限于 JavaScript 语言限制的缺点。然而,通过编译时转换,我们可以让编译器帮我们省去使用 .value 的麻烦。
<script setup>
let count = $ref(0)
function increment() {
// 无需 .value
count++
}
</script>
<template>
<button @click="increment">{{ count }}</button>
</template>
经过reactive函数处理之后返回的对象,如果给这个对象解构或者展开,会让数据丢失响应式的能力,为了解决这个问题需要引入toRefs函数,使用 toRefs函数 可以保证该对象展开的每个属性都是响应式的。(需要从vue中导入ref函数)
<template>
<div>{{ name }}</div>
<div>{{ age }}</div>
<button @click="name = '小华'">修改姓名</button>
</template>
<script>
import { reactive } from 'vue'
export default {
setup () {
const state = reactive({
name: '小明',
age: 18
})
return {
...toRefs(state)
}
}
}
</script>
计算属性来描述依赖响应式状态的复杂逻辑(需要从vue中导入ref函数)
<template>
<p>Has published books:</p>
<span>{{ publishedBooksMessage }}</span>
</template>
<script setup>
import { reactive, computed } from 'vue'
const author = reactive({
name: 'John Doe',
books: [
'Vue 2 - Advanced Guide',
'Vue 3 - Basic Guide',
'Vue 4 - The Mystery'
]
})
// 一个计算属性 ref
const publishedBooksMessage = computed(() => {
return author.books.length > 0 ? 'Yes' : 'No'
})
</script>
计算属性默认是只读的。当你尝试修改一个计算属性时,你会收到一个运行时警告。只在某些特殊场景中你可能才需要用到“可写”的属性,你可以通过同时提供 getter 和 setter 来创建:
<script setup>
import { ref, computed } from 'vue'
const firstName = ref('John')
const lastName = ref('Doe')
const fullName = computed({
// getter
get() {
return firstName.value + ' ' + lastName.value
},
// setter
set(newValue) {
// 注意:我们这里使用的是解构赋值语法
[firstName.value, lastName.value] = newValue.split(' ')
}
})
</script>
注意:计算属性的 getter 应只做计算而没有任何其他的副作用,这一点非常重要,请务必牢记。举例来说,不要在 getter 中做异步请求或者更改 DOM!一个计算属性的声明中描述的是如何根据其他值派生一个值。因此 getter 的职责应该仅为计算和返回该值。
与vue2的watch功能一致。使用方式不同:
在vue3中:在setup函数中执行watch函数开启对响应式数据的监听,watch函数接收三个常规参数:
<template>
{{ name }}
{{ info.age }}
<button @click="name = 'pink'">change name</button>
<button @click="info.age++">change age</button>
</template>
<script>
import { reactive, toRefs, watch } from 'vue'
export default {
setup() {
const state = reactive({
name: 'yx',
info: {
age: 18
}
})
watch(() => {
return state
}, () => {
// 数据变化之后的回调函数
console.log('age发生了变化')
}, {
deep: true, // 开启深度监听
immediate: true // 开启立即执行
})
return {
...toRefs(state)
}
}
}
</script>