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

再聊 Flutter Riverpod ,注解模式下的 Riverpod 有什么特别之处,还有发展方向

三年前我们通过 《Flutter Riverpod 全面深入解析》 深入理解了 riverpod 的内部实现,而时隔三年之后,如今Riverpod 的主流模式已经是注解,那今天就让我们来聊聊 riverpod 的注解有什么特殊之处。

前言

在此之前,我们需要先回忆一下,riverpod 最明显的特点是将 BuildContext 转换成 WidgetRef 抽象 ,从而让状态管理不直接依赖 BuildContext ,所以对应的 Provider 可以按需写成全局对象,而在 riverpod 里,主要的核心对象有:

  • ProviderScopeInheritedWidget 实现,共享实例的顶层存在,提供一个 ProviderContainer 全局共享
  • ProviderContainer:用于管理和保存各种 “Provider” 的 State ,并且支持 override 一些特殊 “Provider” 的行为,还有常见的 read\watch\refesh
  • Ref :提供 riverpod 内的 “Provider” 交互接口,是 riverpod 内 ProviderElementBase 的抽象
  • ProviderElementBase : Ref 的实现,每个 “Provider” 都会有自己的 “Element” ,而构建 “Provider” 时是传入的 Create 函数会在 “Element” 内通过 “setState” 调用执行,比如 StateProvider((ref)=> 0) 这里的 ref ,就是内部在 ”Element“ 里通过 setState(_provider.create(this)); " 的时候传入的 this
  • WidgetRef :替代 Flutter BuildContext 的抽象,内部通过继承 StatefulWidget 实现,作为 BuildContext 的对外替代

所以 “Provider” 里的 Ref 和 “Consumer” 的 WidgetRef 严格来说是两个不同的东西,只是它们内部都可以获取到 ProviderContainer ,从而支持对应 read\watch\refesh 等功能,这也是为什么你在外部直接通过 ProviderContainer 也可以全局直接访问到 read\watch\refesh 的原因。

另外,riverpod 内部定义了自己的 「Element」 和 「setState」实现,它们并不是 Flutter 里的 Element 和 setState,所以上面都加了 “”,甚至 riverpod 里的 “Provider” 和 Provider 状态管理库也没有关系, 这么设计是为了贴合 Flutter 本身的 「Element」 和 「setState」概念,所以这也是为什么说 riverpod 是专为 Flutter 而存在的设计。

注解模式

现在 riverpod 更多提倡使用注解模式,注解模式可以让 riverpod 使用起来更方便且规范,从一定程度也降低了使用难度,但是也对初学者屏蔽了不少过去的手写实现,导致在出现问题时新手也可能会相对更蒙。

简单函数注解

首先我们看这个简单的代码,我们在 main.dart 里添加了了一个 @riverpodhelloWorld ,然后运行 flutter pub run build_runner build --delete-conflicting-outputs ,可以看到此时生成了对应的 main.g.dart 文件:

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';part 'main.g.dart';
String helloWorld(Ref ref) {return 'Hello world';
}
class MyApp extends ConsumerWidget {Widget build(BuildContext context, WidgetRef ref) {final String value = ref.watch(helloWorldProvider);return MaterialApp(home: Scaffold(appBar: AppBar(title: const Text('Example')),body: Center(child: Text(value),),),);}
}

我们看 main.g.dart 文件,可以看到,根据 @riverpod 的规则, helloWorld 会生成一个 helloWorldProvider 实例让我们在使用时 read/watch/refresh :

// GENERATED CODE - DO NOT MODIFY BY HANDpart of 'main.dart';// **************************************************************************
// RiverpodGenerator
// **************************************************************************String _$helloWorldHash() => r'9abaa5ab530c55186861f2debdaa218aceacb7eb';/// See also [helloWorld].
(helloWorld)
final helloWorldProvider = AutoDisposeProvider<String>.internal(helloWorld,name: r'helloWorldProvider',debugGetCreateSourceHash:const bool.fromEnvironment('dart.vm.product') ? null : _$helloWorldHash,dependencies: null,allTransitiveDependencies: null,
);('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
typedef HelloWorldRef = AutoDisposeProviderRef<String>;
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package

通过生成的代码,我们可以看到:

  • _$helloWorldHash() :它主要是用于提供一个唯一标识,用于追踪 Provider 的来源和状态,它是被 debugGetCreateSourceHash 所使用,例如在 Debug 模式下 hotload 时,riverpod 会用这个值来判断当前 provider 是否需要重建,比如当你重新生成的时候 hash 值就会出现变化。
  • helloWorldProviderAutoDisposeProvider 的实例,也就是默认情况下 @riverpod 生成的都是自动销毁的 Provider ,

这里默认使用 AutoDisposeProvider ,也是为了更好的释放内存和避免不必需要的内存泄漏等场景, AutoDisposeProvider 内部,在每次 readinvalidate 、页面退出、ProviderContainer 销毁等场景会自动调用 dispose 。

异步函数注解

接着,如果给 helloWorld 增加 async ,那么我们得到一个 AutoDisposeFutureProvider ,同理,如果是 async* 就会生成一个 AutoDisposeStreamProvider


Future<String> helloWorld(Ref ref) async{return 'Hello world';
}------------------------------GENERATED CODE---------------------------------(helloWorld)
final helloWorldProvider = AutoDisposeFutureProvider<Object?>.internal(helloWorld,name: r'helloWorldProvider',debugGetCreateSourceHash:const bool.fromEnvironment('dart.vm.product') ? null : _$helloWorldHash,dependencies: null,allTransitiveDependencies: null,
);

当然,在返回结果使用上会有些差别, 异步的 Provider 会返回一个 AsyncValue ,或者需要 .value 获取一个非空安全的对象:


Widget build(BuildContext context, WidgetRef ref) {final AsyncValue<String> asyncValue = ref.watch(helloWorldProvider);final String? value = ref.watch(helloWorldProvider).value;return MaterialApp(home: Scaffold(appBar: AppBar(title: const Text('Example')),body: Center(child: Text(asyncValue.when(data: (v) => v,error: (_, __) => "error",loading: () => "loading")),),),);
}

函数注解带参数

当你需要给 helloWorld 增加参数的时候,此时的 helloWorldProvider 就不再是一个 AutoDisposeFutureProvider 实例,它将变成 HelloWorldFamily ,它是一个 Family 的实现:


Future<String> helloWorld(Ref ref, String value, String type) async {return 'Hello world $value $type';
}
Widget build(BuildContext context, WidgetRef ref) {final AsyncValue<String> asyncValue = ref.watch(helloWorldProvider("1", "2"));final String? value = ref.watch(helloWorldProvider("1", "2")).value;
}------------------------------GENERATED CODE---------------------------------/// See also [helloWorld].
class HelloWorldFamily extends Family<AsyncValue<String>> {/// See also [helloWorld].const HelloWorldFamily();/// See also [helloWorld].HelloWorldProvider call(String value,String type,) {return HelloWorldProvider(value,type,);}HelloWorldProvider getProviderOverride(covariant HelloWorldProvider provider,) {return call(provider.value,provider.type,);}static const Iterable<ProviderOrFamily>? _dependencies = null;Iterable<ProviderOrFamily>? get dependencies => _dependencies;static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;Iterable<ProviderOrFamily>? get allTransitiveDependencies =>_allTransitiveDependencies;String? get name => r'helloWorldProvider';
}

在 Dart 中,call 方法是一个特殊的方法,它可以让一个类的实例像函数一样调用。

说到 Family , 它的作用是主要就是支持使用额外的参数构建 Provider ,因为前面 helloWorld 需要传递参数,所以 HelloWorldFamily 的主要作用,就是提供创建和覆盖需要参数的 Provider,例如前面的:

  final AsyncValue<String> asyncValue = ref.watch(helloWorldProvider("1", "2"));final String? value = ref.watch(helloWorldProvider("1", "2")).value;

当然,这里你需要注意,不同与前面的 helloWorldProvider 实例,需要参数的 Provider 需要你每次使用时通过参数构建,而此时你每次调用如 helloWorldProvider("1", "2") 都是创建了一个全新实例,如果你需要同一个数据源下 read/watch ,那么你应该在调用时共用一个全局 helloWorldProvider("1", "2") 实例。

如果是不同 Provider 实例,那么你获取到的参数其实是不一样的,因为内部 map 登记的映射关系就是基于 Provider 实例为 key :

不过对比之下,过去你使用 FutureProvider.family 只能覆带一个 Arg 参数,虽然可以通过语法糖传递多个参数,但是终究还是比注解生成的麻烦:

final helloWorldFamily =FutureProvider.family<String, (String, String)>((value, type) async {return 'Hello world $value $type';
});

另外,注解生成时,还会动态生成一个对应的 “Element” ,让 Element 支持获取 Provider 的参数,并实现对应 build 方法,也就是通过 ref 可以获取到相关参数:

mixin HelloWorldRef on AutoDisposeFutureProviderRef<String> {/// The parameter `value` of this provider.String get value;/// The parameter `type` of this provider.String get type;
}class _HelloWorldProviderElementextends AutoDisposeFutureProviderElement<String> with HelloWorldRef {_HelloWorldProviderElement(super.provider);String get value => (origin as HelloWorldProvider).value;String get type => (origin as HelloWorldProvider).type;
}

最后,带参数之后,生成的 _SystemHash 也会根据参数动态变化,从而支持 hotload 等场景:

class _SystemHash {_SystemHash._();static int combine(int hash, int value) {// ignore: parameter_assignmentshash = 0x1fffffff & (hash + value);// ignore: parameter_assignmentshash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));return hash ^ (hash >> 6);}static int finish(int hash) {// ignore: parameter_assignmentshash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));// ignore: parameter_assignmentshash = hash ^ (hash >> 11);return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));}
}

类注解

接着,我们看 @riverpod 除了可以注解函数之后,还可以直接注解 class ,只是 class 需要继承 _$*** 一个子类:


class HelloWorld extends _$HelloWorld {String build() {return 'Hello world';}changeValue(String value) {state = value;}
}
Widget build(BuildContext context, WidgetRef ref) {final String asyncValue = ref.watch(helloWorldProvider);ref.read(helloWorldProvider.notifier).changeValue("next");
}

通过生成代码可以看到,此时生成的是 AutoDisposeNotifierProvider ,也就是在读取时,可以通过 read(****Provider.notifier) 去改变状态:

String _$helloWorldHash() => r'52966cfeefb6334e736061e19443e4c8b94160d8';/// See also [HelloWorld].
(HelloWorld)
final helloWorldProvider =AutoDisposeNotifierProvider<HelloWorld, String>.internal(HelloWorld.new,name: r'helloWorldProvider',debugGetCreateSourceHash:const bool.fromEnvironment('dart.vm.product') ? null : _$helloWorldHash,dependencies: null,allTransitiveDependencies: null,
);typedef _$HelloWorld = AutoDisposeNotifier<String>;

也就是,通过 @riverpod 注解的 class ,是带有 state 状态的 NotifierProvider ,这是对比注解函数最明显的差异

而如果注解 class 需要携带参数,那么可以在 build 上添加需要的参数,最终同样和函数一样会生成一个对应的 HelloWorldFamily


class HelloWorld extends _$HelloWorld {String build(String value, String type) {return 'Hello world';}changeValue(String value) {state = value;}
}

同理,如果你给 build 增加了 async,那么就会生成一个 AutoDisposeAsyncNotifierProviderImpl 的相关实现:


class HelloWorld extends _$HelloWorld {Future<String> build(String value, String type) async {return 'Hello world';}changeValue(String value) {final currentValue = state.valueOrNull ?? "";state = AsyncData(currentValue + value);}removeString(String value) {final currentValue = state.valueOrNull ?? "";state = state.copyWithPrevious(AsyncData(currentValue.replaceAll(value, "")));}
}

可以看到,在注解 class 下可操作空间是在 build ,并且需要注意的是,当你调用 refresh 的时候,State 是会被清空,并且重新调用 build

KeepAlive

那么我们前面说的都是 AutoDispose ,如果我不想他被释放呢?那就是需要用到大写字母开头的 @Riverpod ,给参数配置上 keepAlive: true

(keepAlive: true)
class HelloWorld extends _$HelloWorld 

然后再看输出文件,你就会看到此时 HelloWorldProvider 继承的是 AsyncNotifierProviderImpl 而不是 AutoDispose 了:

class HelloWorldProvider extends AsyncNotifierProviderImpl<HelloWorld, String> {/// See also [HelloWorld].HelloWorldProvider(String value,String type,) : this._internal(

dependencies

另外 @Riverpod 还有另外一个可配置参数 dependencies ,从名字上理解起来是依赖的意思,但是其实它更多用于「作用域」相关的处理。

在 riverpod 里,框架的设计是支持多个 ProviderContainer 的场景,并且每个容器可以覆盖(override)某些 Provider 的数据,例如我只是添加了一个 dependencies: []此时无论列表是否为空,它都可以被认为是一个具有作用域支持的 Provider,从而实现根据上下文进行数据隔离,另外不为空时还可以看作声明 Provider 在作用域内的依赖关系

(dependencies: [])

但是,不是你加了 dependencies 它就自动产生作用域隔离了,不为空时也不会自动追加依赖,它只是一个声明作用,后续还是需要代码配合。

如下代码所示,这里简单的声明了一个带有 dependenciesCounter ,然后:

  • 在页面通过 ref.watch(counterProvider) 监听了 Counter
  • 在新的 dialog 也通过 ref2.watch(counterProvider) 监听了 Counter
(dependencies: [])
class Counter extends _$Counter {int build() => 0;void update(int count) {state = count;}
}class MyApp extends StatelessWidget {Widget build(BuildContext context) {return ProviderScope(child: MaterialApp(home: Consumer(builder: (ctx, ref, __) {final count = ref.watch(counterProvider);return Scaffold(appBar: AppBar(),body: Column(mainAxisAlignment: MainAxisAlignment.start,children: [Text('Counter: $count'),ElevatedButton(onPressed: () {showDialog(context: ctx,builder: (context) => AlertDialog(title: Text('Dialog'),content: Consumer(builder: (_, ref2, __) {final count2 = ref2.watch(counterProvider);return InkWell(onTap: () {ref2.read(counterProvider.notifier).update(count2 + 1);},child: Text('Dialog Counter: $count2'),);})),);},child: Text('Open Dialog'),),],),floatingActionButton: FloatingActionButton(onPressed: () {ref.read(counterProvider.notifier).update(count + 1);}),);}),),);}
}

结果最后运行发现,Dialog 和主页的 Counter 其实还是共享的, dependencies 并没有起到作用:

之所以这样,原因在于没有增加新的 ProviderScope ,如下代码所示,只要将上面的 showDialog 部分修改为如下代码所示:

  • 新增一个 的 ProviderScope
  • 通过 overrides 指定对应 counterProvider
showDialog(context: ctx,builder: (context) => ProviderScope(overrides: [counterProvider,///你还可以 overrideWith 覆盖修改//counterProvider.overrideWith(()=>Counter())],child: AlertDialog(title: Text('Dialog'),content: Consumer(builder: (_, ref2, __) {final count2 = ref2.watch(counterProvider);return InkWell(onTap: () {ref2.read(counterProvider.notifier).update(count2 + 1);},child: Text('Dialog Counter: $count2'),);})),),
);

以上条件缺一不可以,运行后如下图所示,可以看到此时 counterProvider 在主页和 Dialog 之间被有效分割开:

其实原因从源码里也可以看出来,在 ProviderContainer 内部源码我们可以看到,要产生一个独立的作用域,你需要:

  • root 不为空,也就是有一个上级 ProviderContainer
  • 其次存在 dependencies ProviderContainer 的 override 不为空,也就是 dependencies 不为 null 就行,但是 override 必须有 Provider
  • 最后才是返回全新的 _StateReader 用于提供状态数据

所以,从这里就可以看出,dependencies 只是一个先置条件,具体它是不是局部作用域,还得是你用的时候怎么用

同理依赖也是,比如你写了一个 @Riverpod(dependencies: [maxCountProvider]) ,但是你还是需要对应写上 ref.watch(maxCountProvider) ,不然它也并不起作用:

(dependencies: [maxCountProvider])
int limitedCounter(LimitedCounterRef ref) {final max = ref.watch(maxCountProvider); // 监听 return 0.clamp(0, max); 
}

PS ,如果你只是正常监听,不需要作用域的场景,其实直接写 ref.watch 而不需要 dependencies: [maxCountProvider] 也是可以的。

如果我们从输出端看,可以看到有没有 dependencies ,主要就是 _dependencies_allTransitiveDependencies 是否为空的区别:

注意事项

最后也有一些注意事项,例如:

  • 通过注解生成的 Provider 好不要依赖非生成的 Provider,比如这里的 example 是注解,它监听了一个非注解生成的 depProvider ,这样并不规范:

    final depProvider = Provider((ref) => 0);
    void example(Ref ref) {// Generated providers should not depend on non-generated providersref.watch(depProvider);
    }
    
  • 有作用域时,如果监听了某个 Provider ,那么 dependencies 里必须写上依赖 Provider,以下写法就不合规:

    (dependencies: [])
    void example(Ref ref) {// scopedProvider is used but not present in the list of dependenciesref.watch(scopedProvider);
    }
    
  • Provider 里不应该接收 BuildContext

    // Providers should not receive a BuildContext as a parameter.
    
    int fn(Ref ref, BuildContext context) => 0;
    class MyNotifier extends _$MyNotifier {int build() => 0;// Notifiers should not have methods that receive a BuildContext as a parameter.void event(BuildContext context) {}
    }
    

其实类型的注意事项在 riverpod_lint 里都声明了,只是 Custom lint rules 不会直接展示在 dart analyze ,所以需要用户在添加完 riverpod_lint 后,执行对应的 dart run custom_lint

最后

可以看到,通过注解模式,riverpod 可以让开发者少些很多代码,在整体设计理念没有变化的情况下,模版生成的代码会更规范,并且在上层屏蔽了许多复杂度和工作量。

另外通过 dependencies 我们可以可以看到 riverpod 在存储管理上它是统一的,但是在组合上它是分散的的设计理念。

而 Flutter 状态管理一直以来也是「是非之地」,比如近期就出现说 riverpod 在基准性能测试表示不如 signals 的情况,但是作者也回应了该测试属于「春秋笔法」之流:

另外,由于Dart 宏功能推进暂停 ,而 build runner 与数据类的优化还没落地,作者也在探索没有 codegen 下如何也可以便捷使用 riverpod ,比如让 family 支持多个参数:

当然,从作者的维护体验上看,貌似作者又有停滞 codegen 的倾向,看起来左右摇摆的状态还会持续一段时间:

那么, 2025 年 riverpod 还是你状态管理的首选吗

相关文章:

再聊 Flutter Riverpod ,注解模式下的 Riverpod 有什么特别之处,还有发展方向

三年前我们通过 《Flutter Riverpod 全面深入解析》 深入理解了 riverpod 的内部实现&#xff0c;而时隔三年之后&#xff0c;如今Riverpod 的主流模式已经是注解&#xff0c;那今天就让我们来聊聊 riverpod 的注解有什么特殊之处。 前言 在此之前&#xff0c;我们需要先回忆…...

每日一题----------String 和StringBuffer和StringBuiler重点

本质&#xff1a;是一个char字符数组存储字符串 总结&#xff1a; 1.如果字符串存在大量的修改操作&#xff0c;一般使用StringBuffer或者StringBuilder。 2.如果字符串存在大量的修改操作&#xff0c;并且单线程的情况&#xff0c;使用StringBuilder。 3.如果字符串存在大…...

使用AI一步一步实现若依前端(5)

功能5&#xff1a;侧边栏菜单动态显示 功能4&#xff1a;首页使用Layout布局 功能3&#xff1a;点击登录按钮实现页面跳转 功能2&#xff1a;静态登录界面 功能1&#xff1a;创建前端项目前言 在若依中&#xff0c;侧边栏显示的菜单项&#xff0c;是根据登录用户的角色动态显…...

如何在需求分析阶段考虑未来扩展性

在需求分析阶段考虑未来扩展性的关键在于 前瞻规划、灵活架构、标准设计。其中&#xff0c;前瞻规划尤为重要&#xff0c;因为通过全面分析业务发展趋势与技术演进&#xff0c;能够在初期设计阶段预留足够扩展空间&#xff0c;降低后期改造成本&#xff0c;为企业长期发展奠定坚…...

平面机械臂运动学分析

平面机械臂运动学分析 一 整体概述1 研究步骤&#xff1a; 二 正向1 几何分析2 matlab 仿真模拟&#xff08;1&#xff09;实现效果&#xff08;2&#xff09;matlab代码&#xff1a; 3 DH矩阵计算法&#xff08;1&#xff09;计算公式&#xff08;2&#xff09;计算结果验证&a…...

1688店铺所有商品数据接口详解

​​一、接口概述淘宝开放平台提供 1688.items.onsale.get/taobao.item_search_shop 接口&#xff0c;可批量获取店铺在售商品列表&#xff0c;包含商品 ID、标题、价格、销量、图片等核心信息。该接口适用于商品库管理、竞品监控、数据分析等场景 ​二、接口调用流程 前期准…...

华为hcia——Datacom实验指南——三层交换和ARP的工作原理

什么是三层交换 三层交换是指连接在同一台三层交换机上&#xff0c;不同vlan用户&#xff0c;不同网段ip&#xff0c;通过vlanif接口进行数据交换。 什么是ARP协议 通过网络层的ip地址解析成数据链路层的mac地址。 说白了就是通过目标ip地址去问他对应的mac地址是多少。 A…...

手脑革命:拆解Manus AI如何用“执行智能体”重构生产力——中国团队突破硅谷未竟的技术深水区

第一章&#xff1a;Manus AI 的技术演进与行业背景 1.1 从工具到智能体&#xff1a;AI 技术的范式跃迁 人工智能的发展经历了从规则驱动&#xff08;Rule-based&#xff09;到统计学习&#xff08;Statistical Learning&#xff09;&#xff0c;再到深度学习&#xff08;Deep…...

【算法】二叉树的递归遍历

前序遍历 void preOrder(Node *node){if(node ! nullptr){cout << node->data_ << " ";preOrder(node->left_);preOrder(node->right_);}} 中序遍历 void inOrder(Node *node){if (node ! nullptr){inOrder(node->left_);cout << n…...

中级网络工程师面试题参考示例(1)

一、基础理论 1. OSI七层模型与TCP/IP四层模型的区别是什么&#xff1f;请举例说明第三层&#xff08;网络层&#xff09;和第四层&#xff08;传输层&#xff09;的核心协议。 参考答案&#xff1a; OSI七层模型分为物理层、数据链路层、网络层、传输层、会话层、表示层、应用…...

Java并发 vs 并行:本质区别与应用场景全解析(易混概念)

并发 vs 并行&#xff1a;本质区别与应用场景全解析&#xff08;易混概念&#xff09; 一、核心区别&#xff1a;从定义出发 在计算机科学中&#xff0c;并发&#xff08;Concurrency&#xff09; 和 并行&#xff08;Parallelism&#xff09; 是两种完全不同的任务处理模型&a…...

深度评测DeepSeek、ChatGPT O1和谷歌Gemini AI应用开发场景 - DeepSeek性能完胜!

下面我会展示我为期一周的实验结果&#xff0c;创作不宜&#xff0c;希望大家关注我&#xff0c;以后多多互3&#xff01;前一阵我在互联网上看到很多关于DeepSeek R1的讨论&#xff0c;这个开源模型据说可以媲美&#xff0c;甚至优于像OpenAI o1这样的付费模型。 由于我在日常…...

【GoTeams】-5:引入Docker

本文目录 1. Dokcer-compose回顾下Docker知识编写docker-compose.yaml运行docker 2. 部署go服务编写dockerfile 1. Dokcer-compose 这里简单先用一下win版本的Docker&#xff0c;后期开发好了部署的时候再移植到服务器下进行docker部署。 输入命令docker-compose version 就可…...

mysql的Innodb最大支持的索引长度是多少,以及索引长度怎么计算

今天正好有空&#xff0c;来讲个之前粉丝经常问的一个知识&#xff0c;就是mysql的Innodb最大支持的索引长度是多少&#xff1f;以及索引长度怎么计算&#xff1f; 一、mysql的innodb引擎&#xff0c;创建索引最大支持的长度是多少字节&#xff1f; 不墨迹&#xff0c;直接说…...

深入解析 configService.addListener 使用中的注意事项

在使用 Nacos 的 configService.addListener 方法进行配置监听时&#xff0c;为了确保程序的稳定性、可靠性以及高效性&#xff0c;有诸多注意事项需要我们关注。下面将对这些关键要点进行详细阐述。 一、连接稳定性 1.1 网络连接问题 Nacos 客户端与服务端通过网络进行通信&…...

数据结构和算法--仅仅用于理解里面的术语,入门级别

数据结构和算法 预先知识&#xff1a;java 黑马前29节 cmd命令&#xff1a; 文件夹路径不区分大小写 E: dir:查看所有文件 cd 目录 :进入 cd… 返回上一级 cd 目录1\目录2 cd\ 回到根目录 cls 清屏 exit 退出 打开文件夹必须用cd 查找&#xff0c;但是文件不用&am…...

this.$nextTick() 作用及实现原理

1、原理和作用 2、更新任务推送到微任务队列后&#xff0c;vue是如何知道所有的更新任务执行完成了&#xff1f; vue将更新任务推送给微任务队列&#xff1b;当更新任务执行的时候&#xff0c;将回调队列任务推给微任务队列&#xff1b;通过微任务队列的原子性和先进先出机制&…...

C#常用的循环语句

在C#中&#xff0c;循环是一种控制结构&#xff0c;用于重复执行一组语句直到满足特定条件。C#提供了几种循环结构&#xff0c;包括for循环、while循环、do-while循环和foreach循环。每种循环都有其特定的用途和场景。下面我将逐一介绍这些循环的用法。 一、C#循环类型 1. fo…...

Android View 设置背景方式全解析

一、整体概述 在 Android 开发中&#xff0c;视图&#xff08;View&#xff09;的背景设置是构建用户界面的重要组成部分。一个合适的背景可以提升界面的美观度&#xff0c;增强用户体验。从简单的纯色背景到复杂的动态效果&#xff0c;背景设置不仅影响界面美观&#xff0c;还…...

HTTP拾技杂谈

HTTP拾技杂谈 简单聊聊HTTP中的那些东西 文章目录 HTTP拾技杂谈前言HTTP协议1.请求从客户端到服务器端的4个步骤一般客户端请求如下&#xff1a;服务端响应如下 2.Keep-AliveHTTP方法Cookie 总结 前言 超文本传输协议&#xff08;Hypertext Transfer Protocol &#xff0c;HT…...

网络安全之RSA算法

1978年就出现了这种算法&#xff0c;它是第一个既能用于数据加密也能用于数字签名的算法。它易于理解和操作&#xff0c;也很流行。算法的名字以发明者的名字&#xff08;RonRivest&#xff0c;AdiShamir和LeonardAdleman&#xff09;命名。但RSA的安全性一直未能得到理论上的证…...

神经网络为什么要用 ReLU 增加非线性?

在神经网络中使用 ReLU&#xff08;Rectified Linear Unit&#xff09; 作为激活函数的主要目的是引入非线性&#xff0c;这是神经网络能够学习复杂模式和解决非线性问题的关键。 1. 为什么需要非线性&#xff1f; 1.1 线性模型的局限性 如果神经网络只使用线性激活函数&…...

ES10(2019)、ES11(2020) 新增特性(八)

目录 ES10&#xff08;2019&#xff09; Array.flat() Array.flatMap() String.trimStart()和String.trimEnd() Symbol.prototype.description Object.fromEntries() ES11&#xff08;2020&#xff09; Nullish coalescing Operator【空值运算符】 可选链&#xff08…...

利用MQ自动取消未支付超时订单最佳实践

一、利用MQ自动取消未支付超时订单最佳实践 1、基于 RocketMQ 延迟消息 1.1&#xff1a;延迟消息 当消息写入到 Broker 后&#xff0c;不会立刻被消费者消费&#xff0c;需要等待指定的时长后才可被消费处理的消息&#xff0c;称为延时消息。 1.2&#xff1a;实现流程 &am…...

1-003:MySQL 的索引类型有哪些?

MySQL 中的索引类型主要分为以下几类&#xff0c;每种索引都有不同的适用场景和优化查询的作用&#xff1a; 1. 按存储结构分类 ① 聚簇索引&#xff08;Clustered Index&#xff09; 特点&#xff1a; InnoDB 引擎的 主键索引 就是 聚簇索引。数据与索引存储在一起&#xff…...

php虚拟站点提示No input file specified时的问题及权限处理方法

访问站点&#xff0c;提示如下 No input file specified. 可能是文件权限有问题&#xff0c;也可能是“.user.ini”文件路径没有配置对&#xff0c;最简单的办法就是直接将它删除掉&#xff0c;还有就是将它设置正确 #配置成自己服务器上正确的路径 open_basedir/mnt/qiy/te…...

Unity UGUI下实现精确点击的一种方式

比如有这样一个情况&#xff0c;UI的显示区域是个圆形&#xff0c;在点击的时候也需要精确点击到这个圆形显示区域&#xff0c;但是UI元素的RectTransform是个矩形 1. 使用脚本修改 2. 原理探究 此脚本继承了Image组件&#xff0c;但是获取了自身的Collider2D&#xff0c;目…...

元宇宙崛起:区块链与金融科技共绘数字新世界

文章目录 一、引言二、元宇宙与区块链的深度融合三、区块链在元宇宙金融中的应用四、金融科技在元宇宙中的创新应用五、面临的挑战与机遇《区块链与金融科技》亮点内容简介获取方式 一、引言 随着科技的飞速发展&#xff0c;元宇宙概念逐渐走进人们的视野&#xff0c;成为数字…...

postgresql14编译安装脚本

#!/bin/bash####################################readme################################### #先上传postgresql源码包&#xff0c;再配置yum源&#xff0c;然后执行脚本 #备份官方yum源配置文件&#xff1a; #cp /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS…...

警惕AI神话破灭:深度解析大模型缺陷与禁用场景指南

摘要 当前AI大模型虽展现强大能力&#xff0c;但其本质缺陷可能引发系统性风险。本文从认知鸿沟、数据困境、伦理雷区、技术瓶颈四大维度剖析大模型局限性&#xff0c;揭示医疗诊断、法律决策等8类禁用场景&#xff0c;提出可信AI建设框架与用户防护策略。通过理论分析与实操案…...

1分钟看懂React的那些Hook‘s

一、useEffect的五指山 1.执行时机&#xff1a;组件初始化,组件更新&#xff08;组件内state变化&#xff09; useEffect(() > {}) 2.执行时机&#xff1a;组件初始化 useEffect(() > {},[]) 3.执行时机&#xff1a;组件初始化&#xff0c;依赖的状态发生变化&#xf…...

聚焦两会:科技与发展并进,赛逸展2025成创新新舞台

在十四届全国人大三次会议和全国政协十四届三次会议期间&#xff0c;代表委员们围绕多个关键议题展开深入讨论&#xff0c;为国家未来发展谋篇布局。其中&#xff0c;技术竞争加剧与经济转型需求成为两会焦点&#xff0c;将在首都北京举办的2025第七届亚洲消费电子技术贸易展&a…...

深入C语言:指针与数组的经典笔试题剖析

1. sizeof和strlen的对比 1.1 sizeof sizeof 是C语言中的一个操作符&#xff0c;用于计算变量或数据类型所占内存空间的大小&#xff0c;单位是字节。它不关心内存中存储的具体数据内容&#xff0c;只关注内存空间的大小。 #include <stdio.h> int main() {int a 10;…...

⚡ 回声谷即时通讯系统

基于SpringBootVue3的实时通信解决方案 &#x1f31f; 核心特性 #mermaid-svg-uxEwEcjlUVI6Tjjf {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-uxEwEcjlUVI6Tjjf .error-icon{fill:#552222;}#mermaid-svg-uxEwEcjl…...

实验题目:授权及收回授权、约束、触发器

一、实验环境 实验使用普通PC机一台&#xff0c;MySQL数据库版本8.0.36&#xff0c;使用Navicat Premium 16提供图形化界面。 二、实验内容 1、数据库的账号、用户的建立、删除以及授权机制 2、数据库中数据完整性约束控制技术 3、触发器 三、具体完成情况&#xff08;提…...

Spark(8)配置Hadoop集群环境-使用脚本命令实现集群文件同步

一.hadoop的运行模式 二.scp命令————基本使用 三.scp命令———拓展使用 四.rsync远程同步 五.xsync脚本集群之间的同步 一.hadoop的运行模式 hadoop一共有如下三种运行方式&#xff1a; 1. 本地运行。数据存储在linux本地&#xff0c;测试偶尔用一下。我们上一节课使用…...

c#中使用时间戳转换器

在C#中,时间戳转换器通常用于将时间戳(通常是一个表示自某一特定时间点(如1970年1月1日UTC)以来的毫秒数的长整型值)转换为DateTime对象,或者将DateTime对象转换回时间戳。以下是几种实现这一功能的方法: 1. 使用DateTime的构造函数 将时间戳转换为DateTime long tim…...

LLM中的transformer结构学习(二 完结 Multi-Head Attention、Encoder、Decoder)

文章目录 LLM中的transformer结构学习&#xff08;二 完结 Multi-Head Attention、Encoder、Decoder&#xff09;Self-Attention &#xff08;自注意力机制&#xff09;结构多头注意力 EncoderAdd & Norm 层Feed Forward 层 EncoderDecoder的第一个Multi-Head AttentionMas…...

FreeSWITCH 之 chat

要把 FreeSWITCH 之 chat 完全研究清楚&#xff0c;似乎不容易 发送&#xff0c;路由&#xff0c;接收 跟哪些模块有关 等等 咱一边查资料&#xff0c;一边整理&#xff0c;不着急 先看看 Kamalio 怎么发 MESSAGE loadmodule "uac.so"route[uac_send_message] {…...

本地fake server,

C# 制作的系统级tcp 重定向&#xff0c;整个系统只要有访问指定url&#xff0c;返回自定义内容到访问端。不局限在浏览器单一方面。 再者请理解这个图的含金量&#xff0c;服务器down机都可以模拟。 用途那就太多了&#xff0c;当然很多用途都不正当。嘿嘿 如果你很想要源代…...

用Deepseek写一个 HTML 和 JavaScript 实现一个简单的飞机游戏

大家好&#xff01;今天我将分享如何使用 HTML 和 JavaScript 编写一个简单的飞机游戏。这个游戏的核心功能包括&#xff1a;控制飞机移动、发射子弹、敌机生成、碰撞检测和得分统计。代码简洁易懂&#xff0c;适合初学者学习和实践。 游戏功能概述 玩家控制&#xff1a;使用键…...

解析 SQL,就用 sqlparse!

文章目录 解析 SQL&#xff0c;就用 sqlparse&#xff01;一、背景&#xff1a;为什么你需要 sqlparse&#xff1f;二、什么是 sqlparse&#xff1f;三、如何安装 sqlparse&#xff1f;四、简单易用的库函数1\. parse(sql)2\. format(sql, **options)3\. split(sql)4\. get_typ…...

Flask 全栈学习指南

一、Flask 基础核心 1. 核心概念与启动流程 WSGI 与 Werkzeug Flask 基于 Werkzeug 实现 WSGI 协议&#xff0c;处理 HTTP 请求到响应的全流程。手动实现 WSGI 应用示例&#xff1a;def simple_app(environ, start_response):status 200 OKheaders [(Content-type, text/pla…...

git的使用

1、git的安装&#xff08;windows10&#xff09; 网址&#xff1a;Git - Downloading Package全部默认安装就好。在任意文件夹中右击&#xff0c;列表中出现git即为安装成功。 2、git的基本配置 右击打开git bash设置用户信息 git config --global user.name "username…...

MQTT协议下温度数据上报观测云最佳实践

MQTT 介绍 MQTT&#xff08;Message Queuing Telemetry Transport&#xff0c;消息队列遥测传输协议&#xff09;是一种轻量级的、基于发布/订阅模式的消息传输协议&#xff0c;专为低带宽、高延迟或不可靠的网络环境设计&#xff0c;广泛应用于物联网&#xff08;IoT&#xf…...

什么是Flask

Flask是Python中一个简单、灵活和易用的Web框架&#xff0c;适合初学者使用。它提供了丰富的功能和扩展性&#xff0c;可以帮助开发者快速构建功能完善的Web应用程序。 以下是Python Flask框架的一些特点和功能&#xff1a; Flask 是一个使用 Python 编写的轻量级 WSGI 微 Web…...

数字投屏叫号器-发射端python窗口定制

窗口 本系列前章介绍&#xff0c;叫号器的显示端&#xff0c;完成了视频音频的形成和传输的介绍。本章节开始定制小窗口。 最终实现&#xff0c;处于桌面最前端&#xff0c;发送指令&#xff0c;集合前篇即可完成&#xff1a; 处理本地text.txt更新&#xff0c;随之被rtsp采集…...

文本转语音-音画适时推送rtsp并播放

文本语音 rtsp适时播放叫号系统的底层逻辑 发布Linux, unix socket 和window win32做为音频源的 python10下的(ffmpeg version 7.1) 可运行版本. 这两天在弄这个&#xff0c;前2篇是通过虚拟声卡&#xff0c;达到了最简单的一个逻辑&#xff0c;播放文本就从声卡发声&#xff0…...

clickhouse修改和删除数据

标题&#xff1a;ClickHouse中修改和删除数据的简易指南 在大数据时代&#xff0c;数据库技术的发展日新月异。作为一款专为实时分析设计的列式数据库管理系统&#xff0c;ClickHouse因其高效的查询性能而受到欢迎。照这么推测的话&#xff0c;对于那些习惯于传统SQL操作&…...

2025CSP-J 冲刺训练(1):二分

2025CSP-J 冲刺训练 1 一、二分查找函数1. 头文件2. 前提条件3. 功能函数3.1 lower_bound3.2 upper_bound 二、二分答案模板1. 前提条件2. 模板 三、典型例题1. 寻找固定的和1.1 审题1.2 分析1.3 参考答案 2. Snuke Festival2.1 审题2.2 分析2.3 参考答案 四、拓展例题1. 晒衣服…...