1、vue3中的setup有什么用?
setup的设计是为了使用组合式api
2、为什么不用之前的组件的选项
data、computed、methods、watch 组织逻辑在大多数情况下都有效。然而,当我们的组件变得更大时,逻辑关注点的列表也会增长。这可能会导致组件难以阅读和理解,尤其是对于那些一开始就没有编写这些组件的人来说。而通过setup可以将该部分抽离成函数,让其他开发者就不用关心该部分逻辑了.
3、setup的在vue生命周期的位置
setup位于created 和beforeCreated之前,用于代替created 和beforeCreated,但是在setup函数里不能访问到this,另外setup内可以通过以下hook操作整个生命周期onBeforeMount,onMounted,onBeforeUpdate,onUpdated,onBeforeUnmount,onUnmounted,onErrorCaptured,onRenderTracked,onRenderTriggered
4、setup可以接收哪些参数?
setup可接受props,context
,其中props
由于是响应式数据,不能直接解构赋值,context
不是响应式数据,可以直接解构赋值;setup必须返回一个对象,一旦return,就可以像vue2.x的方式使用该属性
props:['test']
setup(props,context){
//const {test} = props //错
const {test} = toRefs(props) //对
const { attrs, slots, emit }= context //对
return {
test
}
}
5、优先级,如果data,props,setup都有一个同名属性,setup返回的该属性优先级最高,以执行以下代码为例,将显示:test from son's setup
//father.vue
...
<custField :test="test" />
setup(){
const test = ref('test from father')
return{
test
}
}
...
//son.vue
<template>
<div class="custField">
优先级测试
<h1>{
{ test }}</h1>
</div>
</template>
<script>
import { toRefs } from "vue";
export default {
props: ["test"],
data() {
return {
test: "test from son's data",
};
},
setup(props) {
let test = toRefs(props);
test = "test from son's setup";
return { test };
},
};
</script>
6、如上代码所示,若要在setup内执行ref,toRefs,toRef,computed,watch,watchEffect等函数,需先引入
import { toRefs, ref, onMounted, nextTick } from "vue";
7、如何在setup中拿到ref对应的子组件,并执行其的函数,场景如下:使用antd的form表单的验证,在vue2.x方案时可以在methods中通过this时需要使用this.$refs.ruleForm.validate()
,而在setup中拿不到this,应该从{ref}入手,看下面代码
//...
<a-form
ref="ruleForm"
:model="form"
:rules="rules"
>
<a-form-item ref="name" label="Activity name" name="name">
<a-input v-model:value="form.name" />
</a-form-item>
<a-form-item :wrapper-col="{ span: 14, offset: 4 }">
<a-button type="primary" @click="onSubmit"> 验证</a-button>
<a-button style="margin-left: 10px" @click="resetForm"> 重置</a-button>
</a-form-item>
</a-form>
//...vue2.x
methods: {
onSubmit() {
this.$refs.ruleForm
.validate()
.then(() => {
console.log('values', this.form);
})
.catch(error => {
console.log('error', error);
});
},
resetForm() {
this.$refs.ruleForm.resetFields();
},
},
//..vue3
setup(){
//1.设置一个 <a-form
// ref="ruleForm"
// :model="form"
// :rules="rules"
//> ref同名属性,并使用ref(null)包装
const ruleForm=ref(null)//通过ref或reactive包裹起来让其成为响应式数据
//2.一旦后面return {ruleForm},vue3会自动绑定ref="ruleForm"的组件
//设定方法,但是要通过ruleForm.value才能拿到组件
const onSubmit=()=>{
ruleForm.value//通过ref包裹的数据需要使用.value来取得相应的值
.validate()//,而reactive包裹的数据不需要通过.value来取得相应的值
.then(() => {
console.log("values", form);
})
.catch((error) => {
console.log("error", error);
});
}
const resetForm = () => {
console.log("resetForm");
ruleForm.value.resetFields();
};
//3.setup必须返回一个对象,把vue在生命周期需要调用的方法,属性暴露出去
return {
ruleForm,//Q:为什么上面要用.value的形式,A:这里会自动解绑
onSubmit,
resetForm
}
}
8、如何调用子组件内setup内的方法?
- 子组件在setup写好方法
method
,并通过return
暴露出去 - 父组件调用子组件时为其添加
ref
属性 - 父组件setup内拿到子组件添加的
ref
属性property
,再通过property.value.method()
调用
子组件
<template>
// 渲染从父级接受到的值
<div>Son: {
{ valueRef }}</div>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue'
export default defineComponent({
name: 'Son',
setup() {
const valueRef = ref('')
// 该函数可以接受父级传递一个参数,并修改valueRef的值
const acceptValue = (value: string) => (valueRef.value = value)
return {
acceptValue,
valueRef
}
}
})
</script>
父组件
<template>
<div>sonRef</div>
<button @click="sendValue">send</button>
// 这里ref接受的字符串,要setup返回的ref类型的变量同名
<Son ref="sonRef" />
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue'
import Son from '@/components/Son.vue'
export default defineComponent({
name: 'Demo',
components: {
Son
},
setup() {
// 如果ref初始值是一个空,可以用于接受一个实例
// vue3中获取实例的方式和vue2略有不同
const sonRef = ref()
const sendValue = () => {
// 可以拿到son组件实例,并调用其setup返回的所有信息
console.log(sonRef.value)
// 通过调用son组件实例的方法,向其传递数据
sonRef.value.acceptValue('123456')
}
return {
sonRef,
sendValue
}
}
})
</script>
9、vue3如何setup函数如何实现多属性监听,如何实现深度监听?
- 引入
watch
,watch最后返回unwatch方法,在调用该方法将停止监听 - watch传入数组,注意,监听的是普通类型可直接输入,若是引用类型,则需要输入函数返回的值,例如要想同时监听
data.form.c.c1
属性和ddd
属性 - 对于watch第三个传参
deep
和immediate
都不陌生,而flush
的作用是决定callback的执行时机,有三个选项,pre
(默认),post
,sync
,分别对应watch在组件更新前,后,时执行callback.
const ddd = ref("wwww");
const data = reactive({
form: {
a: 1,
b: 2,
c: {
c1: "c1",
c2: "c2",
},
},
haha: "haha",
});
const unwatch = watch(
[ddd, () => data.form.c.c1],//传入数组
(newValue, oldValue) => {//结构的也是数组,
//也可以写成([nowddd,nowC1],[preddd,preC1])=>{...}
console.log(`new--->${newValue}`);
console.log(`old--->${oldValue}`);
console.log(newValue[0]);
console.log(newValue);
},
{ deep: true }//第三个参数传入deep,immediate,flush属性
);
setTimeout(() => {
ddd.value = "eee";
}, 1000);
setTimeout(() => {
data.form.c.c1 = "2222";
setTimeout(() => {
unwatch();//这里异步使用unwatch方法,后面的ddd.value = "ffff"将不被监听
});
}, 2000);
setTimeout(() => {
ddd.value = "ffff";
}, 3000);
10、vue3的watchEffect有什么用?
- 它是一个与侦听器,作用和watch差不多,但是不能拿到
newValue
和oldValue
,下面是它的定义,传参effect函数
和option对象
,effect函数
又可传入onInvalidate函数
,option对象
可传入flush,onTrack,onTrigger
,flush
与watch的flush相同,onTrack,onTrigger
又可传入DebuggerEvent 函数
用于开发调试,返回与watch相同返回一个停止侦听的函数function watchEffect( effect: (onInvalidate: InvalidateCbRegistrator) => void, options?: WatchEffectOptions ): StopHandle; interface WatchEffectOptions { flush?: "pre" | "post" | "sync"; onTrack?: (event: DebuggerEvent) => void; onTrigger?: (event: DebuggerEvent) => void; } interface DebuggerEvent { effect: ReactiveEffect; target: any; type: OperationTypes; key: string | symbol | undefined; } type InvalidateCbRegistrator = (invalidate: () => void) => void; type StopHandle = () => void;
- 传参的
effect函数
会在组件beforeCreate
之前就执行一次,若该函数里使用到了某些数据,将监听该数据,当监听的数据发生变化时就会(若watchEffect
传入了onInvalidate函数
,则会先执行onInvalidate函数
后)再次执行effect函数
.<template> <div class="about"> <h1>This is an about page</h1> <h4>{ { count }}</h4> <h4>{ { test }}</h4> <button @click="jump">jump</button> </div> </template> <script lang='ts'> import { onMounted, ref, watchEffect, onBeforeMount } from "vue"; import { useRoute, useRouter } from "vue-router"; const fetchData = () => { return new Promise((resolve) => { setTimeout(() => { resolve("success"); }, 1000); }); }; export default { setup() { const test = ref("test"); const route = useRoute(); const router = useRouter(); const count = ref(0); const effect = async (onInvalidate) => { console.log('监听route'+route.query); onInvalidate(() => { console.log("执行onInvalidate"); }); const res = await fetchData(); console.log(res); test.value = res; }; onBeforeMount(() => { console.log("onBeforeMount"); }); onMounted(() => { console.log("onmounted"); }); const unWachEffect = watchEffect(effect); useRoute(); setTimeout(() => { console.log("5秒时间后注销WachEffect"); unWachEffect(); }, 5000); setInterval(() => count.value++, 1000);//每一秒count自加1,因为watchEffect带有该参数,所以改变时会自动触发 const jump = () => { router.push(`?time=${new Date().getTime()}`); }; return { count, jump, test }; }, beforeCreate() { console.log("beforeCreate"); }, }; </script>
onInvalidate函数
的执行时机
(1). effect里的值改变时,会先于内部函数执行
(2). 侦听器被停止(组件unMounted也会关闭侦听器)
注意点:
setup函数是Vue3中新增的函数,它是我们在编写组件时,使用Composition API的入口。同时它也是Vue3中新增的一个生命周期函数,会在beforeCreate之前调用。因为此时组件的data和methods还没有初始化,因此在setup中是不能使用this的。所以Vue为了避免我们错误的使用,它直接将setup函数中的this修改成了undefined。并且,我们只能同步使用setup函数,不能用async将其设为异步