vue3之setup的使用理解

1、vue3中的setup有什么用?

props 是响应式的,当传入新的 props 时,它将被更新。但是,因为 props 是响应式的,你不能使用 ES6 解构,它会消除 prop 的响应性。

 
export default {
  props: {
    item: String
  },
  setup(props,context) { //   setup(props,{ emit } ) 解构赋值
    console.log(props.item) // 这个可以访问到props里面的值
    console.log(context.emit) // 这个可拿到emit方法
  }
}
如果需要解构 prop,可以在 setup 函数中使用 toRefs 函数来完成此操作

import { toRefs } from 'vue'
 
setup(props) {
  const { obj } = toRefs(props)
 
  console.log(obj.value)
}
如果 obj是可选则的 prop,则传入的 props 中可能没有 obj。在这种情况下,toRefs 将不会为 obj创建一个 ref 。你需要使用 toRef 替代它:

import { toRef } from 'vue'
setup(props) {
  const obj= toRef(props, 'obj')
  console.log(obj.value)
}

传递给 setup 函数的第二个参数是 context。context 是一个普通 Js 对象,暴露了其它可能在 setup 中有用的值

export default {
  setup(props, context) {
    // Attribute 属性 非响应式对象,等同于 $attrs
    console.log(context.attrs)
 
    // 插槽 非响应式对象,等同于 $slots
    console.log(context.slots)
 
    // 触发事件 方法,等同于 $emit
    console.log(context.emit)
 
    // 暴露公共 property 函数
    console.log(context.expose)
  }
}
context 是一个普通的 JavaScript 对象,也就是说,它不是响应式的,这意味着你可以安全地对 context 使用 ES6 解构。

export default {
  setup(props, { attrs, slots, emit, expose }) {
    ...
  }
}

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也会关闭侦听器)​​​​​​
————————————————
版权声明:本文为CSDN博主「业精于勤,行成于思」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_25506089/article/details/114743592

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值