【vue3】11-Vue 3中的Composition Api(一)

Vue3 - composition Api

  • 前言
    • options Api的弊端
  • 认识composition Api
    • Set up函数的基本使用
      • set up函数的参数
      • set up函数的返回值
    • Set up中数据的响应式
      • 1. Reactive API
      • 2. Ref API
      • 3. ref和reactive的开发
    • setup中的其他函数(了解)
    • setup中禁用this

前言

options Api的弊端

在Vue2中,我们编写组件的方式是Options APl:

  • Options API的一大特点就是在对应的属性中编写对应的功能模块;
  • 比如data定义数据methods中定义方法computed中定义计算属性watch中监听属性改变,也包括生命周期钩子;

但是这种代码有一个很大的弊端:

  • 当我们实现某一个功能时,这个功能对应的代码逻辑会被拆分到各个属性中;
  • 当我们组件变得更大、更复杂时,逻辑关注点的列表就会增长,那么同一个功能的逻辑就会被拆分的很分散;
  • 尤其对于那些一开始没有编写这些组件的人来说,这个组件的代码是难以阅读和理解的(阅读组件的其他人);

下面我们来看一个非常大的组件,其中的逻辑功能按照颜色进行了划分:

  • 这种碎片化的代码使用理解和维护这个复杂的组件变得异常困难,并且隐藏了潜在的逻辑问题;
  • 并且当我们处理单个逻辑关注点时,需要不断的跳到相应的代码块中;

【vue3】11-Vue 3中的Composition Api(一)_第1张图片

如果我们能将同一个逻辑关注点相关的代码收集在一起会更好。

这就是Composition API想要做的事情,以及可以帮助我们完成的事情。


认识composition Api

那么既然知道Composition API想要帮助我们做什么事情,接下来看一下到底是怎么做呢?

  • 为了开始使用Composition API,我们需要有一个可以实际使用它(编写代码)的地方;
  • 在Vue组件中,这个位置就是setup函数;

setup其实就是组件的另外一个选项:

  • 只不过这个选项强大到我们可以用它来替代之前所编写的大部分其他选项;
  • 比如methods、computed、watch、data、生命周期等等;

接下来我们一起学习这个函数的使用:

  • 函数的参数
  • 函数的返回值

Set up函数的基本使用

set up函数的参数

我们先来研究一个setup函数的参数,它主要有两个参数:

  • 第一个参数:props
  • 第二个参数:context

props非常好理解,它其实就是父组件传递过来的属性会被放到props对象中,我们在setup中如果需要使用,那么就可以直接通过props参数获取:

  • 对于定义props的类型,我们还是和之前的规则是一样的,在props选项中定义;
  • 并且在template中依然是可以正常去使用props中的属性,比如message;
  • 如果我们在setup函数中想要使用props,那么不可以通过this去获取(后面我会讲到为什么);
  • 因为props有直接作为参数传递到setup函数中,所以我们可以直接通过参数来使用即可;

另外一个参数是context,我们也称之为是一个SetupContext,它里面包含三个属性:

  • attrs:所有的非prop的attribute;
  • slots:父组件传递过来的插槽(这个在以渲染函数返回时会有作用,后面会讲到);
  • emit:当我们组件内部需要发出事件时会用到emit(因为我们不能访问this,所以不可以通过 this.$emit发出事件);

set up函数的返回值

setup既然是一个函数,那么它也可以有返回值,它的返回值用来做什么呢?

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

甚至是我们可以返回一个执行函数代替在methods中定义的方法:

  • 下面通过从一个计数器案例来体验下:
<template>
  <div class="app">
    <h2>当前计数: {{ counter }}h2>
    <button @click="increment">+button>
    <button @click="decrement">-button>
  div>
template>

<script>

  export default {
    // 使用setup函数
    setup() {
	  //1.定义counter的内容
	  // 默认定义的数据都不是响应式数据
      let counter = 100
      // setup函数定义函数(方法)
      const increment = () => {
        counter++
      }
      // setup函数定义函数(方法)
      const decrement = () => {
        counter--
      }

      // 外部要使用的数据需要通过return导出
      return {
        counter,
        increment,
        decrement
      }
    }
  }
script>

运行上述代码,发现点击 increment 或者 decrement按钮进行操作时,发现counter展示的数值没有发生变化:

因为默认定义的数据不是响应式数据,即对于一个定义的变量来说,默认情况下,Vue并不会跟踪它的变化,来引起界面的响应式操作;

Set up中数据的响应式

如果想为在setup中定义的数据提供响应式的特性,那么我们有如下两种常见的方案:

1. Reactive API

如果想为在setup中定义的数据提供响应式的特性,那么我们可以使用reactive的函数:

<template>
    <div class="app">
        <h2>message:{{ message }}h2>
        <button @click="changeMessage">修改messagebutton>
    div>
    <hr>
    <h2>账号:{{ account.username }}h2>
    <h2>密码:{{ account.password }}h2>
    <button @click="changeAccount">修改账户名button>
    <hr>
template>
  
<script>
import { reactive, ref } from "vue";

export default {
    setup() {
        // 1.定义普通的数据
        // 缺点:数据不是响应的
        let message = "hello world";
        function changeMessage() {
            message = "message已被修改"//不生效
            console.log(222);//222
        }

        // 2.定义响应式的数据
        // 2.1 reactive函数:定义复杂类型的数据(不常用)
        const account = reactive({
            username: "wxx",
            password: "1212135"
        })

        function changeAccount() {
            account.username = "飒飒"//生效
        }

        return {
            message,
            changeMessage,
            account,
            changeAccount,
        }
    }
}
script>

那么这是什么原因呢?为什么就可以变成响应式的呢?

  • 这是因为当我们使用reactive函数处理我们的数据之后,数据再次被使用时就会进行依赖收集;
  • 数据发生改变时,所有收集到的依赖都是进行对应的响应式操作(比如更新界面);
  • 事实上,我们编写的data选项,也是在内部交给了reactive函数将其编程响应式对象的;

2. Ref API

reactive API对传入的类型是有限制的,它要求我们必须传入的是一个对象或者数组类型:

  • 如果我们传入一个基本数据类型(String、Number、Boolean)会报一个警告;

在这里插入图片描述

这个时候Vue3给我们提供了另外一个APl: ref API

  • ref 会返回一个可变的响应式对象,该对象作为一个 响应式的引用 维护着它内部的值,这就是ref名称的来源;
  • 它内部的值是在ref的 value属性中被维护的;

使用示例:

<template>
    
    <h2>当前计数:{{ counter }}h2>
    <button @click="increment">+1button>
template>
  
<script>
import { ref } from "vue";

export default {
    setup() {
        // 2.2 Ref函数:定义简单类型的数据(也可以定义复杂类型数据)
        //错误写法: const counter = ref(0);也能实现变动,因为修改的是counter的value值不少修改的counter本身
        let counter = ref(0)

        function increment() {
            // 错误写法:counter++
            counter.value++
        }

        return {
            counter,
            increment
        }
    }
}
script>

这里有两个注意事项:

  • 模板中引入ref的值时,Vue会自动帮助我们进行解包操作,所以我们并不需要在模板中通过ref.value的方式来使用;
  • 但是在setup函数内部,它依然是一个ref引用,所以对其进行操作时,我们依然需要使用ref.value的方式;

3. ref和reactive的开发

  • 常见使用reactive情况:

【vue3】11-Vue 3中的Composition Api(一)_第2张图片

常见使用ref情况:

【vue3】11-Vue 3中的Composition Api(一)_第3张图片


setup中的其他函数(了解)

1. readonly函数

先来看下边的代码:

  • 在下面的示例中,通过props接收到了父组件传递过来的数据后,直接在当前组件进行了修改,这样是违背了单向数据流

【vue3】11-Vue 3中的Composition Api(一)_第4张图片

如上述代码所示:

我们通过reactive或者ref可以获取到一个响应式的对象,但是某些情况下,我们传入给其他地方(组件)的这个响应式对象希望在另外一个地方(组件)被使用,但是不能被修改,这个时候如何防止这种情况的出现呢?

  • Vue3为我们提供了readonly的方法;
  • readonly会返回原始对象的只读代理〈也就是它依然是一个Proxy,这是一个proxy的set方法被劫持,并且不能对其进行修改);

在开发中常见的readonly方法会传入三个类型的参数:

  • 类型一:普通对象;
  • 类型二: reactive返回的对象;
  • 类型三:ref的对象;

在readonly的使用过程中,有如下规则:

readonly返回的对象都是不允许修改的, 但是经过readonly处理的原来的对象是允许被修改的;

  • 比如 const info = readonly(obj),info对象是不允许被修改的;
  • 但是可以通过修改obj来修改info, obj被修改时,readonly返回的info对象也会被修改;
  • 但是我们不能去修改readonly返回的对象info;
setup() {
	const info = reactive({
	  name: "chenyq",
	  age: 18,
	  height: 1.88
	})
  
	// 为info包裹一个readonly, 子组件就无法修改
	const newInfo = readonly(info)
	console.log(newInfo)

	return {
	  info,
	  changeInfo,
	  newInfo
	}
}

2. Reactive判断的API(了解)

isProxy

检查对象`是否是由reactive或 readonly创建的proxy。

isReactive

检查对象是否是由reactive创建的响应式代理:
如果该代理是readonly创建的,但包裹了由reactive创建的另一个代理,它也会返回true;

isReadonly

检查对象是否是由readonly 创建的只读代理。

toRaw

返回reactive或 readonly代理的原始对象(不建议保留对原始对象的持久引用。请谨慎使用)。

shallowReactive

创建一个响应式代理,它跟踪其自身property 的响应性,但不执行嵌套对象的深层响应式转换(深层还是原生对象)。

shallowReadonly

创建一个proxy,使其自身的property为只读,但不执行嵌套对象的深度只读转换(深层还是可读 可写的)。

3. toRef(s)函数

如果我们希望在模板中展示name,age等数据的值不是info.name,info.age这样的形式,而是直接使用所有对其解构,如下代码示例:

setup() {
  const info = reactive({
    name: "sevgilid",
    age: 19,
    height: 1.87
  })

  const { name, age, height } = info
}

我们会发现直接解构后无论是修改解构后的变量,还是修改reactive返回的state对象,数据都不再是响应式的:

那么如何让解构出来的属性变成响应式的呢?这就需要使用到toRefstoRef函数:

  • toRefs 函数可以将一个响应式对象转换为一个基本类型的对象,并且对象中每个属性都是一个单独的源,并且可以进行解构和响应性访问
  • toRef 函数可以将一个响应式对象中的 指定属性 转换为一个新的响应式数据,这个新的数据可以独立地更新,并且对原响应式对象不会造成影响;
    • 这个函数接收对象键名称两个参数,返回一个带有 value 属性的 ref 对象\

代码示例:

<template>
    <div class="app">
        <!-- <h2>info:{{ info.name }}-{{ info.age }}</h2> -->
        <h2>info:{{ name }}-{{ age }}-{{ height }}</h2>
        <button @click="age++">修改age</button>
    </div>
</template>
   
<script>

import { reactive, toRefs, toRef } from 'vue';
export default {
    setup() {
        const info = reactive({
            name: "sevgilid",
            age: 18,
            height: 1.88

        })
        // reactive被解构后会变成普通的值,失去响应式
        // 使用toRefs,toRef方法就可以
        const { name, age } = toRefs(info)
        // 单独对一个结构
        const height = toRef(info, "height")

        return {
            name,
            age,
            height
        }
    }
}

setup中禁用this

Vue 3 中的 setup 函数是一个新的组件选项。它允许我们在组件渲染之前执行一些前置操作,比如数据处理、事件绑定等;

与 Vue 2.x 中的 data、computed、methods 等属性不同,setup 函数只接收 props 作为参数,并且不能访问 this

为什么不能使用this:

  • 在 Vue 3 中,模板编译的过程发生了很大的变化,这就导致了 this 不能再指向 Vue 实例对象;
  • 在 Vue 2.x 中,我们可以通过 this.$xxx 访问全局实例,但是在 Vue 3 中,由于模板编译的变化,我们需要使用 provide 和 inject 手动注入/引用全局实例对象,而不能再通过 this 来访问它们。

下面的代码演示了 setup 函数内使用 this 的错误示例:

export default {
  setup() {
    console.log(this.$route) // 无法访问 this
  }
}

你可能感兴趣的:(Vue3,vue.js,javascript,前端)