深入与浅谈 Vue 中计算属性和侦听器的区别和使用(Vue3版本为例)

news2024/11/25 0:24:44

#五一技术创作马拉松#

在这里插入图片描述

📋前言

计算属性 computed 和侦听器 watch 都是 Vue.js 框架中用来响应式更新视图的重要概念。在 Vue 项目开发中,这两个技术点是非常重要的,同时也是 Vue 基础中不可缺少的知识点。在面试中,计算属性 computed 和侦听器 watch也是经常出现的考点,作为前端开发也是必须要掌握的,这篇文章详细看看如何使用计算属性 computed 和侦听器 watch ,以及谈谈之前遇到的关于计算属性 computed 和侦听器 watch 的面试题。

📚相关文章:浅谈在 Vue2 和 Vue3 中计算属性和侦听器的一些变化
🔥官方介绍:计算属性 | Vue.js、侦听器 | Vue.js


文章目录

  • 📋前言
  • 🎯计算属性
    • 🧩计算属性的定义
    • 🧩计算属性的用法
    • 🧩计算属性的缓存
  • 🎯侦听器
    • 🧩侦听器性的定义
    • 🧩侦听器的用法
    • 🧩watchEffect() 函数
    • 🧩watch 与 watchEffect
    • 🧩停止侦听器
  • 🎯二者区别
  • 📝最后


🎯计算属性

🧩计算属性的定义

Vue.js 的插值在 HTML 模板中是支持 JavaScript 表达式的。但是如果当表达式过于复杂,模板代码就会变得非常臃肿且可读性差,难以维护。比如说,反转字符串这段代码。

<p>{{message.splice('').reverse().join('') }}</p>

这个表达式首先要将 message 变量按照每一个字母分解成一个字符串数组,然后将字符串数组翻转,接着再拼接字符串数组里面的内容成一个新的字符串,最终达到字符串翻转的效果。这里一个表达式就含有三个 JavaScript 方法,逻辑过于复杂,可读性非常差。

如果页面中有多个地方需要这个字符串翻转的表达式,那整体的 Vue 模板的代码将会变得非常难以维护,而且在 MVVM 模式下,模板中过多的处理数据计算,会导致模板和控制器之间高度耦合。这种情况下,就需要使用计算属性了。

计算属性的作用就是为了解决表达式逻辑过于复杂的场景,使页面尽可能没有数据复杂处理。计算属性是以函数的形式出现在组件的 computed 选项中,在新版的 Vue 3.x 的 setup 语法糖里,可以将计算属性的具体实现函数传递给 computed() 方法,然后赋值给一个变量。如果将上述翻转字符串的表达式通过计算属性实现,代码如下。

<template>
  <p>原始text:{{ message }}</p>
  <p>计算属性text:{{ reverseMessage }}</p>
</template>

<script setup>
import { computed, ref } from "vue";
const message = ref("hello world");
const reverseMessage = computed(() => {
  return message.value.split("").reverse().join("");
});
</script>

在这里插入图片描述
这里的 reversedMessage 就是计算属性,它能够像其他变量一样,通过 Mustache 语法作为插值绑定到模板中。而且如果 message 变量被修改,对应的 reversedMessage 的结果也会立刻被修改。

🧩计算属性的用法

在 Vue 3.x 新版的 setup 语法中,计算属性有两种用法。第一种如上述的代码例子,直接将实现函数以参数的形式,然后传递给 computed() 方法中实现。另一种则是分别具体实现 getter 方法和 setter 方法,然后再封装成对象传递给 computed() 方法。

我们看下面这段代码,商品的单价在组件的 data 中 price 表示,通过计算属性,来看看买多个商品要多少钱。

<template>
  <p>单价:{{ price }} 元/件</p>
  <p>总价:{{ totalPrice }} 元</p>
</template>

<script setup>
import { computed, ref } from "vue";
const price = ref(6);
const totalPrice = computed({
  get: () => {
    return price.value * 5;
  },
  set: (value) => {
    price.value = value / 5;
  },
});
</script>

这里在 computed() 方法中接收了含义 get 变量和 set 变量的对象,其中 get 变量对应 getter 方法实现,set 则是对应 setter 方法实现。
在这里插入图片描述
在浏览器中,可以使用 vue devtools 这个插件来手动修改 totalPrice 的值,然后可以看到页面的单价和总价都发生了变化。
在这里插入图片描述
这里的数字变化是因为在代码中实现了 tolalPrice 计算属性的 selter 方法,当用户修改 totaiPrice 的数值,totalPricesetter 方法就会被调用,因而在 setter 内部被修改的 price 数值也会发生变化。但是此时整个调用链还没有结束,因为 price 的数值发生变动,触发了响应式机制,所以 totalPrice 的数值会根据最新的 price 数值重新计算,渲染最后的计算结果显示在页面上。从而达到单价和总价两个数字同时发生改变

这里有两个细节点需要注意:第一,如果计算属性没有实现 setter 方法,那么在 vue devtools 里计算属性变量是不可以被修改的,第二,如果被计算属性监测的变量在计算属性的 setter 方法内被修改,那么计算属性最终的数值会以被监测变量变化之后的最新数值重新计算或渲染。

🧩计算属性的缓存

计算属性的缓存是指,当计算属性依赖的数据发生变化时,计算属性会重新计算并缓存新的值。在缓存有效期内,如果再次访问该计算属性,则直接返回缓存中的值,不需要重新计算。

❗注意:如果计算属性函数中的数据不是响应式依赖,就不会触发计算属性的缓存机制。比如 Date().now()Date().getTime() 都不是响应式依赖,因此它们的计算属性不会更新

这里我们可以看一个很经典的对比,对比看计算属性的缓存和方法,看看页面渲染的效果。我们以下面这段代码作为案例。

<template>
  <p>单价:{{ price }} 元/件</p>
  <p>computed总价:{{ totalPrice }} 元</p>
  <p>computed总价:{{ totalPrice }} 元</p>
  <p>computed总价:{{ totalPrice }} 元</p>
  <p>methods总价:{{ methods() }} 元</p>
  <p>methods总价:{{ methods() }} 元</p>
  <p>methods总价:{{ methods() }} 元</p>
</template>

<script setup>
import { computed, ref, watch, watchEffect } from "vue";
const price = ref(6);
const totalPrice = computed(() => {
  console.log("调用computed()");
  return price.value * 5;
});
const methods = () => {
  console.log("调用methods()");
  return price.value * 5;
};
</script>

在这里插入图片描述
如上图,我们可以看到页面渲染效果和控制台打印的信息,计算属性的函数只调用了一次,而普通的方法函数则调用了多次。原因就是计算机属性将基于它的响应式依赖关系缓存。即便是多次使用只需要计算一次,而方法则是用多少次就会被执行多少次。

这种缓存机制可以有效地提高应用程序的性能。例如,如果计算属性需要遍历一个大型数组并进行复杂的运算,而且这个数组的数据变化频繁,如果每次都重新计算,会极大地降低应用程序的响应速度。但是,如果使用了计算属性的缓存机制,只有数组数据发生变化时才会重新计算,大大节约了计算资源和时间。

需要注意的是,如果计算属性依赖的数据未被定义为响应式的,或者计算属性的 getter 函数中包含了异步操作或者时间较长的同步操作,那么计算属性的缓存可能会失效或者带来其他副作用。因此,需要根据实际情况谨慎使用计算属性的缓存机制,或者使用其他方式来解决以上问题。面对这种情况,我们换一种思路来使用侦听器。


🎯侦听器

🧩侦听器性的定义

通常情况下,我们可以使用计算属性来处理数据的计算和衍生,但是在某些情况下,计算属性并不够灵活,不能满足我们的需求。例如,需要在数据变化时执行异步操作、发送网络请求或者操作 DOM 等。这个时候,我们可以使用侦听器来处理这些需求。

在 Vue.js 中,侦听器是一个函数,它可以监听响应式数据的变化,当数据发生变化时,侦听器会被触发执行。下面是一个使用侦听器的例子。

<template>
  <input type="number" v-model="num" />
  <p>result为:{{ result }}</p>
</template>

<script setup>
import { ref, watch } from "vue";
const num = ref(0);
const result = ref(0);
watch(
  () => num.value,
  (newValue, oldValue) => {
    console.log("旧值为:", oldValue, "新值为:", newValue);
    return (result.value = num.value * num.value);
  }
);
</script>

在这里插入图片描述
上面的代码,我们通过输入框输入数字修改 num 值,侦听器通过监听 num 值的变化,计算输入数字的结果。这个功能与计算属性很类似,但区别是侦听器可以检测数值的变化,以及在控制台的输出。

侦听器其实是计算属性的底层逻辑实现。不同之处在于侦听器可以知道变化前后的数据数值以及在侦听器之内可以使用异步方法,在侦听器里添加异步操作是一个很重要的操作,这里可以是网络请求,也可以是开销较大的计算,这是计算属性中没有的。

🧩侦听器的用法

在 Vue 3.x 的 setup 语法中,侦听器的语法如下。

watch{
	被监测属性,
	监测到变化时的回调函数,
	参数选项
}

其中,第二个监测到变化时的回调函数的可传参数如下。

() => {
	// 无参数
}
(value) => {
	// 变化后的数值作为参数传入
}
(newValue, oldValue) => {
	// 变化前的数值和变化后的数值作为参数传入
}

所以侦听器不仅可以监测基本数据类型的 ref() 数据,同时还可以监测引用数据类型的 reactive() 数据。不过监测引用数据类型的数据,需要将 { deep: true } 属性作为第三个参数传给 watch() 函数。

{ deep: true } 选项可以告诉 Vue 在监测数据变化时,递归到深层嵌套的属性值中去。这样即使属性值是引用类型,只要其内部的属性值发生了变化,watch 回调函数也能够正常触发执行。看下面这段代码。

<template>
  <div>
    <p>user: {{ data.user }}</p>
    <button @click="updateUser">Update User</button>
  </div>
</template>

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

const data = reactive({
  user: {
    name: "张三",
    age: 20,
    hobby: ["唱歌", "跳舞"],
  },
});

watch(
  () => data.user,
  (newValue, oldValue) => {
    console.log("user changed", newValue, oldValue);
  },
  { deep: true }
);

function updateUser() {
  data.user.name = "李四";
  data.user.hobby.push("游泳");
}
</script>

在这里插入图片描述
在这个例子中,我们创建了一个包含嵌套属性的 reactive()数据对象,并使用 watch 监测了 user 属性。在 watch 选项中,我们使用 { deep: true } 选项来告诉 Vue 在监测数据变化时递归到嵌套属性中去。

当用户点击 Update User 按钮时,updateUser 函数会更新 user 属性中的 namehobby 属性值。这里注意,我们使用了 data.user.namedata.user.hobby.push() 的方式来更新属性值,而不是直接赋值一个新的对象。这样就能够确保 user 属性的引用地址不变,从而让 deep 选项生效。
在这里插入图片描述
此时,由于我们监测了 user属性,并使用了 { deep: true } 选项,当 user 属性中的嵌套属性值发生变化时,watch 回调函数就会被触发执行,打印出相应的信息。

❗注意:深度侦听需要遍历被侦听对象中的所有嵌套的属性,当用于大型数据结构时,开销很大。因此请只在必要时才使用它,并且要留意性能。

即时回调的侦听器​
watch 默认是懒执行的:仅当数据源变化时,才会执行回调。但在某些场景中,我们希望在创建侦听器时,立即执行一遍回调。举例来说,我们想请求一些初始数据,然后在相关状态更改时重新请求数据。

我们可以通过传入 immediate: true 选项来强制侦听器的回调立即执行:

watch(source, (newValue, oldValue) => {
  // 立即执行,且当 `source` 改变时再次执行
}, { immediate: true })

🧩watchEffect() 函数

侦听器的回调使用与源完全相同的响应式状态是很常见的。例如下面的代码,在每当 todoId 的引用发生变化时使用侦听器来加载一个远程资源:

const todoId = ref(1)
const data = ref(null)

watch(todoId, async () => {
  const response = await fetch(
    `https://jsonplaceholder.typicode.com/todos/${todoId.value}`
  )
  data.value = await response.json()
}, { immediate: true })

特别是注意侦听器是如何两次使用 todoId 的,一次是作为源,另一次是在回调中。

我们可以用 watchEffect 函数 来简化上面的代码。watchEffect()允许我们自动跟踪回调的响应式依赖。上面的侦听器可以重写为:

watchEffect(async () => {
  const response = await fetch(
    `https://jsonplaceholder.typicode.com/todos/${todoId.value}`
  )
  data.value = await response.json()
})

这个例子中,回调会立即执行,不需要指定 immediate: true。在执行期间,它会自动追踪 todoId.value 作为依赖(和计算属性类似)。每当 todoId.value 变化时,回调会再次执行。有了 watchEffect(),我们不再需要明确传递 todoId 作为源值。

对于这种只有一个依赖项的例子来说,watchEffect() 的好处相对较小。但是对于有多个依赖项的侦听器来说,使用 watchEffect() 可以消除手动维护依赖列表的负担。此外,如果你需要侦听一个嵌套数据结构中的几个属性,watchEffect() 可能会比深度侦听器更有效,因为它将只跟踪回调中被使用到的属性,而不是递归地跟踪所有的属性。

✅TIP:watchEffect 仅会在其同步执行期间,才追踪依赖。在使用异步回调时,只有在第一个 await 正常工作前访问到的属性才会被追踪。

🧩watch 与 watchEffect

watch 和 watchEffect 都能响应式地执行有副作用的回调。它们之间的主要区别是追踪响应式依赖的方式:

  • watch 只追踪明确侦听的数据源。它不会追踪任何在回调中访问到的东西。另外,仅在数据源确实改变时才会触发回调。watch 会避免在发生副作用时追踪依赖,因此,我们能更加精确地控制回调函数的触发时机。
  • watchEffect,则会在副作用发生期间追踪依赖。它会在同步执行过程中,自动追踪所有能访问到的响应式属性。这更方便,而且代码往往更简洁,但有时其响应性依赖关系会不那么明确。

🧩停止侦听器

setup()<script setup> 中用同步语句创建的侦听器,会自动绑定到宿主组件实例上,并且会在宿主组件卸载时自动停止。因此,在大多数情况下,你无需关心怎么停止一个侦听器。

一个关键点是,侦听器必须用同步语句创建:如果用异步回调创建一个侦听器,那么它不会绑定到当前组件上,你必须手动停止它,以防内存泄漏。如下方这个例子:

<script setup>
import { watchEffect } from 'vue'

// 它会自动停止
watchEffect(() => {})

// ...这个则不会!
setTimeout(() => {
  watchEffect(() => {})
}, 100)
</script>

要手动停止一个侦听器,请调用 watchwatchEffect 返回的函数:

const unwatch = watchEffect(() => {})
// ...当该侦听器不再需要时
unwatch()

注意,需要异步创建侦听器的情况很少,请尽可能选择同步创建。如果需要等待一些异步数据,你可以使用条件式的侦听逻辑:

// 需要异步请求得到的数据
const data = ref(null)

watchEffect(() => {
  if (data.value) {
    // 数据加载后执行某些操作...
  }
})

🎯二者区别

通过上面的内容和例子,我们算是简单了解和学习了计算属性和侦听器,在前端开发的面试中,计算属性和侦听器也是经常出现的考核点,计算属性和侦听器的区别则是面试官经常问道的题目。通过上面的内容,我们也大概可以把这些区别说明白了,接下来我们简单总结一下。

❓问:Vue 中计算属性和侦听器的区别是什么?

📝答:如下。

  • 计算属性是基于它所依赖的响应式数据动态计算的值,只有在计算属性所依赖的数据发生变化时才会重新计算,且计算结果会被缓存起来。相比之下,侦听器监听的是某个特定的数据变化,当该数据变化时,会触发执行回调函数。

  • 计算属性定义的是 getter 函数,只能用于获取数据,不能用于设置数据。而侦听器定义的是回调函数,可以进行任何逻辑处理,包括修改数据本身或者执行其他操作。

  • 在使用场景上,计算属性通常用于将数据转换为显示需要的格式,而侦听器则适合处理需要执行异步或复杂逻辑的情况。


📝最后

计算属性 computed 和侦听器 watch 都是实现响应式编程的重要工具,可以方便地处理复杂的业务数据变化场景。了解它们的区别和应用场景,能够帮助我们更好地设计和优化我们的 Vue 组件和应用。
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/476873.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【前端知识】内存泄漏与垃圾回收机制 (下)

【前端知识相关分享】内存泄漏与垃圾回收机制 &#xff08;下&#xff09; 6. 内存泄漏的解决方法6.1 解决方法概述6.2 什么是垃圾6.3 垃圾回收机制的定义及规则6.4 垃圾回收算法的基本流程 7. 垃圾回收的常见算法7.1 引用计数7.2 标记清除7.3 复制算法7.4 标记整理&#xff08…

十大经典排序算法总结(C语言版本)

前言&#xff1a;排序算法是最经典的算法知识&#xff0c;也是每个合格程序员应该需要掌握的知识点。考虑到排序算法通常代码简短&#xff0c;逻辑思维强和应用范围广等特性&#xff0c;排序算法题目便成为了面试中的常客。在面试中最常考的是快速排序和归并排序等基本的排序算…

SQL(基础)

DDL: 数据定义语言 Definition&#xff0c;用来定义数据库对象&#xff08;数据库、表、字段&#xff09;CREATE、DROP、ALTER DML: 数据操作语言 Manipulation&#xff0c;用来对数据库表中的数据进行增删改 INSERT、UPDATE、DELETE 注意&#xff1a; DDL是改变表的结构 DML…

一以贯之:从城市网络到“城市一张网”

《论语里仁》中子曰&#xff1a;“参乎&#xff0c;吾道一以贯之”。 孔子所说的“一以贯之”&#xff0c;逐渐成为了中国文化与哲学的重要组成部分&#xff0c;指明事物发展往往需要以标准化、集约化、融合化作为目标。这种智慧在数字化发展中格外重要。从云计算、大数据技术模…

一个快速去除黑背景和其他颜色背景,生成透明PNG图的小工具

做粒子效果或者其他一些图案的时候&#xff0c;时常能找到不少原图&#xff0c;但是却有黑色的背景或者其他背景色&#xff0c;导致用起来比较麻烦。这个小工具就可以方便的去除黑背景&#xff0c;生成透明PNG图&#xff0c;可以把想要的图案方便的取出来。 链接请见&#xff…

【Arduino 和 DS3231 实时时钟教程】

【Arduino 和 DS3231 实时时钟教程】 1. 概述2. 原理分析3. DS3231 实时时钟4. 编程1. 概述 在本Arduino教程中,我们将学习如何使用DS3231实时时钟模块。您可以观看以下视频或阅读下面的书面教程。 2. 原理分析 这里出现的第一个问题是,当Arduino本身具有内置计时器时,为什…

利用倾斜摄影超大场景的三维模型轻量化技术如何提高网络传输的效率?

利用倾斜摄影超大场景的三维模型轻量化技术如何提高网络传输的效率&#xff1f; 倾斜摄影超大场景的三维模型轻量化在网络传输中的效率可以通过以下几个方面进行提高&#xff1a; 一、数据压缩 对于倾斜摄影超大场景的三维模型数据&#xff0c;可以采用数据轻量化压缩技术进…

如何利用ChatPDF快速阅读英文论文,帮你写文章

如何利用ChatPDF快速阅读英文论文&#xff0c;帮你写文章 英语渣狂喜&#xff5e;确实惊艳到我了&#xff01; 使用平台&#xff1a;https://www.chatpdf.com/ 1、上传PDF 访问官网&#xff1a;https://www.chatpdf.com/&#xff0c;界面很美&#xff0c;点击直接上传 PDF&…

Python小姿势 - Python学习笔记——类与对象

Python学习笔记——类与对象 类与对象是面向对象编程的两个基本概念。类是对象的抽象概念&#xff0c;对象是类的具体表现。 类是对一类事物的抽象&#xff0c;它是描述一类事物的模板&#xff0c;而对象是类的具体表现。对象是类的实例&#xff0c;类是对象的模板。 举个例子&…

【Midjourney】Midjourney 连续性人物创作 ④ ( 使用 URL + Seed 随机种子生成连续性的人物 )

文章目录 一、生成图片并获取 Seed二、使用 URL Seed 随机种子生成连续性的人物 使用 URL 链接 和 Seed 随机种子 生成连续性人物 , 必须先生成一组图片 , 然后按 U 按钮 , 选择一张大图 , 之后所有的连续性人物图片都基于该图片进行生成 ; 使用 URL Seed 随机种子生成连续性…

吴恩达与 OpenAI 联手打造《ChatGPT 提示工程》中文版教程,开启智能对话新篇章!

ChatGPT 的使用技巧 ChatGPT 上线至今&#xff0c;已经快5个月了&#xff0c;但是不少人还没真正掌握它的使用技巧。 其实&#xff0c;ChatGPT 的难点&#xff0c;在于 Prompt&#xff08;提示词&#xff09;的编写&#xff0c;OpenAI 创始人在今年2月时&#xff0c;在Twitter…

数据结构学习分享之顺序表详解

数据结构第二课 1. 前言2. 线性表3. 顺序表3.1 概念以及结构3.11 静态顺序表3.12 动态顺序表 4. 顺序表的实现4.1 顺序表内容的命名4.2 初始化结构4.3 初始化函数4.4 扩容函数4.5 尾插函数4.6 打印函数4.7 尾删函数4.8 头插函数4.9 头删函数4.10 销毁顺序表 5. 写代码时应该注意…

安装python以及编辑器pycharm

文章目录 前言一、安装python1.安装python2.测试python3.查看环境变量 二、安装Pycharm1.下载Pycharm2.查看环境变量3.测试 总结 前言 python是重要的程序语言之一 本文介绍如何安装python&#xff0c;以及如何安装python编辑器----Pycharm 一、安装python 1.安装python 首先…

SpringCloud_OpenFeign服务调用和Resilience4J断路器

文章目录 一、负载均衡概论1、服务器负载均衡2、客户端负载均衡3、客户端负载均衡策略(SpringCloudRibbon)4、客户端负载均衡策略(SpringCloudLoadBalancer) 二、SpringCloudOpenFeign服务调用1、OpenFeign服务调用的使用2、OpenFeign服务调用的日志增强3、OpenFeign服务调用超…

下载——安装——使用FinalShell

下载——安装——使用FinalShell FinalShell简介&#xff1a;下载&#xff1a;使用&#xff1a; FinalShell简介&#xff1a; FinalShell是一款免费的国产的集SSH工具、服务器管理、远程桌面加速的软件&#xff0c;同时支持Windows&#xff0c;macOS&#xff0c;Linux&#xf…

总结吴恩达 ChatGPT Prompt 免费课程

吴恩达联合 OpenAI 官方&#xff0c;发布了免费的 ChatGPT Prompt 视频教程。 链接&#xff1a;https://learn.deeplearning.ai/chatgpt-prompt-eng/lesson/2/guidelines 视频纯英文&#xff0c;小姐姐的英伦腔&#xff0c;听得很舒服。 我看了第一集&#xff0c;讲了四个技巧&…

阿里云国际版ACE与国内版ACE区别

1.国际版ACE与国内版ACE有哪些不同 2.国际版ACP/ACE约考流程 2.1 登录VUE官方网站约考 https://www.pearsonvue.com.cn/Clients/Alibaba-Cloud-Certification.aspx ​ 2.2 如果之前有注册过账户&#xff0c;那就直接登录&#xff0c;如果还没有账户&#xff0c;那就创建账户 2.…

React之动态渲染菜单(无状态机)

首先如果需要动态渲染菜单&#xff0c;并且根据不同用户获取不同的菜单&#xff0c;需要下面步骤&#xff1a; 我们动态渲染菜单需要使用递归的方式 1️⃣&#xff1a;首先用户的菜单都是从后端获取的&#xff0c;所以需要先发送请求&#xff0c;拿到菜单&#xff0c;然后将数…

AI 命名之羊驼

转眼进入 AI 时代&#xff0c;ChatGPT 吹起了一股大语言模型之风&#xff0c;恐怕羊驼们绝不曾想到&#xff0c;自己的种族竟也被卷入其中。 AI 产品的命名一向偏好晦涩的缩写。GPT&#xff08;Generative Pre-trained Transformers&#xff09;已经是最简明直白的一类。相比之…

gRPC入门教程

1.简介 gRPC是Google开发的一个跨平台、开源的远程过程调用(RPC)框架&#xff0c;可以使用Protocol Buffers作为接口定义语言(IDL)和底层消息交换格式。 在gRPC中&#xff0c;客户端应用程序可以直接调用位于不同机器上的服务器应用程序的方法&#xff0c;就像本地对象一样&a…