ref和reactive
ref和reactive
ref
什么是ref?
ref和reactive一样,也是用来实现响应式数据的方法 由于reactive必须传递一个对象,所以在实际开发中如果只是想让某个变量实现响应式的时候回非常麻烦 所以Vue3提供了ref方法实现简单值得监听
ref本质
ref底层其实还是reactive,所以当运行时系统会自动根据传入的ref转换成reactive
ref注意点
在vue中使用ref的值不用通过value获取 在js中使用ref的值必须通过value获取
<template>
<div>
<button @click="click"></button>
// 直接引用变量获取值
<p>{{num}}</p>
</div>
</template>
<script lang="ts">
import { defineComponent,ref } from 'vue'
export default defineComponent({
setup() {
// 声明双向数据ref
const num=ref()
// js 通过.value 获取值
num.value=123;
const click = ()=>{
num.value+=1;
}
return {
num,
click
}
},
})
</script>
通过ref 获取元素
在vue2中我们可以通过给元素添加ref=‘xxx’,然后在代码中通过refs.xxx的方式来获取元素,在vue3中也可以通过ref来获取元素.但不是像以下这种熟悉的方式,因为在vue3中没有$和refs这些东西
<template>
<div ref="div">
这个是div
</div>
</template>
<script lang="ts">
import { defineComponent,onMounted,ref } from 'vue'
export default defineComponent({
setup() {
const div=ref(null) //本质是reactive({value:null})
// 错误用法
// this.$refs.div
//正确用法
// 需要再onMountd 生命周期内使用
onMounted(()=>{
// 界面挂载完后 会执行
console.log(div.value)
})
//接受的是null,原因是setup执行时机比mounted早,dom还没形成 注意vue3的生命周期
console.log(div.value);// 执行早于 onMounted
return {
div
}
},
})
</script>
输出结果
null
<div>这个是div</div>
案例
这里案例使用的是ts,如果转换成js也类似
案例一
我们这样操作是无法改变message 的值 应为message 不是响应式的无法被vue 跟踪要改成ref
<template>
<div>
<button @click="changeMsg">change</button>
<div>{{ message }}</div>
</div>
</template>
<script setup lang="ts">
let message: string = "我是message"
const changeMsg = () => {
message = "change msg"
}
</script>
<style>
</style>
改为ref
注意被ref包装之后需要.value 来进行赋值
<template>
<div>
<button @click="changeMsg">change</button>
<div>{{ message }}</div>
</div>
</template>
<script setup lang="ts">
import {ref,Ref} from 'vue'
let message:Ref<string> = ref("我是message")
const changeMsg = () => {
message.value = "change msg"
}
</script>
<style>
</style>
//--------------------------------ts两种方式
<template>
<div>
<button @click="changeMsg">change</button>
<div>{{ message }}</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
let message = ref<string | number>("我是message")
const changeMsg = () => {
message.value = "change msg"
}
</script>
<style>
</style>
案例二
判断是不是一个ref对象
import { ref, Ref,isRef } from 'vue'
let message: Ref<string | number> = ref("我是message")
let notRef:number = 123
const changeMsg = () => {
message.value = "change msg"
console.log(isRef(message)); //true
console.log(isRef(notRef)); //false
}
案例三
创建一个跟踪自身.value变化的 ref,但不会使其值也变成响应式的
1、修改其属性是非响应式的这样是不会改变的
<template>
<div>
<button @click="changeMsg">change</button>
<div>{{ message }}</div>
</div>
</template>
<script setup lang="ts">
import { Ref, shallowRef } from 'vue'
type Obj = {
name: string
}
let message: Ref<Obj> = shallowRef({
name: "小满"
})
const changeMsg = () => {
message.value.name = '大满'
}
</script>
<style>
</style>
2、这样是可以被监听到的修改value
import { Ref, shallowRef } from 'vue'
type Obj = {
name: string
}
let message: Ref<Obj> = shallowRef({
name: "小满"
})
const changeMsg = () => {
message.value = { name: "大满" }
}
3、强制更新页面DOM
这样也是可以改变值的
<template>
<div>
<button @click="changeMsg">change</button>
<div>{{ message }}</div>
</div>
</template>
<script setup lang="ts">
import { Ref, shallowRef,triggerRef } from 'vue'
type Obj = {
name: string
}
let message: Ref<Obj> = shallowRef({
name: "小满"
})
const changeMsg = () => {
message.value.name = '大满'
triggerRef(message)
}
</script>
<style>
</style>
案例四
自定义ref
customRef 是个工厂函数要求我们返回一个对象 并且实现 get 和 set
<script setup lang="ts">
import { Ref, shallowRef, triggerRef, customRef } from 'vue'
function Myref<T>(value: T) {
return customRef((track, trigger) => {
return {
get() {
track()
return value
},
set(newVal: T) {
console.log('set');
value = newVal
trigger()
}
}
})
}
let message = Myref('小满')
const changeMsg = () => {
message.value = '大满'
// triggerRef(message)
}
</script>
toRef、toRefs、toRaw
toRef
toRef是将对象中的某个值转化为响应式数据 toRef(obj,key)
toRef定义原始数据非响应式数据
toRef 如果原始对象是非响应式的就不会更新视图 数据是会变的
<template>
<div>
<button @click="change">change</button>
<div>{{ obj }}</div>
</div>
</template>
<script setup lang="ts">
import { toRef, reactive } from 'vue'
const obj = {
num: 1,
count: 2,
}
let state = toRef(obj, 'num')
// toRef 如果原始对象是非响应式的就不会更新视图 数据是会变的
// toRef 如果原始对象是响应式时,原始数据,和copy的数据 都会产生影响,视图也发生变化
const change = () => {
state.value = 3
obj.count = 3
console.log('原始obj', obj)
console.log('引用state', state)
}
</script>
原始数据非响应式数据效果

toRef定义原始数据为响应式数据
toRef 如果原始对象是响应式时,原始数据,和copy的数据 都会产生影响,视图也发生变化
<template>
<div>
<button @click="change">change</button>
<div>{{ obj }}</div>
</div>
</template>
<script setup lang="ts">
import { toRef, reactive } from 'vue'
const obj = reactive({
num: 1,
})
let state = toRef(obj, 'num')
// toRef 作用:就是对原始数据,和copy的数据 都会产生影响 若是使用reactive定义的复杂数据,也是会联动视图
const change = () => {
state.value = 3
console.log('原始obj', obj)
console.log('引用state', state)
}
</script>
效果

toRefs 可批量创建响应式数据
可以帮我们批量创建ref对象主要是方便我们解构使用
也就是对对象解构时,解构出来的值为非响应式数据,但使用 toRefs 定义后 这些被解构出来的值 为响应式数据
<template>
<div>
<button @click="change">change</button>
<div>{{ obj }}</div>
</div>
</template>
<script setup lang="ts">
import { toRefs, reactive } from 'vue'
const obj = reactive({
num: 1,
count: 2,
})
let { num, count } = obj // 直接结构出来的数据,是非响应式的数据
console.log('num', num, 'count', count)
let { num: num1, count: count1 } = toRefs(obj) // 直接结构出来的数据,是非响应式的数据,通过toRefs定义为响应式数据
console.log('num1', num1, 'count1', count1)
const change = () => {
num = 10
count1.value = 100
console.log('事件num', num)
}
</script>
点击事件后,非响应式的num值变成10,但是在视图中没有更新

toRaw 将响应式对象转化为普通对象
将响应式对象转化为普通对象 数据变化,视图不变化
<template>
<div>
<button @click="change">change</button>
<div>{{ obj }}</div>
</div>
</template>
<script setup lang="ts">
import { toRaw, reactive } from 'vue'
const obj = reactive({
num: 1,
count: 2,
})
let newObj = toRaw(obj)
console.log('newObj', newObj)
const change = () => {
newObj.count = 33
console.log('newObj2', newObj)
}
</script>

markRaw包裹之后的对象,不会转化为响应式数据
使用markRaw包裹之后的对象,永远不能转成响应式数据,即使使用ref包裹。页面不会发生改变 注意点:前提不是先使用 reactive 包裹 当前数据转化为 响应式数据了
效果
点击时候,name值 不修改

reactive全家桶
用来绑定复杂的数据类型 例如 对象 数组
reactive 源码约束了我们的类型

什么是reactive?
reactive是Vue3中提供实现响应式数据的方法 在Vue2中响应式数据是通过defineProperty来实现的 而在Vue3响应式数据是通过ES6的Proxy来实现的
reactive注意点
reactive参数必须是对象(json/arr) 如果给reactive传递了其他对象,默认情况下修改对象,界面不会自动更新,如果想更新,可以通过重新赋值的方式
他是不可以绑定普通的数据类型这样是不允许 会给我们报错
import { reactive} from 'vue'
let person = reactive('sad')

绑定普通的数据类型 我们可以 使用昨天讲到ref
你如果用ref去绑定对象 或者 数组 等复杂的数据类型 我们看源码里面其实也是 去调用reactive
使用reactive 去修改值无须.value
reactive 基础用法
<template>
<div>{{ job.type }}</div>
<div>{{ arr }}</div>
</template>
<script>
import { reactive } from 'vue' // 一定要引入reactive
export default {
steup() {
let job = reactive({ // reactive可以是对象数据类型
type: '工程师',
money: 4000
})
let arr = reactive(['数据1','数据2']) // reactive也可以是数组数据类型
function change(){
job.type = '农名工' // 对象类型的数据直接.
job.money = 5000
arr[0] = '更新数据1' // Vue3中可以实现数组的数据响应式了!
}
return{
job,
arr
}
}
}
</script>
当要初始化一个空对象时,使用reactive最好先将里面的结构一起定义好;或者使用ref初始化一个空对象,
<template>
<div>{{ job.type }}</div>
<div>{{ person.name }}</div>
</template>
<script>
import { reactive } from 'vue' // 一定要引入reactive
export default {
steup() {
let job = reactive({ // 先定义好reactive里面的结构
type: '',
money: 0
})
let person = ref({})
function change(){
job.type = '农名工' // 对象类型的数据直接.
job.money = 5000
arr[0] = '更新数据1' // Vue3中可以实现数组的数据响应式了!
}
function newPerson() {
name: '张三',
age: 18
}
return{
job,
person
}
}
}
</script>
数组异步赋值问题
这样赋值页面是不会变化的因为会脱离响应式
let person = reactive<number[]>([])
setTimeout(() => {
person = [1, 2, 3]
console.log(person);
},1000)
解决方案1
使用push
import { reactive } from 'vue'
let person = reactive<number[]>([])
setTimeout(() => {
const arr = [1, 2, 3]
person.push(...arr)
console.log(person);
},1000)
方案2
包裹一层对象
type Person = {
list?:Array<number>
}
let person = reactive<Person>({
list:[]
})
setTimeout(() => {
const arr = [1, 2, 3]
person.list = arr;
console.log(person);
},1000)