百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 热门文章 > 正文

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

bigegpt 2024-08-05 11:49 3 浏览

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>

相关推荐

程序员请收好:10个非常有用的 Visual Studio Code 插件

一个插件列表,可以让你的程序员生活变得轻松许多。作者|Daan译者|Elle出品|CSDN(ID:CSDNnews)以下为译文:无论你是经验丰富的开发人员还是刚刚开始第一份工作的初级开发人...

PADS在WIN10系统中菜单显示不全的解决方法

决定由AD转PADS,打开发现菜单显示不正常,如下图所示:这个是由于系统的默认字体不合适导致,修改一下系统默认字体即可,修改方法如下:打开开始菜单-->所有程序-->Windows系统--...

一文讲解Web前端开发基础环境配置

先从基本的HTML语言开始学习。一个网页的所有内容都是基于HTML,为了学好HTML,不使用任何集成工具,而用一个文本编辑器,直接从最简单的HTML开始编写HTML。先在网上下载notepad++文...

TCP/IP协议栈在Linux内核中的运行时序分析

本文主要是讲解TCP/IP协议栈在Linux内核中的运行时序,文章较长,里面有配套的视频讲解,建议收藏观看。1Linux概述  1.1Linux操作系统架构简介Linux操作系统总体上由Linux...

从 Angular Route 中提前获取数据

#头条创作挑战赛#介绍提前获取意味着在数据呈现在屏幕之前获取到数据。本文中,你将学到,在路由更改前怎么获取到数据。通过本文,你将学会使用resolver,在AngularApp中应用re...

边做游戏边划水: 基于浅水方程的水面交互、河道交互模拟方法

以下文章来源于腾讯游戏学堂,作者Byreave篇一:基于浅水方程的水面交互本文主要介绍一种基于浅水方程的水体交互算法,在基本保持水体交互效果的前提下,实现了一种极简的水面模拟和物体交互方法。真实感的...

Nacos介绍及使用

一、Nacos介绍Nacos是SpringCloudAlibaba架构中最重要的组件。Nacos是一个更易于帮助构建云原生应用的动态服务发现、配置和服务管理平台,提供注册中心、配置中心和动态DNS...

Spring 中@Autowired,@Resource,@Inject 注解实现原理

使用案例前置条件:现在有一个Vehicle接口,它有两个实现类Bus和Car,现在还有一个类VehicleService需要注入一个Vehicle类型的Bean:publicinte...

一文带你搞懂Vue3 底层源码

作者:妹红大大转发链接:https://mp.weixin.qq.com/s/D_PRIMAD6i225Pn-a_lzPA前言vue3出来有一段时间了。今天正式开始记录一下梗vue3.0.0-be...

一线开发大牛带你深度解析探讨模板解释器,解释器的生成

解释器生成解释器的机器代码片段都是在TemplateInterpreterGenerator::generate_all()中生成的,下面将分小节详细展示该函数的具体细节,以及解释器某个组件的机器代码...

Nacos源码—9.Nacos升级gRPC分析五

大纲10.gRPC客户端初始化分析11.gRPC客户端的心跳机制(健康检查)12.gRPC服务端如何处理客户端的建立连接请求13.gRPC服务端如何映射各种请求与对应的Handler处理类14.gRP...

聊聊Spring AI的Tool Calling

序本文主要研究一下SpringAI的ToolCallingToolCallbackorg/springframework/ai/tool/ToolCallback.javapublicinter...

「云原生」Containerd ctr,crictl 和 nerdctl 命令介绍与实战操作

一、概述作为接替Docker运行时的Containerd在早在Kubernetes1.7时就能直接与Kubelet集成使用,只是大部分时候我们因熟悉Docker,在部署集群时采用了默认的dockers...

在MySQL登录时出现Access denied for user ~~ (using password: YES)

Windows~~~在MySQL登录时出现Accessdeniedforuser‘root‘@‘localhost‘(usingpassword:YES),并修改MySQL密码目录适用...

mysql 8.0多实例批量部署script

背景最近一个项目上,客户需要把阿里云的rdsformysql数据库同步至线下,用作数据的灾备,需要在线下的服务器上部署mysql8.0多实例,为了加快部署的速度,写了一个脚本。解决方案#!/bi...