Vue3之setup()、reactive、ref
概述
在Vue3中,将Vue2.0的option API 制作成hook函数,比如watch,compute
方法等,在Vue3中新增的setup()
方法中,可以以函数的形式去使用Vue2.0的watch,compute
等属性,setup
函数的调用时机是创建组件实例,然后初始化props
,紧接着是调用setup
函数,从生命周期的角度来看,它会在beforeCreate
之前调用,所以在setup
函数内是拿不到上下文对象的,它创建的是data
和method
.
而reactive、ref
是两个响应式引用,用它们修饰的变量或者是对象会具有响应式的效果,ref
主要用于处理基础类型的数据,如字符串,数字等,而reactive
处理非基础类型的数据,比如自定义的对象就可以用reactive
修饰,使其具有响应式的效果
实例解析
在Vue3中,
setup
函数主要负责完成组件内部的状态管理,响应式数据的设置,引入第三方库,组合函数等操作,setup
函数接收两个参数,分别是props
和context
, 其中props
是组件的属性对象,context
是一个包含了一些实用的对象和方法的上下文对象。setup需要返回一个对象,这个对象包含了组件中需要使用的所有数据和方法等。
响应式API ref()和reactive()
我们可以看一个简单的例子,使用setup
函数和ref
定义一个变量展示一个name
,2秒后自动改变name
的内容。代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>setUp、ref、reactive的使用</title>
<script src="https://unpkg.com/vue@next"></script>
</head>
<body>
<div id="root"></div>
</body>
<script>
const app = Vue.createApp({
template:`<div>{{name}}<div>`,
setup(props,context){
const {ref} = Vue;
const name = ref('zhongxj');
setTimeout(()=>{
name.value = 'walt';
},2000);
return {name}
}
});
const vm = app.mount('#root');
</script>
在上面的代码中我们需要注意的是,使用ref之前需要使用
const {ref} = Vue;
来导入Vue对象的ref
函数,然后使用时用ref
包含起来的值就会自动带有响应式的效果,如上面的例子中,先是定义了name的
值为‘zhongxj’
,过来2秒,将值修改成‘walt’
时,页面中的展示的值也会自动跟着改变。
说完setup和ref函数的使用,我们在本文的开始还提到了reactive函数,这个函数的作用和ref的作用是一样的,都是为了给修饰的值赋予响应式的效果,只是reactive主要修饰的是非基础类型的数据,如下面代码所示:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>setUp、ref、reactive的使用</title>
<script src="https://unpkg.com/vue@next"></script>
</head>
<body>
<div id="root"></div>
</body>
<script>
const app = Vue.createApp({
template:`<div>{{nameObj.name}}<div>`,
setup(props,context){
const {reactive} = Vue;
const nameObj = reactive({name:'zhongxj'});
setTimeout(()=>{
nameObj.name = 'walt';
},2000);
return {nameObj}
}
});
const vm = app.mount('#root');
</script>
从上面的代码中可以知道,可以使用reactive去修饰一个对象,从而让这个对象具有响应式的能力。ref和reactive这两个响应式API的原理是通过代理的方式实现的,通过proxy对数据进行封装,当数据变化时,触发模版等内容的更新 。比如:
// 基础数据类型
let name = ref('zhong');
//会将 'zhong' 变成proxy({value:'zhong'}) 这样的响应式引用
//非基础数据类型
const nameObj = reactive({name:'xcy'});
// 会将{name:'xcy'} 变成proxy({name:"xcy"})的一个响应式引用
toRef()、toRefs()使数据具有响应式能力
我们普通的对象是不具有响应式能力的,如果要想让它具备响应式能力,我们必须转化一下,Vue提供了toRef()和toRefs() API可以将普通对象转换成具有响应式能力的对象。 首先要介绍的式toRef()的使用,先看一段代码。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>toRef使用</title>
<script src="https://unpkg.com/vue@next"></script>
</head>
<body>
<div id="root"></div>
</body>
<script>
const app = Vue.createApp({
template:
`
<div> {{age}}</div>
` ,
setup(props,context){
const {reactive,toRef} = Vue;
const data = reactive({name:'walt',age:'18'});
const age = toRef(data,'age');
setTimeout(()=>{
age.value='22'
},2000);
return{age};
}
});
const vm = app.mount('#root');
</script>
上面的代码展示了toRef方法的使用,我们首先定义了一个data的响应式数据,但是我们只想返回age给到界面展示,这时候我们新定义了一个age变量,如果不用toRef()函数转换一下,我们的age是不具备响应式能力的。加了toRef后,age就带有响应式能力了。这里还有一个细节,假设我们的age是原来对象中没有的属性,也可以转。如下所示:
setup(props,context){
const {reactive,toRef} = Vue;
const data = reactive({name:'walt'});// 没有age属性
const age = toRef(data,'age');// 将age转成响应式的变量
setTimeout(()=>{
age.value='22'
},2000);
return{age};
}
上面的代码也是没有问题的。但是我们发现此时我们转换的都是单一的属性,假设我们需要将整个对象转成响应式的就需要使用toRefs()函数,具体的使用方法如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>toRefs的使用</title>
<script src="https://unpkg.com/vue@next"></script>
</head>
<body>
<div id="root"></div>
</body>
<script>
const app = Vue.createApp({
template:
`
<div>{{name}}</div>
<div>{{age}}</div>
`,
setup(props,context){
const {reactive ,toRefs} = Vue;
const nameObj = reactive({name:'walt',age:29,sex:'male'});
setTimeout(()=>{
nameObj.name = 'zhongxj';
nameObj.age = 28;
},2000);
const {name,age} = toRefs(nameObj);
return {name,age}
}
});
const vm = app.mount('#root');
</script>
如上面代码所示,我们定义了一个nameObj对象,然后使用toRefs()方法将其转变成立响应式的对象,当我们在2秒后改变了对象内的name,age属性时,界面上的name和age属性也会随之跟着改变。
最后简单介绍下setUp()函数中的context参数,如下面代码所示:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>toRef和context参数使用</title>
<script src="https://unpkg.com/vue@next"></script>
</head>
<body>
<div id="root"></div>
</body>
<script>
const app = Vue.createApp({
// template:
// `
// <child app='zhongxj' />
// ` ,
// template:
// `
// <child>
// parent
// </child>
// ` ,
template:`<child @change="handleClick"></child>`,
methods:{
handleClick(){
alert('change')
}
},
setup(props,context){
const {reactive,toRef} = Vue;
const data = reactive({name:'walt'});
return{data};
}
});
app.component('child',{
// template:
// `
// <div>child</div>
// ` ,
template:`<div @click="handleClick">hello world </div>`,
setup(props,context){
const{attrs,slots,emit} = context;
const {h} = Vue;
// 父组件传递过来的None-Props属性,子组件不用props['app']
//的方式接收的话就叫做Nons-props属性
// console.log(attrs.app);
// console.log(slots.default());
// 可以使用render的h函数渲染父组件传递过来的插槽内容
// return () => h('div',{},slots.default());
function handleClick(){emit('change')}
return{
handleClick
}
}
});
const vm = app.mount('#root');
</script>
上面的代码中最主要的就是const{attrs,slots,emit} = context;
这段代码,即从context中可以拿到父组件传递过来的None-props属性、插槽、emit。然后可以实现很多灵活的需求,比如本例中的事件通信,具体的读者可以打开代码中的注释体验。
注:子组件不用props['app']的方式接收的话就叫做Nons-props属性
总结
本文主要介绍了Vue3的setUp函数和响应式的API toRef(),toRefs(),以及其使用的方法,本文的介绍其实只是入门的知识,只是为了抛砖引玉,读者在简单入门后若要深刻了解请移步官网或者其他大牛的博客。
转载自:https://juejin.cn/post/7232326110837981242