好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

Vue组合式API--setup中定义响应式数据的示例详解

一、Options API(选项式API)的弊端

1.1 什么是选项式API

在 Vue2.x 中,编写组件的方式是使用 Options API ,它的特点是在对应的属性中编写对应的功能模块。

比如 data 中定义数据、 methods 中定义方法、 computed 中定义计算属性、 watch 中监听属性改变。

生命周期钩子也属于 Options API (选项式 API )。

1.2 选项式API的弊端

当我们实现某一个功能时,这个功能对应的代码逻辑会被拆分到各个属性中。

当组件变得很大、很复杂时,逻辑关注点的列表就会增长,那么同一个功能的逻辑就会被拆分的很分散。

特别是对于那些一开始没有编写这些组件的人(阅读组件的人)来说,这个组件的代码是难以阅读和理解的。

碎片化的代码使用理解和维护这个复杂的组件变得非常困难,很容易隐藏了潜在的逻辑问题。 当处理单个逻辑关注点时,需要不断的跳到相应的代码块中,增加了代码维护的成本。

二、Composition API(组合式API)概述

2.1 Composition API的作用

如果能将同一个逻辑关注点相关的代码收集在一起会更好一些,这就是 Composition API 想 要做的事情。

Vue Composition API 可以简称为 VCA 。

2.2 Composition API的编码位置

Vue3.x 提供了 setup 函数来编写各种组合式 API 。

setup 其实也是组件的另外一个选项,只不过这个选项可以用来替代大部分其他选项式 API 选项。

比如 methods 、 computed 、 watch 、 data 、各种生命周期钩子函数等等。

三、setup函数的参数和返回值

3.1 setup函数的参数

setup函数主要由两个参数:

props :父组件传递过来的属性会被放到 props 对象中 props 的类型,和选项式 API 中的的规则一样,在props选项中定义 props 的使用,在 template 中依然是可以把 props 中的属性当成普通 Vue 变量去使用。 在 setup 函数中用 props ,不可以通过 this 去获取 context :也称之为 SetupContext ,是 setup 函数的上下文对象,它主要由三个属性 attrs :所有的非 props 的的属性 slots :父组件传递过来的插槽,在以渲染函数返回时会有作用 emit :当组件内部需要发出事件时会用到emit 主要是因为 setup 函数中不能访问 this ,所以无法通过 this.$emit 发出事件

3.2 setup函数的返回值

setup 的返回值可以来替代 data 选项,因此 setup 的返回值可以在模板 template 中使用。

<script>
import { ref } from 'vue'

export default {
  setup() {
    // 定义counter的内容
    // 默认定义的数据都不是响应式数据,使用ref可以让数据变成响应式的
     let counter = ref(100)
     return {counter}
  }
}
</script>

setup 的返回值可以来替代 methods 中定义的方法

<script>
import { ref } from 'vue'

export default {
  setup() {
    // 定义counter的内容
    // 默认定义的数据都不是响应式数据,使用ref可以让数据变成响应式的
    let counter = ref(100)
    const increment = () => {
       counter.value ++
       console.log(counter.value)
    }

	return {counter, increment}

  }
}
</script>

四、setup中如何定义响应式数据

4.1 reactive函数

代码示例:

<template>
  <div>
    <h2>账号: {{ account.username }}</h2>
    <h2>密码: {{ account.password }}</h2>
    <!-- 没使用reactive函数时,点击此按钮,能够修改数据,但是却不能渲染到页面上 -->
    <button @click="changeAccount">修改账号</button>
  </div>
</template>
<script>
  // 1.引入reactive函数
  import { reactive } from 'vue'

  export default {
    setup() {

      // 2.使用reactive函数: 定义复杂类型的响应式数据数据
      const account = reactive({
        username: "张三",
        password: "666666"
      })
      function changeAccount() {
        account.username = "kobe"
      }
	  // 3.返回定义的数据
      return {account, changeAccount}
    }
  }
</script>

由代码示例可知,使用reactive函数注意好三步即可:

从 Vue 中引入 reactive 函数 把要定义的复杂数据类型( 对象或者数组 )传入 reactive 函数, reactive 函数的返回值就是响应式数据不要 忘记在 setup 函数中返回才可以在模板上使用

为什么加了reactive函数后数据就变成了响应式的:

当我们使用 reactive 函数处理我们的数据之后,数据再次被使用时就会进行依赖收集。

当数据发生改变时,所有收集到的依赖都是进行对应的响应式操作。

选项式 API 中的 data 选项中的复杂数据,在底层也是交给了 reactive 函数将其编程响应式对象的。

4.2 ref函数

1)ref函数的基本使用

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

如果传入一个基本数据类型,比如 String 、 Number 、 Boolean 等等就会报出一个警告:

value cannot be made reactive : xxx

Vue3 给我们提供了另外一个 ref 函数。

ref 函数返回可变的响应式对象,该对象作为一个 响应式的引用维护着它内部的值,这也是 ref 名称的来源。

它内部的值是在 ref 的 value 属性中被维护的,因此使用 ref 函数定义的变量,使用时需要从 value 属性取值。

代码示例:

<template>
  <div>
    <!-- 默认情况下在template中使用ref时, vue会自动对其进行解包(取出其中value) -->
    <h2>当前计数: {{ counter }}</h2>
    <button @click="increment">+1</button>
    <button @click="counter++">+1</button>
  </div>
</template>
<script>
  // 1.引入ref函数
  import { ref } from 'vue'

  export default {
    setup() {

      // 2.ref函数: 主要定义简单类型的数据(也可以定义复杂类型的数据)
      const counter = ref(0)
      function increment() {
        counter.value++
      }
	  // 3.返回定义的数据
      return {counter, increment}
    }
  }
</script>

2)ref浅层解包

<template>
  <div>
    <!--ref 浅层解包 (意思就是把ref变量放别的对象中时就会有下面两个情况)-->
    <!-- 使用的时候不需要写.value -->
    <h2>当前计数: {{ info.counter }}</h2>
    <!-- 修改的时候需要写.value -->
    <button @click="info.counter.value++">+1</button>
  </div>
</template>
<script>
  // 1.引入ref函数
  import { ref } from 'vue'

  export default {
    setup() {

      // 2.ref函数: 主要定义简单类型的数据(也可以定义复杂类型的数据)
      const counter = ref(0)
      const info = {counter}
	  // 3.返回定义的数据
      return {counter}
    }
  }
</script>

注意点:

在模板中引入 ref 函数定义的值时, Vue 会自动帮助我们进行解包操作 解包就是 Vue 自动取出 ref 函数所定义的变量中的 value 值 所以使用者并不需要在模板中通过 ref.value 的方式来使用 在 setup 函数内部, ref 函数定义的值是一个 ref 引用, 所以需要使用 ref.value 的方式去操作 关于 ref 的浅层解包 直接在 template 中使用 ref 变量,会进行自动解包 如果把 ref 变量放在一个对象当中会有下面的情况 使用时,会进行自动解包 修改时,不会进行自动解包,因此需要使用 value 属性去获取值

4.3 关于如何选择reactive函数和ref函数

1)选择reactive函数的场景

reactive 函数应用于本地的数据的定义,本地数据代表这个数据并不是来源于服务器 reactive 函数定义的复杂数据的多个属性之间是有一定联系的

import {reactive} from 'vue'

const user = reactive({
	username: '张三',
	password: '123456'
})

例如收集一个表单中的数据,就是很适合使用reactive函数的场景

2)选择ref函数的场景

定义本地的简单类型的数据

import {ref} from 'vue'

const message = ref('hello ref')

定义从服务器中获取的数据

import {ref} from 'vue'

const music = ref([])							// 定义一个本地数组
const serverMusicList = ['小星星', '虫儿飞']		// 模拟从服务器获取到的数据
music.value = serverMusicList					// 把数据赋值给music变量

这是经验之谈,很多优秀的开源项目中也都是这么取做的,文档中并没有明确说明什么情况使用什么

这其实也是程序员本身应该自己去考虑的问题

五、单向数据流和readonly的使用

5.1 单向数据流规范

1)什么是单向数据流规范

父组件传递给子组件的对象数据,子组件只能使用,不允许修改。

虽然是能修改的,但是违反了单向数据流的原则。

如果真的想修改这个数据,应该发出事件,让父组件监听这个事件,然后在父组件中进行修改。

2)为什么要遵循这个原则

今后项目中可能会有很多子组件,父组件的一个数据可能会传递给多个子组件。

由于传递的是数据对象的引用,因此其中一个子组件中修改了数据,会导致全部引用它的地方,全部受到影响。

而且维护时,难以知道是哪个位置修改的这个数据。

其它地方也有很多类似的概念规范:

react 中 react 有一个重要的原则:任何一个组件都应该像纯函数一样,不能修改传入的 props js 中 在 js 中也有类似原则,熟悉 js 的可能会知道一个纯函数的概念。纯函数意思就是函数不要对传入的参数进行修改。

这些规范如果不遵守,代码确实也能实现功能。但是将来维护性将会非常差,谁试谁知道。

5.2 readonly的使用

在给组件传递数据时,如果希望组件使用传递的内容。但是不允许它们修改时,就可以使用 readonly 了。

因为单向数据流原则在 Vue2.x 的时候时没法从代码上避免的。如果一个程序员根本不知道这个原则。

就有可能写出难以维护的代码。

而 Vue3.x 则提供了 readonly 函数从编码层面去避免这个问题。

<template>
	<!-- 给组件home传递一个只读数据 -->
	<home :info="readonlyInfo" />
</template>
<script>
    import { readonly } from 'vue'
    setup() {
      // 本地定义多个需要传递给子组件的数据
      const info = reactive({
        name: "张三",
        age: 25,
        height: 1.78
      })

      // 使用readOnly包裹info
      const roInfo = readonly(info)
      return {roInfo}
    }
</script>

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

比如 对于语句 const info = readonly(obj) , info 对象是不允许被修改的。

当 obj 被修改时, readonly 返回的 info 对象也会被修改;

但是我们不能去修改 readonly 返回的对象 info ;

如果代码本身就非常遵守单向数据流的原则,那么也可以不使用

readonly的其它注意点:

readonly 会返回原始对象的只读代理 也就是它依然是一个 Proxy ,这是一个 proxy 的 set 方法被劫持,并且不能对其进行修改 在开发中常见的 readonly 方法会传入三个类型的参数: 类型一:普通对象 类型二: reactive 返回的对象 类型三: ref 的对象

六、Reactive相关的判断的API

isProxy :检查对象是否是由 reactive 或 readonly 创建的 proxy isReactive : 检查对象是否是由 reactive 创建的响应式代理 如果该代理是 readonly 建的,但包裹了由 reactive 创建的另一个代理,它也会返回 true isReadonly :检查对象是否是由 readonly 创建的只读代理。 toRaw :返回 reactive 或 readonly 代理的原始对象(不建议保留对原始对象的持久引用。请谨慎使用) shallowReactive : 创建一个响应式代理,它跟踪其自身 property 的响应性

但不执行嵌套对象的深层响应式转换 (深层还是原生对象)。

shallowReadonly : 创建一个 proxy ,使其自身的 property 为只读

但不执行嵌套对象的深度只读转换(深层还是可读、可写的)

这些用的可能不会很多,但是也要了解

七、ref其他的API

7.1 toRefs

使用 ES6 的解构语法,对 reactive 返回的对象进行解构获取值,数据将不再是响应式的。

const info = reactive({
    name: "张三",
    age: 25,
    height: 1.78
})

ES6解构:

const {name, age, height} = info;

数据失去响应式

toRefs解构:

const {name, age, height} = toRefs(info);

解构后数据仍然保持响应式

7.2 unref

用于获取一个 ref 引用中的 value 。

如果参数是一个 ref ,则返回内部值,否则返回参数本身 这也是 val = isRef(val) ? val.value : val 的语法糖函数 7.3 isRef

判断值是否是一个ref对象。

7.4 shallowRef

创建一个浅层的 ref 对象。

7.5 triggerRef

手动触发和 shallowRef 创建对象的响应式。

到此这篇关于Vue组合式API--setup中定义响应式数据详解的文章就介绍到这了,更多相关Vue组合式API setup内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!

查看更多关于Vue组合式API--setup中定义响应式数据的示例详解的详细内容...

  阅读:35次