一、setup基本用法
1、setup()-组合API的入口函数
- 为什么要用setup?
在实际开发中,为了实现一个功能,其逻辑可能需要data,computed,methods, watch等几项共同协作才能实现,当组件变得更大时,逻辑关注点就会变得碎片化,不利于阅读和维护,因此在处理单个逻辑关注点时,将这些相关的代码配置在一起,就是setup的目的。 - 怎么用?
调用setup(),所有的组合API函数都在此使用,只在初始化时执行一次
setup()如果返回对象,对象中的属性或方法,模板中可以直接使用
<template>
<div>{{count}}</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
name: 'App',
setup(){
const count = 10
return {
count
}
}
});
</script>
但直接在setup()中声明的变量并不是响应式的,需要使用ref()函数。
2、ref()
ref是一个函数,其作用为:定义一个基本类型的响应式数据
返回值是一个reference对象,对象中有一个value属性,用于保存该响应式数据的值
DOM模板中不需要使用.value属性的写法
<template>
<div class="btn" @click="updateCount">update</div>
<!-- DOM中使用ref定义的响应式数据时不需要加.value -->
<div>{{count}}</div>
</template>
<script lang="ts">
//1、引入ref
import { defineComponent, ref } from 'vue'
export default defineComponent({
name: 'App',
setup() {
//2、使用ref(),将count定义为一个响应式数据,参数是初始值
const count = ref(0)
function updateCount() {
//使用数据时,调用该ref对象的value属性
count.value++
}
return {
count,
updateCount
}
}
})
</script>
3、reactive()
当在setup中需要声明一个响应式的对象数据时,使用reactive()
<template>
<div class="btn" @click="update">update</div>
<div>
<p>name: {{user.name}}</p>
<p>age: {{user.age}}</p>
</div>
</template>
<script lang="ts">
//1、引入reactive
import { defineComponent, reactive } from 'vue'
export default defineComponent({
name: 'App',
setup() {
const obj = {
name: 'Liane',
age: 18
}
//2、调用reactive(),将目标对象作为参数传入
const user = reactive(obj)
console.log(user) //Proxy {name: "Liane", age: 18}
function update() {
user.age++
}
return {
user,
update
}
}
})
</script>
reactive()接收一个普通对象,然后返回该普通对象的响应式代理器对象。
内部基于ES6的Proxy实现,通过代理对象操作源对象,内部数据都是响应式的。
二、setup详解
1、setup执行时机
- 在beforeCreate执行之前执行,此时组件对象还没有创建
- this是undefined,不能通过this来访问data/computed/methods/props
- 所有的compositionAPI相关的回调函数中也不可以使用this
<template>
<h2>Chile{{msg}}</h2>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'App',
props: ['msg'],
//我们发现setup会在beforeCreate之前执行
//this打印为undefined
beforeCreate() {
console.log('beforeCreate')
},
setup() {
console.log('setup')
console.log(this) //undefined
}
})
</script>
2、setup的返回值
- 一般都是返回一个对象,为模板提供数据,就是模板中可以直接使用此对象中所有属性/方法
- 返回对象中的属性会与data函数返沪对象的属性合并成为组件对象的属性
- 返沪对象中的方法会与methods中的方法合并成组件对象的方法
- 若有重名,setup优先
<template>
<h2>Chile{{msg}}</h2>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue'
export default defineComponent({
name: 'App',
props: ['msg'],
data() {
return {
a: 0
}
},
setup() {
const b = ref(1)
const fun1 = () => {
return 'fun1'
}
return {
b,
fun1
}
},
methods: {
fun2() {
return 'fun2'
}
},
mounted() {
console.log(this)
}
})
</script>
注意:
- 一般不用混合使用:methods中可以访问setup提供的属性和方法,但在setup中不能访问data和methods
- setup不能是一个async函数:因为返回值不再是return的对象,而是promise,模板就看不到return的属性数据了
3、setup的参数
setup(props,context)
setup(props,{attrs,slots,emit})
- props参数:包含组件props配置声明且传入了的所有props的对象
- attrs参数:包含没有在props配置中声明的属性对象,相当于this.$attrs
- slots参数:包含所有传入的插槽内容的对象,相当于this.$slots
- emit参数:可以用来分发一个自定义事件,相当于this.$emit
定义一个子组件Child.vue
<template>
<slot></slot>
<h2 @click="fun">Child{{msg}}</h2>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue'
export default defineComponent({
name: 'App',
props: ['msg'],
setup(props, { attrs, emit }) {
console.log(props) //Proxy {msg: 0}
console.log(attrs) //Proxy {text: "child", __vInternal: 1, onClickChild: ƒ}
const fun = (n: number) => {
//使用emit分发一个自定义事件
emit('clickChild', 2)
}
return {
fun
}
}
})
</script>
在App中使用Child组件
<template>
<div class="btn" @click="update">update</div>
<!-- prop attrs 自定义事件 -->
<Child :msg="msg" text="child" @clickChild="changeChild" />
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue'
import Child from './components/Child.vue'
export default defineComponent({
name: 'App',
components: {
Child
},
setup() {
const msg = ref(0)
const update = () => {
msg.value++
}
//child组件自定义事件
const changeChild = (n: number) => {
msg.value += n
}
return {
msg,
update,
changeChild
}
}
})
</script>
三、reactive和ref
- 都是Vue3组合API中2个最重要的响应式API
- ref用来处理基本类型数据,reactive用来处理对象(递归深度响应式)
- 如果用ref处理对象或数组,内部会自动将对象/数组转换为reactive的代理对象
- ref内部:通过给value属性添加getter/setter来实现对数据的劫持
- reactive内部:通过使用proxy来实现对对象内部所有数据的劫持,并通过Reflect反射操作对象内部数据
- ref的数据操作:在js中使用ref对象.value获取数据,在模板中可直接使用
<template>
<div class="btn" @click="add">add</div>
<div>name: {{user1.name}}
<br>age:{{user1.age}}</div>
<div>name: {{user2.name}}
<br>age:{{user2.age}}</div>
</template>
<script lang="ts">
import {defineComponent,ref,reactive} from 'vue'
export default defineComponent({
name:'App',
setup(){
const user1 = ref({
name: 'Rose',
age: 16
})
const user2 = reactive({
name: 'Jack',
age: 18
})
const add = ()=>{
//使用ref创建的响应式对象,通过.value获取数据
user1.value.age ++
//使用reactive创建的响应式对象,可直接获取数据
user2.age ++
}
return {
user1,
user2,
add
}
}
})
</script>
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。