Vue3使用setup、ref和reactive
1. 初识setup函数
- 理解: 它是Vue3中新的一个配置项, 值为函数
- setup是所有的Composetion API(组合API)"表演的舞台"
- 组件中所用到: 数据方法等等,均要配置到setup中。
- setup函数的两种返回值:
- 若返回一个对象,则对象中的属性、方法,在模版中局可以直接使用。(重点关注!)
- 若返回一个渲染函数:则可以自定义渲染内容。(了解)
- 注意点:
- 尽量不要与Vue2.x配置混用,有下面奇怪的情形出现:
- Vue2.x配置(data,methods、computed...)中可以访问到setup中的属性,方法。
- 但在setup中不能访问到Vue2.x配置(data, methods、computed...)。
- 如果有重名,setup优先
- setup不能是一个async函数,因为返回值不再是return的对象,而是promise, Vue不会自动解析then中的值,自然模版看不到return对象中的属性。
提示
例外:用到
<Suspense>和异步组件配合使用时可以return一个Promise实例对象,即此时是异步的 - 尽量不要与Vue2.x配置混用,有下面奇怪的情形出现:
2. setup函数深入理解
- setup执行的时机:在beforeCreate之前执行一次,this的值是undefined
- setup的参数:
- props: 值为对象,包含组件外部传递过来,且组件内部声明接收了的属性
- context(上下文对象):
- attrs: 值为对象,包含组件外部传递过来,但没有在props配置中声明的属性
- slots: 收到的插槽内容,相当于
this.$slots - emit: 分发自定义事件的函数, 相当于
this.$emit
在Vue3中:
js
<script>
export default {
name: 'Demo',
beforeCreate(){
console.log('------------beforeCreate-------------')
},
// Vue3中提供两个参数方便调用,避免没有this使用影响,需要重点关注props和context
setup(props, context){
console.log('------------setup-------------', this);
}
}
</script>执行结果:

提示
因此在Vue3中不用关心是否能写箭头函数了,因为已经没有了this,箭头函数中现在不用写this避免undefined出现。
- context参数
attrs源自Vue2的$attrs, context参数slots源自Vue2中的$slots
2.1 回顾Vue2中的attrs和slots
$attrs用来保存父组件传入子组件的属性信息,子组件$attrs会自动接收,它和props互斥,在props中定义的会在$attrs移除
在Vue2中About.vue代码如下:
vue
<template>
<div>
<About msg="早上好" name="jack">
<span>在广州打工</span>
<span>在成都打工</span>
</About>
</div>
</template>可以查看控制台打印About.vue的实例对象查看上面的属性: 其中$slots中存放了两个<span>的虚拟节点, default表示为默认插槽。
带名字的插槽传入
vue
<template>
<div>
<About msg="早上好" name="jack">
<template v-slot:test1>
<span>在广州打工</span>
</template>
<template v-slot:test2>
<span>在成都打工</span>
</template>
</About>
</div>
</template>
可见出现两个带有名称的插槽在$slots上面。
2.2 分析props对象
在Vue3中父组件App.vue传入的数据:"你好啊"
vue
<template>
<Demo hello="你好啊"></Demo>
</template>
<script>
import { ref, reactive} from 'vue'
import Demo from './components/Demo.vue'
export default {
name: 'App',
components: {Demo},
setup(props, context) {
}
}
</script>
vue
<template>
<div>{{ hello }}</div>
</template>
<script>
import { ref } from 'vue'
export default {
name: 'Demo',
setup(props, context) {
console.log('------------setup-------------', props);
}
}
</script>
{{ hello }}
txt
运行警告消失, props数据出来了:

### 2.2 分析context对象
打印context对象如下,有四大属性attrs、emit、expose、slots

<div class="vp-code-group"><div class="tabs"><input type="radio" name="cg143" id="cg143-0" checked><label for="cg143-0">Demo.vue</label><input type="radio" name="cg143" id="cg143-1"><label for="cg143-1">App.vue</label></div><div class="blocks"><div class="language-vue vp-adaptive-theme"><span class="lang">vue</span><pre class="shiki "><code><span class="line"><template>
</span>
<span class="line"> <div>{{ hello }}</div>
</span>
<span class="line"> <button @click="invokeWalk">触发自定义事件</button>
</span>
<span class="line"> <slot></slot>
</span>
<span class="line"></template>
</span>
<span class="line">
</span>
<span class="line"><script>
</span>
<span class="line">import { ref } from 'vue'
</span>
<span class="line">export default {
</span>
<span class="line"> name: 'Demo',
</span>
<span class="line"> props: ['hello'],
</span>
<span class="line"> emits: ['walkCode'], // Vue3需要在子组件中申明使用自定义函数,不然会有警告
</span>
<span class="line"> setup(props, context) {
</span>
<span class="line"> console.log('------------context-------------', context)
</span>
<span class="line"> console.log('attrs', context.attrs); // 由于配置props: ['hello'],attrs中也就没有了hello值
</span>
<span class="line"> console.log('slots', context.slots); // slots不会随着子组件中配置了<slot></slot>,就会消失里面的值
</span>
<span class="line"> console.log('emit', context.emit);
</span>
<span class="line"> let invokeWalk = ()=>{
</span>
<span class="line"> context.emit('walkCode', 'jack')
</span>
<span class="line"> }
</span>
<span class="line"> return {
</span>
<span class="line"> invokeWalk
</span>
<span class="line"> }
</span>
<span class="line"> }
</span>
<span class="line">}
</span>
<span class="line"></script>
</span></code></pre></div><div class="language-vue vp-adaptive-theme"><span class="lang">vue</span><pre class="shiki "><code><span class="line"><template>
</span>
<span class="line"> <Demo hello="你好啊" @walkCode="goWalkCode">
</span>
<span class="line"> <span>我是自定义Html代码</span>
</span>
<span class="line"> </Demo>
</span>
<span class="line"></template>
</span>
<span class="line">
</span>
<span class="line"><script>
</span>
<span class="line"> import { ref, reactive} from 'vue'
</span>
<span class="line"> import Demo from './components/Demo.vue'
</span>
<span class="line"> export default {
</span>
<span class="line"> name: 'App',
</span>
<span class="line"> components: {Demo},
</span>
<span class="line"> setup(props, context) {
</span>
<span class="line"> let goWalkCode=(name)=>{
</span>
<span class="line"> alert(`继续搞java, ${name}`)
</span>
<span class="line"> }
</span>
<span class="line"> return {
</span>
<span class="line"> goWalkCode
</span>
<span class="line"> }
</span>
<span class="line"> }
</span>
<span class="line">}
</span>
<span class="line"></script>
</span></code></pre></div></div></div>
### 2.3 setup返回数据误区
1. 不具有响应式,只在第一次页面正常显示,点击按钮页面不变。
```js
return {
name: person.name, // [!code error]
age: person.age, // [!code error]
salary: person.job.j1.salary, // [!code error]
}- 虽然具有响应式,但是数据已经没有和person关联,person的数据不再受到变化,响应式的数据只是return产生的新数据,不能在返回数据中加入ref。
js
return {
name: ref(person.name), // [!code error]
age: ref(person.age), // [!code error]
salary: ref(person.job.j1.salary) // [!code error]
}3. ref函数
不同于Vue2中的ref类似id的效果,获取组件实例对象,Vue3中的ref是一个函数
- 作用: 定义一个响应式的数据
- 语法:
const xxx = ref(initValue)
- 创建一个包含响应式数据的引用对象(reference对象)
- JS中操作数据: xxx.value
- 模版中数据读取:不需要.value得到值,直接
{{xxx}}即可,Vue3读取模版插值的时候会判断是RefImpl类型自动读取value属性。 - 接收的数据可以是: 基本类型、也可以是对象类型。
- 基本类型的数据: 响应式依然是靠Object.defineProperties()的get与set的实现, 是RefImpl类型数据
- 对象类型的数据: 内部"求助"了Vue3.x中的新函数--reactive函数, 是Proxy类型数据
4. reactive函数
- 作用: 定义一个对象类型的响应式数据(基本类型不要使用它,要用ref函数)
- 语法: const 代理对象 = reactive(源对象)接收一个对象(或数组), 返回一个代理对象,返回一个代理对象(Proxy的实例对象, 简称proxy对象)
- reactive定义的响应式数据是深层次的
- 内部基于ES6的proxy实现,通过代理对象操作源对象内部数据进行操作。
5. 对比ref和reactive
- 从定义数据角度对比:
- ref用来定义: 基本类型数据。
- reative用来定义: 对象(或数组)类型数据
- 备注: ref也可以定义对象(或数组)类型数据, 它内部会自动reactive转为代理对象。
- 从原理角度对比:
- ref通过Object.defineProperty()的get与set来实现响应式(数据劫持)
- reactive通过使用Proxy来实现响应式(数据劫持), 并通过Reflect操作源对象内部的数据
- 从使用角度对比:
- ref定义的数据: 操作数据需要.value, 读取数据时模版中直接读取不需要.value
- reactive定义的数据:操作数据与读取数据: 均不需要.value

评论