「掘金日新计划 · 8 月更文挑战」的第15天—Vue3常⽤Composition API

117 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第15天,点击查看活动详情

image.png

setup

1.setup 函数是Vue3新的配置项
2.是使⽤组合API的前提,数据、⽅法都要放到 setup 函数⾥声明
3.写法:
在setup里面声明变量和方法,再return出去,通过在插值语法在变量中访问这些变量和方法

<script>
export default {
  name: "Setup",
  props: {
    msg: String,
  },
  setup() {
    const name = "张三";
    const age = "14";
    function sayHello() {
      alert(`你好,我是${name},今年${age}岁了`);//这边不用this是因为这个变量都在这个setup作用域里面
    }
    return {
      name,
      age,
      sayHello,
    };
  },
};
</script>

4.剖析setup函数的执⾏时机和两个参数

  • 执⾏时机(setup在所有周期函数之前执行)
    在⽣命周期函数 beforeCreate 之前执⾏⼀次,⽽且 setup 函数没有 this
  • 两个参数 props:接收⽗组件的值,是⼀个对象 context:上下⽂对象 触发⾃定义事件
<script>
export default {
  name: "SetupMore",
  props: ["mess"], //接收父组件的值
  emits: ["miaozai"], //需要emits声明才能在setup中使⽤

  //在⽣命周期函数 beforeCreate 之前执⾏⼀次
  //   beforeCreate(){
  //     console.log('=========beforeCreate========')
  //   },

  // vue3触发
  setup(props, context) {
    // console.log("========setup====")//在⽣命周期函数 beforeCreate 之前执⾏⼀次
    console.log(this); //⽽且 setup 函数没有 this
    console.log(props.mess);
    function clickMe() {
      context.emit("miaozai", "我是⼦组件的值,我现在发给父组件");
    }

    return {
      clickMe,
    };
  },
  //   vue2触发自定义事件
  mounted() {
    this.$emit("miaozai", "我是子组件传过来的值");
  },
};
</script>

实现数据的响应式—ref函数

1.定义
定义⼀个响应式的数据
2.基本类型数据

1.引⼊ ref 函数
import { ref } from "vue";

2.创建⼀个包含响应式数据的引⽤对象(ref 对象)
let name = ref("张三");
let age = ref(18);

3.操作数据
function changePerson() {
    name.value = "李四";
    age.value = "19";
}


3.对象类型数据

1.创建⼀个包含响应式数据的引⽤对象(ref 对象)
 let obj= ref({
     fruit:'苹果',
     price:'12'
  });
  
2.操作数据
function changePrice() {
 obj.value.price = "24";
}

4.注意事项:

  • 可以处理基本类型数据数组或者对象类型的数据
  • 基本类型数据的响应式是通过 Object.defineProperty() 实现
  • 对象类型数据的响应式是通过 ES6 中的 Proxy 实现

实现数据的响应式—reactive函数

1.定义:
定义⼀个对象类型的响应式数据(不能处理基本类型数据)
2.写法:

<template>
  <div>
    <!-- <h1>水果店{{data.fruit}}</h1> -->
    <!-- 使用toRef后不需要再用data了 -->
    <h1>水果店:{{ fruit }}</h1>

    <h1>水果网站:{{ data.website }}</h1>
    <h1>
      水果品种:
      <ul>
        <li>{{ data.fruits.a }}</li>
        <li>{{ data.fruits.b }}</li>
        <li>{{ data.fruits.c }}</li>
      </ul>
    </h1>
  </div>
</template>

<script>
import { reactive, toRef, toRefs } from "vue";
export default {
  setup() {
    //定义
    let data = reactive({
      fruit: "水果王",
      website: "fruit.com",
      fruits: {
        a: "苹果",
        b: "香蕉",
        c: "西瓜",
      },
    });

    // 拿到目标对象的属性
    let fruit = toRef(data, "fruit");

    return {
      data,
      fruit,
      ...toRefs(data), //展开第一层同时也是拿到第一层
    };
  },
};
</script>

<style>
</style>

3.reactive 和 ref 不同点

  • 处理数据类型不同: ref 可以处理基本类型和对象(数组)类型数据, reactive 只能处理对象(数组)类型数据
  • 实现原理不同: ref 处理基本类型数据通过Object.defineProperty() 实现, reactive 通过Proxy 实现
  • 操作不同: ref 操作数据需要加 .value
  • 组件数据多时更加趋向使⽤ reactive

computed函数

1.使用:
通过已有的属性计算⽽来,跟vue2.x中的功能原理⼀样,使⽤⽅式有区别
2.代码:

<template>
  <div>
    <h2>姓:{{ person.firstName }}</h2>
    <h2>名:{{ person.lastName }}</h2>
    <h2>全名:{{ fullName }}</h2>
    <h2>更改:<input type="text" v-model="fullName" /></h2>
  </div>
</template>

<script>
// import { ref, computed } from "vue";
import { reactive, computed } from "vue";

export default {
  // vue2实现方式
  // data(){
  //   return{
  //     fistName:'张',
  //     lastname:'三'
  //   }
  // },
  // computed:{
  //  fullName(){
  //   return this.fistName+"-"+this.lastName
  //  }
  // }

  // vue3实现方式   计算 ref 定义的响应式数据
  // setup() {
  //   let firstName = ref("李");
  //   let lastName = ref("四");

  //   const fullName = computed({
  //     get() {
  //       return firstName.value + "-" + lastName.value;
  //     },
  //     set(value) {
  //       const arr = value.split("-");
  //       firstName.value = arr[0];
  //       lastName.value = arr[1];
  //     },
  //   });

  //   return {
  //     firstName,
  //     lastName,
  //     fullName,
  //   };
  // },

  setup() {
    let person = reactive({
      firstName: "zhang ",
      lastName: "san",
    });
    let fullName = computed({
      get() {
        return person.firstName + "-" + person.lastName;
      },
      set(value) {
        const arr = value.split("-");
        person.firstName = arr[0];
        person.lastName = arr[1];
      },
    });

    return {
      person,
      fullName
    };
  },
};
</script>

<style>
</style>

watch函数

1.使用:
监听值的变化,执⾏相关的操作,跟vue2.x中的配置⼀样
2.代码:

<template>
  <div>
    当前的计数:<span>{{ num }}</span> 当前的计数:<span>{{ num1 }}</span>
    <button @click="num++">累加</button>
    <button @click="num++">累加num1</button>
    <br />
    当前的计数c:<span>{{ numObj.c }}</span>
    <button @click="numObj.c++">累加c</button>
    当前的计数d:<span>{{ numObj.d }}</span>
    <button @click="numObj.c++">累加d</button>
    当前的计数a:<span>{{ numObj.a.ee }}</span>
     <button @click="numObj.a.ee++">累加.a.ee</button>
  </div>
</template>

<script>
import { ref, watch, reactive } from "vue";
export default {
  // vue2实现监听
  // data(){
  //     return{
  //         num:1,
  //     }
  // },
  // watch:{
  //     num(){
  //         console.log('num改变了')
  //     }
  // }

  // vue3
  setup() {
    // 监听ref定义的数据
    let num = ref(1);
    let num1 = ref(3);

    // 监听 reactive 定义的数据
    let numObj = reactive({
      c: 1,
      d: 2,
      a:{
        ee:34
      }
    });

    // 监听⼀个ref定义的数据
    // watch(
    //   num,
    //   (newValue, oldValue) => {
    //     console.log("num增加了", newValue, oldValue);
    //   },
    //   { immediate: true, deep: true }
    // );

    // 监听多个ref定义的数据
    watch([num, num1], (newValue, oldValue) => {
      console.log("num1增加了", newValue, oldValue);
    });

    // 监听对象类型  bug:原本c的值为1没有打印出来
    watch(numObj, (newValue, oldValue) => {
      console.log("numObj变化了", newValue, oldValue);
    });

    // 监听对象中的⼀个基本类型属性
    watch(
      () => numObj.c,
      (newValue, oldValue) => {
        console.log("numObj变化了", newValue, oldValue);
      }
    );

    // 监听对象中的⼀些基本类型属性
    watch([() => numObj.c, () => numObj.d], (newValue, oldValue) => {
      console.log("numObj变化了", newValue, oldValue);
    });

    // 监听对象中的对象类型属性
    watch(numObj.a, (newValue, oldValue) => {
      console.log("numObj.c变化了", newValue, oldValue);
    });

    return {
      num,
      num1,
      numObj,
    };
  },
};
</script>

<style>
</style>

3.总结实现监听⽣效:

  • ref 定义的数据
    (1)基本类型数据作为监听值
    (2)对象作为监听值,需要加 .value (⽤的少)\
  • reactive 定义的数据
    (1)对象作为监听值
    (2)属性作为监听值,需要放在回调函数中\

setup中的watchEffect函数

1.使用:
在监听的回调函数中使⽤了属性,则监听该属性,不⽤在参数上指明监听哪个属性 2.代码:

<template>
  <h1>
    当前的计数a:<span>{{ numa }}</span> <br />
    当前的计数b:<span>{{ numb }}</span> <br />
  </h1>
  <button @click="numa++">增加a</button>
  <button @click="numb++">增加b</button>
  <hr />
</template>

<script>
import { ref, watchEffect } from "vue";
export default {
  setup() {
    let numa = ref(1);
    let numb = ref(1);

    // watch ⼿动添加定向的监听属性
    // watch([numa, numb], (newValue, oldValue) => {
    //   console.log("numa或者numb变化了", newValue, oldValue);
    // });

    // watchEffect ⾃动监听使⽤到的属性
    watchEffect(() => {
      // let xd = numa.value;
      // let xd1 = numb.value;
      console.log("watchEffect函数执行了"); //初始化执⾏:watchEffect 会初始化执⾏⼀次
    });

    return {
      numa,
      numb,
    };
  },
};
</script>

<style>
</style>

3.与 watch 的区别

  • 属性监听区别:
    watch ⼿动添加定向的监听属性
    watchEffect ⾃动监听使⽤到的属性\
  • 初始化执⾏:
    watchEffect 会初始化执⾏⼀次\