赞
踩
使用 vue 的reactive()
函数。
import {reactive} from 'vue';
//这行代码可以放在<script setup> 或者在 setup() 函数中
const state = reactive({count : 0});
state
就是响应式对象。
在 <script setup>
中的顶层的导入和变量声明,可在同一模板中直接使用。
<template> <!-- 直接使用state.count --> <div>当前的值:{{state.count}}</div> </template> <script setup> import {reactive} from "vue"; const state = reactive({count:0}); //延迟5秒后,count数加1 setTimeout(()=>{ state.count +=1;//直接使用 },5000) </script>
reactive()
创建的响应式对象都是深层的,深层对象或数组也是响应式的,他们的改变都能被监测到。响应式对象内的对象依然是代理。
<template> <!-- 直接使用state.count --> <div>当前的值:{{state.count}}</div> <div>当前的值:{{state.taskInfo.status}}</div> </template> <script setup> import {reactive} from "vue"; const state = reactive({count:0,taskInfo:{status:"未开始"}}); state.taskInfo= {status:"暂停中"};//赋值新对象后,state.taskInfo属性还是响应式的 setTimeout(()=>{ state.count +=1; state.taskInfo.status ="进行中"; },5000) </script>
不会,因为原对象就是一个普通对象,通过reactive()
生成的是一个代理对象,可以理解为有一个对象包裹住这个原对象,这个对象就叫原对象的包装对象,响应性是通过这个包装对象实现的。所以普通对象的属性值改变后不会触发更新。
import {reactive} from "vue"; const obj = {count:0,taskInfo:{status:"未开始"},username:['张三','李四']}; const state = reactive(obj); //obj.count = 15;如果在这里改值的话 界面上显示的值直接就变成15了 ?? setTimeout(()=>{ obj.count = 5;//改变原始对象的属性值 obj.taskInfo = {status:"暂停中"} obj.username =['马冬梅'] console.log(obj.count) console.log(obj.taskInfo) console.log(obj.username) console.log(state.count)//响应式对象的值也变了,输出的是5,但是不会触发DOM刷新,界面上显示还是原来的值 0,下同 console.log(state.taskInfo) console.log(state.username) },5000)
reactive()
函数存在一些局限使用ref()
可以创建任何值类型的响应式ref
,ref()
函数会将传入的值包装成一个带有value
属性的ref
对象,value
属性的值就是传入的值。当传入的值是对象,将会调用reactive()
自动转换他的.value
。ref
能让我们创造一种对任意值得引用,并且在不丢失响应性的前提下传递这些引用。这是因为传递的都是这个ref
对象。
import {ref} from "vue";
const btnState = ref(0)
setTimeout(()=>{
btnState.value = 1
},5000)
ref
对象呢?在模板中属于顶层属性时就不需要解包,他们会自动解包。如果不是顶层属性则不行。我们可以通过解构把他们变为顶层属性。
<template> <!-- 必须得加上value 不然显示的是[Obejct Object]1,而不是李四1--> 用户名:{{obj2.username.value+'1'}} <!-- 这个就不需要加value了 ,因为username本身就是顶级属性了--> 用户名:{{username+'1'}} </template> <script setup> import {ref} from "vue"; const obj2 = {username:ref('张三')} setTimeout(()=>{ obj2.username.value = '李四' },5000) const {username} = obj2; </script>
当这个ref
对象在深层响应式对象中,作为属性被访问时也会自动解包,就像普通对象属性一样。但是浅层响应式对象则不行。
<template>
<!-- 这个就不需要加value了 ,因为是在深层响应式对象中的一个属性,自动解包-->
年龄:{{obj3.age+10}}
</template>
<script setup>
import {ref} from "vue";
const obj3 = reactive({age:ref(20)})
setTimeout(()=>{
obj3.age = 50;//不用加value 自动解包
},5000)
</script>
当ref
对象是响应式数组的一个元素时,使用这个ref
对象也不会自动解包,需要加上value
ref
对象要加一个value
会很麻烦。我们可以使用语法糖来替换。将ref()改为$ref()即可。
const btnState = $ref(0)
const obj2 = {username:$ref('张三')}
要使用语法糖,需要显示启用。
vite.config.js
需要 “@vitejs/plugin-vue”>=“^2.0.0”,
export default defineConfig({
plugins: [
vue({
reactivityTransform:true,//显示启用语法糖,配置完之后要重启
}),
]
});
setup()
基本使用setup()
是一个钩子函数,是在组件中使用组合API的入口,当我们在选项式API的组件中使用组合API时需要这个函数,组合API都要写在这个函数内。我们在这个函数内使用组合API声明响应式状态,比如使用ref()
、reactive()
等函数,然后在这个函数返回的对象会暴露这些响应式状态给模板和组件实例。其他的选项可以通过组件实例访问了。
<script> import {ref} from 'vue'; export default { setup(){ const username = ref('张三'); return { username//在调用这个属性不用加value,因为在暴露的时候自动解包了 } }, methods:{ getName(){ console.log(this.username);//使用this就可以直接调用暴露的属性, return this.username; } }, mounted(){ this.getName(); } } </script> <template> <div>{{username}}</div> </template>
setup()
函数有参数 ,第一个参数是组件的props
。和标准组件一致,这个props
也是响应式的,在传入新的props
时会同步更新。如果解构了props
对象,那么解构出的变量就会失去响应性,所以推荐使用props.xxx
的形式使用props
.
export default{
props:{
username,
},
setup(props){
console.log(props.username);
}
}
如果需要解构props
,或者将某个prop
传到外部函数并保持响应性,可以使用toRefs()
或toRef()
函数。
export default{
props:{
username,
},
setup(props){
const {username} = toRefs(props);
//username 是一个ref对象,value是props.username
}
}
第二个参数是一个Setup上下文对象,这个对象暴露了一些可能在setup
中用到的值。上下文对象是非响应式的,可以解构。attrs
和 slots
都是有状态的对象,它们总是会随着组件自身的更新而更新。attrs
和 slots
的属性都不是响应式的。
export default {
setup(props, context) {
// 透传 Attributes(非响应式的对象,等价于 $attrs)
console.log(context.attrs)
// 插槽(非响应式的对象,等价于 $slots)
console.log(context.slots)
// 触发事件(函数,等价于 $emit)
console.log(context.emit)
// 暴露公共属性(函数)
console.log(context.expose)
}
}
ref()
reactive()
转为具有深层次响应式的对象。ref
对象,此对象内部只有指向内布置的属性value
。所有对value
的操作都将被追踪。computed()
getter
函数,返回一个只读的响应式ref
对象。get
和set
函数的对象,返回一个可写的ref
对象。ref
对象,根据不同参数返回只读或者可写的ref
对象。该ref
通过属性value
暴露getter
函数的返回值。<template> <div>One的值:{{comCountOne}}</div> <div>Two的值:{{comCountTwo}}</div> </template> <script setup> import {computed} from "vue"; let count = $ref(0) //传入一个getter函数 ,返回一个只读的ref对象 const comCountOne = computed(()=>{ return count++; }) //传入一个带有get和set的对象,返回一个可写的ref对象 const comCountTwo = computed({ get(){ return count }, set(newVale){ count=newVale + 5; } }) setTimeout(()=>{ comCountTwo.value = 100 },5000) </script>
reactive()
readonly()
ref
对象watchEffect()
flush:'post'
将会是侦听器延迟到组件渲染之后再执行(watchPostEffect()
函数)。在某些特殊情况下可能需要响应式依赖发生改变时立即触发侦听器,这可以通过flush:'sync'
来实现(watchSyncEffect()
函数)。import {watchEffect} from "vue";
let count = $ref(0)
watchEffect(()=>{
console.log(count);
})
count++;
watch()
watch()
是懒监听,只有当数据源变化时才执行回调函数。
ref
immediate
: 在侦听器创建时立即触发回调。第一次调用时旧值是undefined
deep
: 如果源是对象,强制深度遍历,一遍在深层级变更时触发回调。flush
: 调整回调函数的刷新时机。onTrack
、onTrigger
:调试侦听器的依赖。import {watch,ref,reactive} from "vue"; let one = ref(0)//用$ref()创建的ref对象无法使用watch const state = reactive({count:0}) //监听ref对象 watch(one,(val,oldVal)=>{ console.log("--"+val) console.log("--"+oldVal) }) //一个getter函数 watch(()=>{return state.count},(count,oldCount)=>{ console.log(count) console.log(oldCount) }) //监听多个来源 watch([one,state],([newValOne,newState],[old1,old2])=>{ console.log("-newValOne-"+newValOne) console.log("-newState-"+newState.count) console.log("-old1-"+old1) console.log("-old2-"+old2.count) }) setTimeout(()=>{ one.value = 100; state.count = 1000; },5000)
isRef()
ref
对象true
代表是,否则不是unref()
ref
对象,则返回内部值,否则返回参数本身。toRef()
基于响应式对象上的一个属性,创建一个对应的ref
对象。这样创建的ref
与其源属性保持同步。
ref
对象$toRef()
,提示未声明?<template> <div>addressRef的值:{{addressRef}}</div> <div>user.address的值:{{user.address}}</div> </template> <script setup> import {watch,ref,reactive,toRef} from "vue"; const user = reactive({ username:'张三', address:'长春' }) const addressRef = toRef(user,'address') setTimeout(()=>{ user.address = "松原" },5000) setTimeout(()=>{ addressRef.value = "白山" },10000) </script>
toRefs()
将一个响应式对象转换为一个普通对象,这个普通对象的每个属性都是指向源对象相应属性的ref
对象。每个ref
都是使用toRef()
创建的。toRefs()
在调用时只会为源对象上可以枚举的属性创建ref
,对可能不存在的不会。
const user = reactive({
username:'张三',
address:'长春'
})
const userOfRefs = toRefs(user);
const {usernameRef,addressRef} = userOfRefs;//解构也不会丢失响应性
isProxy()
检查一个对象是否有reactive()
、readonly()
、shallowReactive()
、shallowReadonly()
创建的代理
isReactive()
检查一个对象是否有reactive()
、shallowReactive()
创建的代理
isReadonly()
检查一个对象是否有readonly()
、shallowReadonly()
创建的代理
provide()
提供一个值,可以被后代组件注入。provide()
要和setup()
同步调用。
symbol
ref
对象,后代组价可以由此和提供者建立响应式的联系。import {provide, reactive,ref} from 'vue' const obj = reactive({ user:{ username:'张三', address:'长春' }, task:{ completeNumber:0, craft:{ version:1 } } }) provide('obj',obj) const countRef = ref(0) provide('count',countRef) setTimeout(()=>{ countRef.value = 1000; obj.user.address='成都' obj.task.completeNumber = 100 obj.task.craft.version = 2.4 },5000)
Provide
vue实例也有一个provide()
函数,此函数提供的数据在该应用内的所有组件中都可以注入。
inject()
在当前组件注入上层组件提供的数据。inject()
要和setup()
同步调用。
undefined
或默认值。false
<template> <div>{{countRef}}</div> <div>地址:{{obj.user.address}}</div> <div>数量:{{obj.task.completeNumber + 10 }}</div> <div>版本:{{ obj.task.craft.version}}</div> </template> <script setup> import {inject} from "vue"; const obj = inject('obj') const countRef = inject('count') setTimeout(()=>{ countRef.value = 50000; obj.user.address='武汉' obj.task.completeNumber = 700 obj.task.craft.version = 12.5 },20000) </script>
在上面的例子中,我们可以看到,有两个setTimeout()
函数都直接修改响应式数据了。如果有多个文件都直接修改数据的话,我们对于数据的更改是不确定的,所以尽可能的对于响应式的状态的变更都保持在供给方组件中,这样确保状态的声明和变更操作都内聚在同一个组件中。我们把上面的例子改一改。
根组件:
<script setup> import {provide, reactive,ref,readonly} from 'vue' const obj = reactive({ user:{ username:'张三', address:'长春' }, task:{ completeNumber:0, craft:{ version:1 } } }) provide('obj',readonly(obj))//用readonly包装,这样后代组件就不能直接修改了 let countRef = ref(0) provide('count',readonly(countRef)) setTimeout(()=>{ countRef.value = 1000; obj.user.address='成都' obj.task.completeNumber = 100 obj.task.craft.version = 2.4 },5000) function changeData() { countRef.value = 50000; obj.user.address='武汉' obj.task.completeNumber = 700 obj.task.craft.version = 12.5 } provide('changeData',changeData)//把这个函数提供给后代组件,后代都通过这个方法进行数据更新 </script>
后代组件
const obj = inject('obj')
const countRef = inject('count')
const changeData = inject('changeData')
setTimeout(()=>{
changeData();//调用祖先组件提供更改数据的方法
},20000)
使用字符串作为注入名,可能会引起冲突,使用Symbol
则可以避免这些冲突。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。