探究Vue3中的Composition API:优化组件逻辑的新利器

一、toRef函数

在 Vue 3.0 中,引入了一种新的响应式 API,即 toReftoRef 函数可以将一个普通值转换为响应式引用类型,这样就可以在模板中直接使用这个响应式引用类型的属性,并且当该属性发生变化时,视图会自动更新。

js 复制代码
<template>
  <div>
      <h2>年龄:{{ age }}</h2>
      <h2>原值:{{person.age }}</h2>
      <button  @click="age++;console.log(age)">年龄增加</button>
  </div>
</template>

<script>
import { reactive} from 'vue';

export default {
  name: 'TodoList',
  setup() {
    // 使用 ref 创建响应式数据
    const person = reactive({
      name: 'JingYu',
      age: 18,
    })
    // 暴露数据和方法给模板使用
    return {
      age:person.age,
      person,
    };
  },
};
</script>

通过控制台打印输出的内容和页面的变化,我们可以观察到,age的值一直在变化,但其不是响应式的,所以页面没有随着值的变化而更新视图。这是因为这种写法就好比我们定义了一个新的变量,将person.age的值18赋值给这个变量。但这个变量并不是响应式的。

这时我们就可以通过toRef将其转换为响应式的

js 复制代码
    const person = reactive({
      name: 'JingYu',
      age: 18,
    })
    // 暴露数据和方法给模板使用
    let age=toRef(person,'age')
    console.log(age)
    return {
      age,
      person,
    };

细心地同学可能从上面的代码中注意到了一点,我在页面中还显示了一个person.age的值。

js 复制代码
  <div>
      <h2>年龄:{{ age }}</h2>
      <h2>原值:{{person.age }}</h2>
      <button  @click="age++;console.log(age)">年龄增加</button>
  </div>

为什么要展示它的值呢,看了将person中age属性单独转换成一个单独的响应式引用之后,你就会发现,页面展示的两个值都会随之改变。

如果你不小心写成了这种形式

js 复制代码
let age=toRef(person.age)

你就会惊奇的发现,页面展示的person.age不会随之改变了.

这里需要注意一下两种写法的区别:

1.当你使用toRef(person, 'age')时,你是在告诉Vue你希望将person对象的age属性转换为一个单独的响应式引用。这意味着当person.age的值发生变化时,引用的值也会相应地更新。 2.toRef(person.age)是将person.age直接转换为响应式引用,而不是从person对象中获取对age属性的引用。这意味着当你修改person.age的值时,引用的值不会自动更新。

二、toRefs函数

torefstoRef的作用是一样的,只不过toRefs是将一个对象的所有属性都变为单独的响应式。

js 复制代码
    setup() {
    // 使用 ref 创建响应式数据
    const person = reactive({
      name: 'JingYu',
      age: 18,
    })
    // 暴露数据和方法给模板使用
    return {
      ...toRefs(person)
    };
  },

通过使用扩展运算符将person对象的所有属性展开返回。成功实现单个属性的响应式。

三、shallowReactive 与 shallowRef

shallow是浅的、浅显的意思。

顾名思义:

  • shallowReactive:只处理对象最外层属性的响应式(浅响应式)
js 复制代码
      <h2>年龄{{ person.age }}</h2>
      <h2>薪资{{ person.job.salary }}</h2>
      <button  @click="person.age++;console.log(person.age,'---')">年龄增加</button>
      <button  @click="person.job.salary++;console.log(person.job.salary)">薪资增加</button>
      const person = shallowReactive({
      name: 'JingYu',
      age: 18,
      job:{
        name:'前端开发',
        salary:8
      }
    })

我们可以观察到年龄是响应式的,而第二层的属性薪资就不是响应式的了。

  • shallowRef:只处理基本数据类型的响应式, 不进行对象的响应式处理。
js 复制代码
      <h2>年龄{{ person.age }}</h2>
      <button  @click="person.age++;console.log(person.age,'---')">年龄增加</button>
      const person = shallowRef({
      name: 'JingYu',
      age: 18,
    })

虽然person.age的值改变了,但数据并不是响应式的,所以页面视图没有更新。

  • 使用场景

1.如果有一个对象数据,结构比较深, 但变化时只是外层属性变化 ===> shallowReactive。

2.如果有一个对象数据,后续功能不会修改该对象中的属性,而是用新的对象来替换 ===> shallowRef。

解释一下这句话:

js 复制代码
      <h2>年龄{{ person.age }}</h2>
      <button  @click="person={age:20}">年龄改变</button>

此时点击按钮页面会变为20,因为我们改变的不是x里面的属性,而是将整个person对象重新赋值,person是响应式。的。

四、readonly 与 shallowReadonly

  • readonly: 让一个响应式数据变为只读的(深只读)。

如果使用readonly修饰对象person,所有的属性都不能修改了。当你修改时控制台还会输出警告.

js 复制代码
<template>
	<h2>姓名:{{name}}</h2>
	<h2>年龄:{{age}}</h2>
	<h2>薪资:{{job.j1.salary}}K</h2>
	<button @click="name+='~'">修改姓名</button>
	<button @click="age++">增长年龄</button>
	<button @click="job.j1.salary++">涨薪</button>
</template>

<script>
	import {reactive,toRefs,readonly} from 'vue'
	export default {
		name: 'HelloWorld',
		setup(){
			//数据
			let person = reactive({
				name:'张三',
				age:18,
				job:{
					j1:{
						salary:20
					}
				}
			})

			person = readonly(person)
			//返回一个对象(常用)
			return {
				...toRefs(person)
			}
		}
	}
</script>

如果使用shallowReadonly修饰对象person,只有最外层的不能修改了。salary还是可以修改的。

js 复制代码
<template>
	<h2>姓名:{{name}}</h2>
	<h2>年龄:{{age}}</h2>
	<h2>薪资:{{job.j1.salary}}K</h2>
	<button @click="name+='~'">修改姓名</button>
	<button @click="age++">增长年龄</button>
	<button @click="job.j1.salary++">涨薪</button>
</template>

<script>
	import {reactive,toRefs,shallowReadonly} from 'vue'
	export default {
		name: 'HelloWorld',
		setup(){
			//数据
			let person = reactive({
				name:'张三',
				age:18,
				job:{
					j1:{
						salary:20
					}
				}
			})

			person = shallowReadonly(person)

			//返回一个对象(常用)
			return {
				...toRefs(person)
			}
		}
	}
</script>

五、toRaw 与 markRaw

  • toRaw是一个用于将响应式对象转换为其原始非响应式版本的函数。
    toRaw函数接受一个响应式对象作为参数,并返回该对象的原始非响应式版本。它实际上返回了一个指向原始对象的引用,而不是创建一个新的对象。
js 复制代码
import { reactive, toRaw } from 'vue' 
const originalObj = { foo: 'bar' } 
const reactiveObj = reactive(originalObj) 
console.log(reactiveObj.foo) // 输出 "bar" 
const rawObj = toRaw(reactiveObj) 
rawObj.foo = 'baz' // 修改原始对象 
console.log(reactiveObj.foo) // 输出 "baz"
  • markRaw是Vue3中用于标记一个对象,使其永远不会转换为响应式对象的函数。

有些值不应被设置为响应式的,例如复杂的第三方类库或Vue组件对象。在这种情况下,我们可以使用markRaw函数。

js 复制代码
import { reactive, markRaw } from 'vue' 
const originalObj = { foo: 'bar' } 
const reactiveObj = reactive(originalObj) 
console.log(reactiveObj.foo) // 输出 "bar" 
const rawObj = markRaw(originalObj) 
console.log(isReactive(reactiveObj)) // 输出 "true" 
console.log(isReactive(rawObj)) // 输出 "false" 
// 尝试将rawObj转换为响应式对象 
const reactiveRawObj = reactive(rawObj) 
console.log(isReactive(reactiveRawObj)) // 输出 "false"

在上面的示例中,我们首先使用reactive函数将originalObj转换为一个响应式对象reactiveObj。然后,我们使用markRaw函数将originalObj转换为其非响应式版本rawObj。我们使用isReactive函数验证了reactiveObj是响应式对象,而rawObj不是。最后,我们尝试将rawObj转换为响应式对象,但是通过isReactive函数验证后发现,转换并未生效。

六、provide 与 inject

在Vue 3中,provideinject是用于跨层级组件通信的两种方法。

provide方法允许父组件向下传递数据给子组件。provide() 接受两个参数:第一个参数是要注入的 key,可以是一个字符串或者一个 symbol,第二个参数是要注入的值。

inject方法允许子组件从父组件中获取传递的数据。它接收两个参数:一个是需要注入的键值,另一个是默认值。如果父组件没有提供该键值,则inject方法将返回默认值。

具体用法

祖组件中:

js 复制代码
setup(){
	......
    let car = reactive({name:'奔驰',price:'40万'})
    provide('car',car)
    ......
}

后代组件:

js 复制代码
setup(props,context){
	......
    const car = inject('car')
    return {car}
	......
}

响应式数据的判断

  • isRef: 检查一个值是否为一个 ref 对象
  • isReactive: 检查一个对象是否是由 reactive 创建的响应式代理
  • isReadonly: 检查一个对象是否是由 readonly 创建的只读代理
  • isProxy: 检查一个对象是否是由 reactive 或者 readonly 方法创建的代理
相关推荐
Boilermaker19921 小时前
【Java EE】SpringIoC
前端·数据库·spring
中微子1 小时前
JavaScript 防抖与节流:从原理到实践的完整指南
前端·javascript
天天向上10241 小时前
Vue 配置打包后可编辑的变量
前端·javascript·vue.js
芬兰y2 小时前
VUE 带有搜索功能的穿梭框(简单demo)
前端·javascript·vue.js
好果不榨汁2 小时前
qiankun 路由选择不同模式如何书写不同的配置
前端·vue.js
小蜜蜂dry2 小时前
Fetch 笔记
前端·javascript
拾光拾趣录2 小时前
列表分页中的快速翻页竞态问题
前端·javascript
小old弟2 小时前
vue3,你看setup设计详解,也是个人才
前端
Lefan2 小时前
一文了解什么是Dart
前端·flutter·dart
Patrick_Wilson2 小时前
青苔漫染待客迟
前端·设计模式·架构