前言
在上一篇
的文章中,我们了解了vue的ref和reactive响应式引用的用法
,本章节中将会继续学习compositionAPI
中的知识点toRef和setup的context参数
。
toRef
之前的文章中,我们学过一个类似的方法toRefs
,这两者的区别不仅是多了个s
而已,两者的用法也是不一样的哦。
🔥 toRefs
是可以用来解构一个reactive
函数包裹的对象,解构出来的值可以直接返回给模板并直接渲染出来。
const app = Vue.createApp({ template: ` <div>{{name}}</div> `, setup(props, context){ let { reactive, toRefs } = Vue let data = reactive({name: 'JueJin'}) const {name} = toRefs(data) setTimeout(() => { name.value = 'Hello World' }, 2000) return{ name } } }); 复制代码
如果此时我们需要在data
中解构出一个没有定义过的值age
,会有怎样的结果呢?
const app = Vue.createApp({ template: ` <div>{{name}} - {{age}}</div> `, setup(props, context){ let { reactive, toRefs } = Vue let data = reactive({name: 'JueJin'}) const {name, age} = toRefs(data) setTimeout(() => { name.value = 'Hello World' age.value = 18 }, 2000) return{ name, age } } }); 复制代码
- 页面上并不会渲染出
age
这个值,而且浏览器控制台也会报错,提示我们无法设置未定义的属性
此时我们就不需要使用toRefs
去解构了,可以使用一个新的函数toRef
🔥 toRef
是用来设置未定义的属性值的,当需要解构的对象中没有定义某个值,而我们需要去修改某个值的时候,就可以使用toRef
。
const app = Vue.createApp({ template: ` <div>{{age}}</div> `, setup(props, context){ let { reactive, toRef } = Vue let data = reactive({name: 'JueJin'}) // 此处是定义了一个age,并从data中取出 const age = toRef(data, 'age') setTimeout(() => { age.value = 18 }, 2000) return{ age } } }); 复制代码
- 通过
toRef
函数尝试从data
中取出age
,如果可以取到就直接赋值给定义的age
,如果取不到值就给一个默认空的响应式数据值。
- 现在从浏览器中会发现是可以正常渲染出未定义的
age
值的。
📢 但是此处不建议这样去使用,如果大家要去修改某个值,最好是在对象中定义好一个默认值,然后再去修改。
context
在setup
函数中会接收两个参数:props
和context
。
之前的内容中我们学过这两个参数的作用,props
是外部传递过来的参数,context
是上下文的数据。
外部传递什么,props
里面就接收什么,所以下面的内容中我们主要去学习一下context
。
<script> const app = Vue.createApp({ template: ` <child /> ` }); app.component('child', { template: `<div>child</div>`, setup(props, context){ const { attrs, slots, emit} = context return {} } }) const vm = app.mount('#root'); </script> 复制代码
context
中有三个参数:attrs
、slots
和emit
attrs
我们可以先把attrs
打印出来看看里面有些啥。
console.log('attrs:', attrs) 复制代码
- 打印出来之后会发现好像并没有什么特别的东西显示出来。
❓ 那attrs
是什么呢?
❗️ attrs
就是一个None-Props
属性
const app = Vue.createApp({ template: ` <child app='NoneProps' /> ` }); 复制代码
- 父组件通过标签的形式向子组件传递了一个参数
app
。
- 子组件正常情况下是通过
props:['app']
这种方式去接收的。
- 如果子组件不接收的话,那这个
app
就是一个None-Props
属性了。
此时我们去打印一下attrs.app
,看看会打印出什么值出来。
console.log('attrs:', attrs.app) 复制代码
- 通过浏览器控制台会发现,可以正常打印出父组件通过标签传递的
None-Props
属性app
的值。
slots
slots
就是以前的文章中我们学过的插槽的概念,父组件在子组件标签里面传递DOM节点给子组件渲染出来。
<script> const app = Vue.createApp({ template: ` <child> <div>父组件内容</div> </child> ` }); app.component('child', { template: `<div>child</div>`, setup(props, context){ const { attrs, slots, emit} = context console.log('slots:', slots) return {} } }) const vm = app.mount('#root'); </script> 复制代码
- 此时页面上渲染的还是子组件的内容,并没有把父组件里面传递过来的DOM元素渲染出来。
- 通过打印
slots
,可以看出里面是有一个default
函数的。
那我们再把default
函数打印出来看看里面是啥东西。
console.log('slots:', slots.default()) 复制代码
✋🏻 打印出来的内容是一个虚拟DOM,那我们是不是可以在子组件中直接返回这个虚拟DOM呢?
app.component('child', { template: `<div>child</div>`, setup(props, context){ const { h } = Vue const { attrs, slots, emit} = context console.log('slots:', slots.default()) return () => h('div', {}, slots.default()) } }) 复制代码
在setup
函数中通过h
函数(vue底层中的创建虚拟DOM的一个函数,由于用到的地方不对,所以没有具体学习和讲解
)创建一个虚拟DOM,并将插槽的默认函数slots.default()
返回到外部即可。
emit
emit
就是以前的文章中我们学过的子组件触发父组件方法的概念,当我们点击子组件元素时,会触发this.$emit()
函数然后调用父组件中的事件方法。
在setup
函数中要怎么去触发父组件的事件方法呢?
<script> const app = Vue.createApp({ methods: { handleChange(){ console.log('触发事件') } }, template: ` <child @change="handleChange" /> ` }); app.component('child', { template: `<div @click='handleClick'>child</div>`, setup(props, context){ const { attrs, slots, emit} = context function handleClick(){ emit('change') } return { handleClick } } }) const vm = app.mount('#root'); </script> 复制代码
- 在子组件中的模板元素上定义一个点击事件,并调用
setup
函数中的事件方法。
- 事件方法中通过
emit
函数,传递一个事件名称,并通过return
返回给外部。
- 父组件中接收
emit
中的事件名称,然后调用父组件中自己的函数。
- 点击页面子组件内容时,就会打印出父组件中的
console.log()
总结
本篇文章中主要和大家一起学习了toRef
函数和setup
函数中的content
参数。
在content
参数中有三个值:attrs
、slots
和emit
,这三个值都和以前学过的内容有关,只是通过compositionAPI
中的语法变得更加简介。
由于本篇文章中的内容比较复杂,也是理解setup
函数的关键知识点,所以希望大家多多练习,大家加油💪🏻💪🏻