专业的编程技术博客社区

网站首页 > 博客文章 正文

Vue3,Composition API(组合) setup、ref、reactive、toRefs案例

baijin 2024-10-27 08:14:08 博客文章 25 ℃ 0 评论

Composition API(组合式)

setup()函数

setup函数的参数:1、第一个参数:props,2、第二个参数:context。

第一个参数:props

子组件接收的从父组件传递过来的属性,会被放到props对象中,在setup函数中使用,可以直接通过props参数获取使用即可。

第二个参数:context

context也称之为是一个SetupContext,它里面包含三个属性:

1、attrs:为一个对象,存储着所有的从父组件传递过来的非props的attribute;

2、slots:父组件传递过来的插槽;

3、emit:组件内部需要发出事件时用emit;

setup函数的返回值

setup的返回值可以在模板template中被使用,也就是说可以通过setup的返回值来替代data选项。

注:对于定义的变量来说,默认情况下,Vue并不会跟踪它的变化,来引起界面的响应式操作。

注:setup中避免使用this,因为它不会找到组件实例,setup的调用发生在data property、computed property或methods被解析之前,所以它们无法在setup中被获取。

参考:Vue3,仍然支持Options API用法,更推荐Composition API,代码案例

ref()

ref,生成值类型(即基本数据类型) 的响应式数据,用于模板和reactive,通过.value来修改值,注意一定要加上.value,不仅可以用于响应式,还可以用于模板的DOM元素。

ref()函数可以根据给定的值来创建一个响应式的数据对象,返回值是一个对象,且只包含一个 .value属性。

为什么要用ref?

值类型(即基本数据类型)无处不在,如果不用ref而直接返回值类型,会丢失响应式,如在setup、computed、合成函数等各种场景中,都有可能返回值类型,Vue如果不定义 ref ,用户将自己制造 ref ,这样反而会更加混乱。

reactive()

Vue3中,reactive提供实现响应式数据的方法。在Vue2中响应式数据是通过defineProperty来实现的,而在Vue3中响应式数据是通过ES6的Proxy来实现的。

reactive()函数接收一个普通的对象,返回出一个响应式对象。

reactive参数必须是对象(json/arry),默认情况下修改对象,界面不会自动更新,若想更新,可通过重新赋值的方式。

toRefs()

对一个普通对象来说,要实现响应式,就用reactive,用了reactive后,它就是一个响应式对象。在一个响应式对象里面,如果其中有一个属性要拿出来单独做响应式的话,就可以用toRef。

toRef,可以响应对象Object ,其针对的是某一个响应式对象(reactive封装)的属性prop。toRef和对象Object两者保持引用关系,即一个改完另外一个也跟着改。toRef,如果用于普通对象(非响应式对象),产出的结果不具备响应式。

toRef(Object, prop) 的形式来传对象名和具体的属性名,达到某个属性数据响应式的效果。

export default和export default defineComponent区别

defineComponent

Vue3中,新增了defineComponent,它并没有实现任何的逻辑,只是把接收的Object直接返回,它的存在是完全让传入的整个对象获得对应的类型,它的存在就是完全为了服务TypeScript 而存在的。

Vue,单文件组件开发时,会有下面两种写法:JavaScript的写法、TypeScript的写法。

JavaScript的写法

<script >
export default({
  // js写法
})
</script>

TypeScript的写法

<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
  // ts写法
})
</script>

Composition API(组合式)的代码案例

代码案例一

效果

demo01Manage.vue

<template>
  <div id="demo01">
    <h2>
      姓名:<span style="color: red">{{ userName }}</span>
    </h2>
    <h2>
      年龄:
      <button type="button" @click="changeAge(-1)">+</button>
      {{ userAge }}
      <button type="button" @click="changeAge(1)">+</button>
    </h2>
    <h2>出生年份: (插值表达式实现) {{ new Date().getFullYear() - userAge }}</h2>
    <h2>
      出生年份:(计算属性实现)
      <button type="button" @click="changeYear(-1)">-</button>
      {{ birthYear }}
      <button type="button" @click="changeYear(1)">+</button>
    </h2>
  </div>
</template>

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

export default {
  name: "demo01",
  setup() {
    // ref(),接受一个参数值并返回一个响应式且可改变的ref对象
    const userName = ref("张三");
    // 年龄
    const userAge = ref(20);
    // 出生年份
    const birthYear = computed({
      // 设置 getter 和 setter
      get: () => {
        return new Date().getFullYear() - userAge.value;
      },
      set: (val) => {
        userAge.value = new Date().getFullYear() - val;
      },
    });
    // 计算属性
    function changeAge(val) {
      // userAge是一个响应式对象,不可以userAge++
      userAge.value += val;
    }
    // 计算属性
    const changeYear = (val) => {
      birthYear.value = birthYear.value + val;
    };
    return { userName, userAge, birthYear, changeAge, changeYear };
  },
};
</script>

<style>
#demo01{
  background-color: rgb(171, 179, 223);
}
</style>

代码案例二

效果

demo02Manage.vue

<template>
  <div id="demo02">
    <h2>
      姓名:<span style="color: red">{{ userName }}</span>
    </h2>
    <h2>
      年龄:
      <button type="button" @click="changeAge(-1)">+</button>
      {{ userAge }}
      <button type="button" @click="changeAge(1)">+</button>
    </h2>
    <h2>
      出生年份: (插值表达式实现) {{ new Date().getFullYear() - userAge }}
    </h2>
    <h2>
      出生年份:(计算属性实现)
      <button type="button" @click="changeYear(-1)">-</button>
      {{ birthYear }}
      <button type="button" @click="changeYear(1)">+</button>
    </h2>
  </div>
</template>

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

export default {
  name: "demo02",
  setup() {
    const data = reactive({
      userName: "张三",
      userAge: 20,
      birthYear: computed({
        // 设置 getter 和 setter
        get: () => {
          return new Date().getFullYear() - data.userAge;
        },
        set: (val) => {
          data.userAge = new Date().getFullYear() - val;
        },
      }),
    });
    // 计算属性
    function changeAge(val) {
      data.userAge += val;
    }
    // 计算属性
    const changeYear = (val) => {
      data.birthYear = data.birthYear + val;
    };
    // 直接返回data是一个响应式的数据,页面需要使用data.xxx得到属性
    // 页面直接使用属性,使用 ..toRefs()方法将一个整体的响应式对象变成普通对象
    // 展开(解包)得到单独的响应式数据
    return { ...toRefs(data), changeAge, changeYear };
  },
};
</script>

<style>
#demo02 {
  background-color: rgb(223, 171, 219);
}
</style>

代码案例三

效果

demo03Manage.vue

<template>
  <div id="demo03">
    <h1>{{name}}</h1>
    <Child 
      id="child"
      class="demoClass"
      message="发送消息"
      :title="title"
      @receiveData="receiveData">
    </Child>
  </div>
</template>

<script>
import { ref } from 'vue'
import Child from './childComponent.vue'
export default {
  name:'demo03',
  components:{
    Child
  },
  setup() {
    const name = ref('我是父组件');
    const title = ref('定义标题' + new Date().getTime());
    // 接收子页面传输的元素
    const receiveData = (data) => {
       console.log("parent.监听到了change事件", data);
    };
    return {
      name,
      title,
      receiveData
    }
  },
}
</script>

<style>
#demo02 {
  background-color: rgb(171, 217, 223);
}
.demoClass{
  color:rgb(4, 0, 255);
}
</style>

childComponent.vue

<template>
  <div id="demo03">
    <div>
      <h3>子组件</h3>
      <h3>{{ title }}</h3>
      <h3>{{ message }}</h3>
      <h3>title: {{ $props.title }} ----- message: {{ $props.message }}</h3>
      <h3>id: {{ $attrs.id }} ----- class: {{ $attrs.class }}</h3>
      <slot></slot>
    </div>
    <hr/>
    <h2>姓名:<span style="color: red">{{ userName }}</span></h2>
    <h2>
      年龄:
      <button type="button" @click="changeAge(-1)">+</button>
      {{ userAge }}
      <button type="button" @click="changeAge(1)">+</button>
    </h2>
    <h2>
      出生年份: (插值表达式实现) {{ new Date().getFullYear() - userAge }}
    </h2>
    <h2>
      出生年份:(计算属性实现)
      <button type="button" @click="changeYear(-1)">-</button>
      {{ birthYear }}
      <button type="button" @click="changeYear(1)">+</button>
    </h2>
  </div>
</template>

<script>
import { reactive, computed, toRefs, watch } from "vue";

export default {
  name: "demo03",
  // setup 参数
  props: {
    title: {
      type: String,
      default: "",
    },
    message: {
      type: String,
      required: true,
    },
  },
  emits: ["receiveData"],
  setup(props, context) {
    // =======================================================//
    const { attrs, slots, emit } = context;
    //在setup中获取props中属性,通过setup函数的第一个参数获取
    console.log("props", props, props.message, props.title);
    //父组件传递给子组件的非props属性在setup中使用,这些属性都存储在context.attrs对象中
    console.log("attrs", attrs, attrs.id, attrs.class);
    //父组件插入子组件插槽中的内容
    console.log("slots", slots);
    // =======================================================//
    //emit用来向父组件发送事件
    emit("receiveData", "子组件发送事件");
    // =======================================================//
    const data = reactive({
      userName: "张三",
      userAge: 20,
      birthYear: computed({
        // 设置 getter 和 setter
        get: () => {
          return new Date().getFullYear() - data.userAge;
        },
        set: (val) => {
          data.userAge = new Date().getFullYear() - val;
        },
      }),
    });
    // 使用监听器
    const watchTitle = watch(() => props.title, function(newTitle, oldTitle){
        console.log("......" + oldTitle + "->" + newTitle);
        emit("receiveData", oldTitle + "->" + newTitle);
      }
    );
    const watchAge = watch(() => data.userAge, (newAge, oldAge) => {
        console.log("receiveData", newAge + "->" + oldAge);
        emit("receiveData", newAge + "->" + oldAge);
      }
    );
    // 计算属性
    function changeAge(val) {
      data.userAge += val;
      props.title = "年龄" + data.userAge;
    }
    // 计算属性
    const changeYear = (val) => {
      // 响应式对象
      data.birthYear = data.birthYear + val;
    };
    // 直接返回data是一个响应式的数据,页面需要使用data.xxx得到属性
    // 页面直接使用属性,使用 ..toRefs()方法将一个整体的响应式对象变成普通对象
    // 展开(解包)得到单独的响应式数据
    return {
      ...toRefs(data),
      watchTitle,
      watchAge,
      changeAge,
      changeYear,
    };
  },
};
</script>

<style>
#demo03 {
  background-color: rgb(171, 223, 197);
}
</style>

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表