当前位置: 首页 > news >正文

Vue3 响应式API:高级函数(二)

shallowRef()

  • shallowRef 是一个特殊的 ref 创建函数,它允许你创建一个只追踪顶层属性变化的响应式引用。与 ref 不同的是,shallowRef 创建的响应式引用对其内部值的深层嵌套属性是不敏感的,也就是说,只有当 shallowRef 的 .value 被一个新对象替换时,Vue 的响应式系统才会追踪这个变化。
  • shallowRef 的主要作用是创建一个只对其顶层属性敏感的响应式引用。这在某些场景下非常有用,比如当你想要引用一个大型对象或数组,并且只需要关心该对象或数组的引用变化,而不关心其内部属性的变化时。但是,请注意不要意外地修改其内部属性,因为这可能不会触发视图更新。
import { shallowRef } from 'vue';  // 创建一个 shallowRef  
const shallow = shallowRef({  nested: {  prop: 'Hello Vue 3!'  }  
});  
// 修改 shallowRef 的顶层属性(即引用变化),这将触发视图更新  
shallow.value = {  newNested: {  prop: 'New value!'  }  
};  
// 尝试修改 shallowRef 的嵌套属性(内部对象的变化),这不会触发视图更新  
shallow.value.nested.prop = 'This change will not trigger a view update.';

triggerRef()

  • 强制触发依赖于一个浅层 ref 的副作用,这通常在对浅引用的内部值进行深度变更后使用。
  • 如果你确实在某个地方看到了 triggerRef() 这样的函数,那么它可能是某个库或应用程序中定义的自定义函数,而不是 Vue 3 官方 API 的一部分。
const shallow = shallowRef({greet: 'Hello, world'
})// 触发该副作用第一次应该会打印 "Hello, world"
watchEffect(() => {console.log(shallow.value.greet)
})// 这次变更不应触发副作用,因为这个 ref 是浅层的
shallow.value.greet = 'Hello, universe'// 打印 "Hello, universe"
triggerRef(shallow)

customRef()

  • customRef 是一个高阶函数,它允许你创建一个自定义的 ref,以精细地控制 ref 的 get 和 set 行为。当你需要自定义响应式引用的行为时,customRef 是非常有用的。
  • customRef 接收一个工厂函数作为参数,该工厂函数返回一个对象,该对象必须包含 get 和 set 方法。get 方法返回引用的值,而 set 方法接收新的值并更新引用。
import { customRef, ref } from 'vue';  function customRefWithValidation(value, validator) {  // 使用内置的 ref 创建一个响应式引用  const innerRef = ref(value);  // 返回一个自定义的 ref 对象  return customRef((track, trigger) => {  // get 方法:返回内部 ref 的值  return {  get() {  track(); // 通知 Vue 这个 ref 正在被读取  return innerRef.value;  },  set(newValue) {  // 可以在这里添加自定义逻辑,比如验证  if (validator(newValue)) {  innerRef.value = newValue;  trigger(); // 通知 Vue 这个 ref 的值已经改变  } else {  console.warn('Invalid value!');  }  }  };  });  
}  // 使用自定义的 ref  
const validatedRef = customRefWithValidation(0, (value) => typeof value === 'number' && value >= 0);  // 设置值  
validatedRef.value = 5; // 成功  
validatedRef.value = -1; // 失败,并在控制台中打印警告

shallowReactive()

  • 创建一个浅层的响应式对象。与 reactive 不同,shallowReactive 只会对对象的顶层属性进行响应式追踪,而不会递归地追踪其嵌套对象的属性。
  • 不要意外修改嵌套属性:由于 shallowReactive 不会追踪嵌套对象的属性变化,因此如果你尝试修改嵌套对象的属性并期望视图更新,你可能会遇到意外的行为。
  • 与 reactive 的对比:与 reactive 不同,shallowReactive 是浅层的,只追踪顶层属性的变化。如果你需要追踪嵌套对象的属性变化,请使用 reactive。
  • 与其他工具函数的结合使用:你可以将 shallowReactive 与其他 Vue 3 的响应式工具函数(如 watch、computed 等)结合使用,以实现更复杂的响应式逻辑。
import { shallowReactive } from 'vue';  const state = shallowReactive({  nested: {  prop: 'Hello Vue 3!'  },  shallowProp: 'Shallow prop'  
});  // 修改顶层属性,将触发视图更新  
state.shallowProp = 'New shallow prop value';  // 修改嵌套对象的属性,不会触发视图更新  
state.nested.prop = 'New nested prop value'; // 这个变化不会被追踪

shallowReadonly()

  • 创建一个浅层的只读响应式对象。与 readonly 函数类似,shallowReadonly 会创建一个响应式对象的代理,但不同之处在于 shallowReadonly 只会将对象的顶层属性标记为只读,而不会递归地将对象的嵌套属性也标记为只读。
  • 不要意外修改嵌套属性:虽然 shallowReadonly 不会阻止你修改嵌套对象的属性,但这样做并不会触发 Vue 的响应式系统,因此不会触发相关的依赖更新。
  • 与 readonly 的对比:与 readonly 不同,shallowReadonly 是浅层的,只会将对象的顶层属性标记为只读。如果你需要递归地将对象的所有属性(包括嵌套属性)都标记为只读,请使用 readonly。
  • 与其他工具函数的结合使用:你可以将 shallowReadonly 与其他 Vue 3 的响应式工具函数(如 computed、watch 等)结合使用,以实现更复杂的响应式逻辑。但请记住,由于 shallowReadonly 是浅层的,它不会追踪嵌套属性的变化。
import { shallowReadonly } from 'vue';  const original = {  nested: {  prop: 'Hello Vue 3!'  },  shallowProp: 'Shallow prop'  
};  const state = shallowReadonly(original);  // 读取顶层属性  
console.log(state.shallowProp); // 'Shallow prop'  // 读取嵌套对象的属性  
console.log(state.nested.prop); // 'Hello Vue 3!'  // 尝试修改顶层属性,会抛出错误  
// state.shallowProp = 'New shallow prop value'; // TypeError: Cannot assign to read only property 'shallowProp' of object  // 尝试修改嵌套对象的属性,这将成功(但Vue不会追踪这个变化)  
state.nested.prop = 'New nested prop value'; // 不会抛出错误,但Vue不会追踪这个变化

toRaw()

  • 用于获取一个响应式对象的原始对象。这意味着你可以通过 toRaw 函数绕过 Vue 的响应式系统,直接访问原始的非代理对象。这在某些需要直接操作原始对象的场景下可能会很有用。
  • 不要意外修改原始对象:虽然 toRaw 允许你直接访问和修改原始对象,但这可能会导致与 Vue 的响应式系统不一致的情况。请确保你了解这样做的后果,并在必要时采取适当的措施来同步状态。
  • 与 Vue 的响应式系统分离:通过 toRaw 获取的原始对象与 Vue 的响应式系统完全分离。这意味着对原始对象的任何修改都不会触发 Vue 的依赖更新或视图渲染。
  • 谨慎使用:由于 toRaw 可以绕过 Vue 的响应式系统,因此在使用时需要格外小心。请确保你了解你的代码和 Vue 的响应式系统的工作原理,并谨慎地决定何时使用 toRaw。
import { reactive, toRaw } from 'vue';  const original = {  count: 0  
};  const state = reactive(original);  // 通过响应式对象修改值  
state.count = 1;  // 获取原始对象  
const raw = toRaw(state);  // 原始对象和响应式对象不是同一个对象  
console.log(state === raw); // false  // 原始对象和响应式对象的内容是相同的  
console.log(state.count === raw.count); // true  // 直接修改原始对象,响应式系统不会追踪这个变化  
raw.count = 2;  // 响应式对象的内容没有被改变  
console.log(state.count); // 1

markRaw()

  • 用于将一个对象标记为非响应式的,从而确保它不会被 Vue 的响应式系统转换为代理对象。这在某些情况下可能是有用的,特别是当你有一个不应该被 Vue 追踪其属性变化的对象时。
  • 不要意外修改非响应式对象:虽然 markRaw 允许你创建一个非响应式的对象,但这并不意味着你可以随意修改它而不用担心任何副作用。如果你正在与组件或其他 Vue 的功能交互,并且错误地修改了一个非响应式对象,可能会导致不一致的行为或难以调试的问题。
  • 谨慎使用:由于 markRaw 会绕过 Vue 的响应式系统,因此在使用时需要格外小心。请确保你了解你的代码和 Vue 的响应式系统的工作原理,并谨慎地决定何时使用 markRaw。
  • 不与其他响应式功能结合使用:一旦一个对象被 markRaw 标记为非响应式,你就不应该再使用 Vue 的其他响应式功能(如 reactive、ref、computed 等)来尝试使其变得响应式。这样做可能会导致不可预测的行为。
import { reactive, markRaw } from 'vue';  // 创建一个普通的对象  
const rawObject = {  value: 'I am raw!'  
};  // 使用 markRaw 标记该对象为非响应式  
const nonReactiveObject = markRaw(rawObject);  // 尝试使用 reactive 包裹它,但 Vue 会识别到它已经被标记为非响应式  
const reactiveWrapper = reactive(nonReactiveObject);  // reactiveWrapper 实际上与 nonReactiveObject 是同一个对象,没有被转换为代理  
console.log(reactiveWrapper === nonReactiveObject); // true  // 修改 rawObject 的属性,Vue 不会追踪这个变化  
rawObject.value = 'I am still raw!';  // 因为 reactiveWrapper 是 rawObject 的引用,所以它的值也会变化,但 Vue 并没有追踪这个变化  
console.log(reactiveWrapper.value); // 'I am still raw!'

effectScope()

  • effectScope 是一个相对高级的工具函数,它允许你创建一个独立的作用域(scope)来管理副作用(effects)。副作用通常指的是那些依赖于响应式数据变化并执行相应操作的函数,例如计算属性、侦听器(watchers)或渲染函数。
  • 谨慎使用:effectScope 是一个相对高级的工具,通常用于处理复杂的响应式逻辑。在大多数情况下,使用 Vue 提供的 reactive、ref、computed 和 watch 等 API 就足够了。
  • 资源管理:当使用 onScopeDispose 添加清理逻辑时,请确保正确管理资源,以避免内存泄漏或其他问题。
  • 副作用的触发:在 effectScope 内的副作用仍然遵循 Vue 的响应式系统规则。只有当它们依赖的响应式数据发生变化时,它们才会被触发执行。
  • 性能考虑:创建过多的作用域或副作用可能会对性能产生影响。因此,请确保在使用 effectScope 时考虑性能因素,并尽量保持作用域和副作用的数量在可控范围内。
import { reactive, effectScope, effect, onScopeDispose } from 'vue';  // 创建一个响应式对象  
const state = reactive({ count: 0 });  // 创建一个新的 effectScope  
const scope = effectScope();  // 在该作用域内创建一个 effect  
scope.run(() => {  effect(() => {  console.log('count changed:', state.count);  });  // 当作用域被销毁时,执行一些清理逻辑  onScopeDispose(() => {  console.log('Scope disposed!');  });  
});  // 触发 state.count 的变化  
state.count++; // 输出 "count changed: 1"  // 你可以稍后手动停止并销毁作用域及其内部的 effects  
scope.stop(); // 销毁作用域,输出 "Scope disposed!"  // 再次触发 state.count 的变化,但不会有任何输出  
// 因为作用域已经被销毁了  
state.count++;

getCurrentScope()

  • getCurrentScope 是一个与 effectScope 相关的工具函数,但它并不是 Vue 官方 API 的一部分。
  • 在大多数情况下,你不需要直接访问当前的作用域。Vue 的响应式系统会自动管理作用域的创建、销毁和其中的副作用。但是,如果你确实需要这样的功能(可能是为了调试或高级用例),你可以考虑使用 Vue 的内部 API 或自己实现类似的逻辑。
  • 如果你确实需要类似 getCurrentScope 的功能,你可能需要考虑以下替代方案:

使用自定义标记:在你的代码中,你可以为每个 effectScope 创建一个唯一的标识符,并在创建作用域时将其存储在某个全局或局部的可访问位置。然后,你可以通过该标识符来获取相应的作用域。
使用外部库或插件:如果你发现自己经常需要这样的功能,你可以考虑使用第三方库或插件来扩展 Vue 的功能。这些库或插件可能已经实现了类似 getCurrentScope 的功能,并且经过了广泛的测试和验证。
重新设计你的代码:在大多数情况下,你可能不需要直接访问当前的作用域。相反,你可以重新设计你的代码,使其更加模块化和可维护。例如,你可以将相关的副作用组织在同一个组件或模块中,并使用Vue 的响应式系统来自动管理它们。

onScopeDispose()

  • onScopeDispose 并不是一个直接暴露给用户的 API,但它是一个在内部使用的重要概念,特别是在与 effectScope 一起使用时。onScopeDispose 主要用于注册当特定作用域(scope)被停止或销毁时应当执行的回调函数。
  • 当你在 effectScope 的上下文中创建副作用(如使用 effect 或 watchEffect)时,你可以使用 onScopeDispose 来注册一个清理函数,这个函数会在该作用域被停止或销毁时执行。
import { reactive, effectScope, effect } from 'vue';  // 创建一个响应式对象  
const state = reactive({ count: 0 });  // 创建一个新的 effectScope  
const scope = effectScope();  // 在该作用域内创建一个 effect 并注册一个清理函数  
scope.run(() => {  // 创建一个 effect  const cleanupEffect = effect(() => {  console.log('count changed:', state.count);  });  // 注册一个当作用域被销毁时执行的清理函数  // 注意:这里不是直接使用 onScopeDispose,而是模拟其功能  const onDispose = () => {  console.log('Scope disposed! Cleaning up...');  // 在这里执行清理逻辑,比如取消订阅、移除事件监听器等  // cleanupEffect.stop(); // 如果需要的话,可以停止这个特定的 effect  };  // 假设我们有一个机制来在作用域停止时调用 onDispose  // ...(在 Vue 内部,这是自动处理的)  // 模拟作用域停止的事件(在 Vue 中,你不需要手动这样做)  // scope.stop(); // 这会触发上面定义的 onDispose 函数  
});  // 触发 state.count 的变化  
state.count++; // 输出 "count changed: 1"  // 假设在某个时候,作用域被停止了(在 Vue 中可能是组件卸载时)  
// scope.stop(); // 如果这行代码被取消注释,会输出 "Scope disposed! Cleaning up..."

相关文章:

Vue3 响应式API:高级函数(二)

shallowRef() shallowRef 是一个特殊的 ref 创建函数,它允许你创建一个只追踪顶层属性变化的响应式引用。与 ref 不同的是,shallowRef 创建的响应式引用对其内部值的深层嵌套属性是不敏感的,也就是说,只有当 shallowRef 的 .valu…...

『大模型笔记』什么是提示词注入(Prompt Injection)攻击?

什么是提示词注入(Prompt Injection)攻击? 文章目录 一. 什么是提示词注入(Prompt Injection)?二. 参考文献一. 什么是提示词注入(Prompt Injection)? 想花1美元买一辆新SUV吗?有人真的尝试过这样做。事实上,他们在一家特定汽车经销商的网站聊天机器人上进行了尝试。为了…...

SD-WAN与IPSec的对比

在现代企业中,随着网络环境的日益复杂,SD-WAN和IPSec作为两种关键的网络技术,各有其独特的优势和应用场景。那么,SD-WAN和IPSec究竟有什么不同?企业在不同情况下应该选择哪种技术呢? SD-WAN和IPSec的基本概…...

Ceph入门到精通-ceph经典盘符飘逸问题处理步骤

在Ceph存储系统中,"盘符飘逸"通常指的是Ceph OSD(Object Storage Daemon)使用的磁盘在系统重启后没有被正确挂载或识别。这可能是由于多种原因造成的,例如磁盘连接问题、驱动问题或配置错误。以下是解决此问题的步骤: 确认磁盘状态: 使用lsblk或fdisk -l命令来…...

【CV算法工程师必看】作为一个图像算法工程师,需要会什么,要学哪些技术栈?

作为一个图像算法工程师,除了基本的编程技能和理论知识,还需要掌握一系列的技术栈。以下是详细的技能和技术栈分类: 编程语言 Python: 主要用于快速开发和原型设计。常用库:OpenCV、Pillow、NumPy、SciPy、Scikit-image、TensorFlow、PyTorch。C++: 高性能要求的项目中广…...

【造化弄人:计算机系大学生真的象当年的高速公路收费员一样吗?】

曾经高速公路的收费员是多么的自豪和骄傲,按照常逻辑,车是越来越多,收费员应该越来越多?但现实情况,大家有目共睹! 不论你的车子怎么跑,只要上高速就要交费,那时候的收费员&#xf…...

民主测评要做些什么?

民主测评,作为一种重要的民主管理工具,旨在通过广泛征求群众意见,对特定对象或事项进行客观、公正的评价。它不仅是推动民主参与、民主监督的重要手段,也是提升治理效能、促进社会和谐的有效途径。以下将详细介绍民主测评的主要过…...

JimuReport 积木报表 v1.7.5 版本发布,免费的低代码报表

项目介绍 一款免费的数据可视化报表工具,含报表和大屏设计,像搭建积木一样在线设计报表!功能涵盖,数据报表、打印设计、图表报表、大屏设计等! Web 版报表设计器,类似于excel操作风格,通过拖拽完…...

Ubuntu安装Protobuf

以前的版本中,有./configure,所以参照下面的博客链接 Ubuntu安装Protobuf,指定版本_ubuntu更新protobuf-CSDN博客 后来的版本中,没有了./configure文件,需要安装bazel,参照下面的官网链接 protobuf/src/README.md a…...

揭秘Lazada API:掌握数据驱动的电商帝国,轻松实现销售飞跃

当涉及到Lazada API接口的技术帖子时,通常我们需要了解如何与Lazada的API进行交互,以执行各种操作,如获取产品信息、处理订单、管理库存等。由于Lazada的API是私有的并且需要特定的认证和访问权限,以下是一个简化的示例&#xff0…...

ThinkPHP发邮件配置教程?群发功能安全吗?

ThinkPHP发邮件的注意事项?如何优化邮件发送的性能? 无论是用户注册、密码重置还是消息提醒,发送邮件都是一个常见的需求。AokSend将详细介绍如何在ThinkPHP框架中配置和发送邮件,帮助开发者轻松实现邮件功能。 ThinkPHP发邮件&…...

编译和安装OpenMediaVault(OMV)NAS

下载OMV源码: git clone --depth1 https://github.com/openmediavault/openmediavault.git 安装编译环境: cd openmediavault/ ./buildenvadm.sh -h ./buildenvadm.sh install 这里会安装一堆编译OMV需要的工具。 如果编译过程中报dh命令找不到的错误&a…...

Java【问题 07】SSH不同版本使用jsch问题处理(7.4升级9.7及欧拉原生8.8)

SSH不同版本使用jsch问题处理 1.问题一2.问题二2.1 说明2.2 解决 3.问题三 1.问题一 # 1.系统 cat /etc/os-release # 系统信息 NAME"openEuler" VERSION"22.03 (LTS-SP1)" ID"openEuler" VERSION_ID"22.03" PRETTY_NAME"openEu…...

k8s和deepflow部署与测试

Ubuntu-22-LTS部署k8s和deepflow 环境详情: Static hostname: k8smaster.example.net Icon name: computer-vm Chassis: vm Machine ID: 22349ac6f9ba406293d0541bcba7c05d Boot ID: 605a74a509724a88940bbbb69cde77f2 Virtualization: vmware Operating System: U…...

matlab使用教程(92)—流线图、流带图和流管图

1.使用向量数据显示流线图 MATLAB 向量数据集 wind 代表北美地区的气流。本示例结合使用了几种方法: 利用流线跟踪风速 利用切片平面显示数据的横截面视图 利用切片平面上的等高线提高切片平面着色的可见性 1.1确定坐标的范围 加载数据并确定用来定位切片平面…...

全网最全!场外个股期权的询价下单流程的详细解析

场外个股期权的询价下单流程 场外个股期权交易,作为在交易所外进行的个性化期权交易方式,为投资者提供了更加灵活和定制化的交易选择。以下是场外个股期权询价下单流程的详细步骤: 文章来源/:财智财经 第一步:明确交…...

linux 如何解压 zip

使用unzip命令解压zip文件: unzip file.zip这将会将file.zip文件解压到当前目录。 使用tar命令解压zip文件: tar -xf file.zip这将会将file.zip文件解压到当前目录。 使用7z命令解压zip文件: 7z x file.zip这将会将file.zip文件解压到当…...

【ubuntu】增加samba服务和文件夹

发现ai -server的ubuntu机器无法git clone 下来github的文件所以 使用samba 连接到linux的文件夹proj然后在我的windows上git clone 即可。安装samba Creating config file /etc/samba/smb.conf with new version Setting up libcephfs2 (17.2.7-0ubuntu0.22.04.1) ... Setting…...

vue3中作用域插槽

1、先说一下具名插槽 有时在一个组件中包含多个插槽出口是很有用的。举例来说&#xff0c;在一个 组件中&#xff0c;有如下模板&#xff1a; <div class"container"><header><!-- 标题内容放这里 --></header><main><!-- 主要内容…...

Vuforia AR篇(六)— Mid Air 半空识别

目录 前言一、什么是Mid Air&#xff1f;二、使用步骤三、示例代码四、效果 前言 增强现实&#xff08;AR&#xff09;技术正在改变我们与数字世界的互动方式。Vuforia作为先进的AR开发平台&#xff0c;提供了多种工具来创造引人入胜的AR体验。其中&#xff0c;Mid Air功能以其…...

React 第五十五节 Router 中 useAsyncError的使用详解

前言 useAsyncError 是 React Router v6.4 引入的一个钩子&#xff0c;用于处理异步操作&#xff08;如数据加载&#xff09;中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误&#xff1a;捕获在 loader 或 action 中发生的异步错误替…...

Java 语言特性(面试系列2)

一、SQL 基础 1. 复杂查询 &#xff08;1&#xff09;连接查询&#xff08;JOIN&#xff09; 内连接&#xff08;INNER JOIN&#xff09;&#xff1a;返回两表匹配的记录。 SELECT e.name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id d.dept_id; 左…...

转转集团旗下首家二手多品类循环仓店“超级转转”开业

6月9日&#xff0c;国内领先的循环经济企业转转集团旗下首家二手多品类循环仓店“超级转转”正式开业。 转转集团创始人兼CEO黄炜、转转循环时尚发起人朱珠、转转集团COO兼红布林CEO胡伟琨、王府井集团副总裁祝捷等出席了开业剪彩仪式。 据「TMT星球」了解&#xff0c;“超级…...

有限自动机到正规文法转换器v1.0

1 项目简介 这是一个功能强大的有限自动机&#xff08;Finite Automaton, FA&#xff09;到正规文法&#xff08;Regular Grammar&#xff09;转换器&#xff0c;它配备了一个直观且完整的图形用户界面&#xff0c;使用户能够轻松地进行操作和观察。该程序基于编译原理中的经典…...

JVM 内存结构 详解

内存结构 运行时数据区&#xff1a; Java虚拟机在运行Java程序过程中管理的内存区域。 程序计数器&#xff1a; ​ 线程私有&#xff0c;程序控制流的指示器&#xff0c;分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器完成。 ​ 每个线程都有一个程序计数…...

【Linux系统】Linux环境变量:系统配置的隐形指挥官

。# Linux系列 文章目录 前言一、环境变量的概念二、常见的环境变量三、环境变量特点及其相关指令3.1 环境变量的全局性3.2、环境变量的生命周期 四、环境变量的组织方式五、C语言对环境变量的操作5.1 设置环境变量&#xff1a;setenv5.2 删除环境变量:unsetenv5.3 遍历所有环境…...

苹果AI眼镜:从“工具”到“社交姿态”的范式革命——重新定义AI交互入口的未来机会

在2025年的AI硬件浪潮中,苹果AI眼镜(Apple Glasses)正在引发一场关于“人机交互形态”的深度思考。它并非简单地替代AirPods或Apple Watch,而是开辟了一个全新的、日常可接受的AI入口。其核心价值不在于功能的堆叠,而在于如何通过形态设计打破社交壁垒,成为用户“全天佩戴…...

书籍“之“字形打印矩阵(8)0609

题目 给定一个矩阵matrix&#xff0c;按照"之"字形的方式打印这个矩阵&#xff0c;例如&#xff1a; 1 2 3 4 5 6 7 8 9 10 11 12 ”之“字形打印的结果为&#xff1a;1&#xff0c;…...

用神经网络读懂你的“心情”:揭秘情绪识别系统背后的AI魔法

用神经网络读懂你的“心情”:揭秘情绪识别系统背后的AI魔法 大家好,我是Echo_Wish。最近刷短视频、看直播,有没有发现,越来越多的应用都开始“懂你”了——它们能感知你的情绪,推荐更合适的内容,甚至帮客服识别用户情绪,提升服务体验。这背后,神经网络在悄悄发力,撑起…...

echarts使用graphic强行给图增加一个边框(边框根据自己的图形大小设置)- 适用于无法使用dom的样式

pdf-lib https://blog.csdn.net/Shi_haoliu/article/details/148157624?spm1001.2014.3001.5501 为了完成在pdf中导出echarts图&#xff0c;如果边框加在dom上面&#xff0c;pdf-lib导出svg的时候并不会导出边框&#xff0c;所以只能在echarts图上面加边框 grid的边框是在图里…...