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

flutter 专题二 Flutter状态管理之Riverpod 0.8.4

一 、flutter 有哪些状态管理方式

Flutter的状态管理方式有很多,Redux、 Bloc、 MobX、Provider等等。单单一个Provider,我也见到了各种组合,例如ChangeNotifier + Provider / StateNotifier + Provider( + freezed)。各种方式各有千秋,我们根据自己的习惯和项目的情况去选择就好,这里不做讨论;

二 、Riverpod 介绍

Riverpod和Provider师出同门,都来自作者Remi,Riverpod可以被认为是Provider的重写,来实现原本不可能的功能。就像它的名字一样,字母与Provider相同,但是又不相同。

你可以理解Riverpod是Provider的升级版,解决了Provider的一些痛点:

Provider是InheritedWidget的封装,所以在读取状态时需要BuildContext。这导致了许多的限制,许多新手在不理解InheritedWidget和BuildContext时,跨页面获取状态经常会ProviderNotFoundException。而Riverpod不再依赖Flutter,也就是没有使用InheritedWidget,所以也不需要BuildContext。

读取对象是编译安全的。没有那么多的运行时异常。

能够有多个相同类型的provider。

provider可以是私有的。

当不再使用provider的状态时,将其自动回收。

当然目前Riverpod也有一些不足(0.14.0+3版本):

  • 毕竟诞生不久,它还不能保证是完全稳定的。
  • 可能后期会有API的破坏性改动。(比如在0.7.0就有不少Breaking,导致我之前写的部分示例内容就报错了。)
  • 目前生产环境中使用需要谨慎。

三、Riverpod的三种使用方式

Riverpod 提供了三种使用方式,如下图,可以根据自己的实际项目选择适合自己的方式

本篇不引入flutter_hooks相关内容,这里我就选择flutter_riverpod 。那么将它添加到pubspec.yaml

flutter_riverpod: ^0.14.0+3

四 、flutter 基础使用

Provider

这里使用RiverpodProvider需要三步就可以。

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';// 1.创建一个全局的provider,里面储存“Hello World!”
final Provider<String> helloWorldProvider = Provider((_) => 'Hello World!');void main() {runApp(// 2.添加“ProviderScope”。所有使用Riverpod的Flutter程序都必须// 在widget tree的根部添加它,用来储存各个provider。ProviderScope(child: MyApp(),),);
}class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return MaterialApp(title: 'Riverpod Example',theme: ThemeData(primarySwatch: Colors.blue,visualDensity: VisualDensity.adaptivePlatformDensity,),home: ProviderExample(),);}
}// 3.使用“ConsumerWidget”,在“build”中获取对应的provider
class ProviderExample extends ConsumerWidget {@overrideWidget build(BuildContext context, ScopedReader watch) {final String value = watch(helloWorldProvider);return Scaffold(appBar: AppBar(title: Text('Provider Example')),body: Center(child: Text(value),),);}
}

这里储存“Hello World!” 使用的是Provider,它提供一个永远不变的对象。不过大部分场景下状态都是可变的,下面用计数器来举例。

StateProvider

在“Hello World”的基础上,做两点修改即可。

  1. 定义一个全局常量StateProvider

final StateProvider<int> counterProvider = StateProvider((_) => 0);

2、 

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';class StateProviderExample extends StatelessWidget {@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text('StateProvider Example'),),body: Center(child: Column(mainAxisAlignment: MainAxisAlignment.center,children: <Widget>[Text('You have pushed the button this many times:',),Consumer(builder: (context, watch, _) {/// 使用Consumer(ConsumerWidget的封装),控制刷新的范围。int count = watch(counterProvider).state;return Text('$count',style: Theme.of(context).textTheme.headline4,);},),],),),floatingActionButton: FloatingActionButton(/// 使用read获取counterProvider,操作state。onPressed: () => context.read(counterProvider).state++,tooltip: 'Increment',child: Icon(Icons.add),),);}}

如果你的状态比较复杂可以使用ChangeNotifierProvider,如果习惯使用StateNotifier,可以使用StateNotifierProvider 。其实StateProvider的内部是StateController,也还是StateNotifier。源码如下;

class StateProvider<T>extends AlwaysAliveProviderBase<StateController<T>, StateController<T>> {StateProvider(Create<T, ProviderReference> create, {String name,}) : super((ref) => StateController(create(ref)), name);...
}class StateController<T> extends StateNotifier<T> {StateController(T state) : super(state);@overrideT get state => super.state;@overrideset state(T value) => super.state = value;
}

StateNotifierProvider的用法与StateProvider基本一致,这里就不贴出来了,有兴趣的可以点击这里查看。

2021-04-19更新:

0.14.0对StateNotifierProvider的语法有破坏性变化,避免了StateNotifierProvider的错误使用。具体见文档。

ChangeNotifierProvider
这部分没啥说的,注意ChangeNotifier与StateNotifier的区别,需要自己调用notifyListeners通知变更。

final ChangeNotifierProvider<Counter> _counterProvider = ChangeNotifierProvider((_) => Counter());class Counter extends ChangeNotifier {int _count = 0;int get count => _count;void increment() {_count++;notifyListeners();}void decrement(){_count--;notifyListeners();}
}class ChangeProviderNotifierExample extends StatelessWidget {@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text('ChangeNotifierProvider Example'),),body: Center(child: Column(mainAxisAlignment: MainAxisAlignment.center,children: <Widget>[Text('You have pushed the button this many times:',),Consumer(builder: (context, watch, _) {int count = watch(_counterProvider).count;return Text('$count',style: Theme.of(context).textTheme.headline4,);},),],),),floatingActionButton: FloatingActionButton(/// 使用read获取counterProvider。onPressed: () => context.read(_counterProvider).increment(),tooltip: 'Increment',child: Icon(Icons.add),),);}
}

FutureProvider

final FutureProvider<String> futureProvider = FutureProvider((_) async {/// 延时3sawait Future.delayed(const Duration(seconds: 3));return 'Riverpod';
});class FutureProviderExample extends StatelessWidget {@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text('FutureProvider Example'),),body: Center(child: Consumer(builder: (context, watch, _) {AsyncValue<String> futureProviderValue = watch(futureProvider);/// 根据相应状态展示return futureProviderValue.when(loading: () => CircularProgressIndicator(),error: (error, stack) => Text('Oops, something unexpected happened'),data: (value) => Text('Hello $value',style: Theme.of(context).textTheme.headline4,),);},),),);}
}

作者也提供了StreamProvider。用法大同小异,有兴趣的可以查看我的示例代码。

ProviderListener
如果你希望在Widget Tree上监听provider的状态变化,可以使用ProviderListener。用上面的计数器例子,当计数器为5时,触发监听。

ProviderListener<StateController<int>>(provider: counterProvider,onChange: (_, counter) {if (counter.state == 5) {print('当前计数器为5,触发监听。');}},child: Consumer(builder: (context, watch, _) {int count = watch(counterProvider).state;return Text('$count',style: Theme.of(context).textTheme.headline4,);},),
),

ScopeProvider

一般我们在实现一个列表的Item时,需要传入相应的index大致如下:

ListView.builder(itemCount: 50,itemBuilder: (context, index) {return ProductItem(index: index);},
)

如果使用ScopedProvider并结合 ProviderScope,就可以简单的获取index,不必从构造方法接收它。使用起来很简单,直接上代码:

/// 定义ScopedProvider
final ScopedProvider<int> currentProductIndex = ScopedProvider<int>(null);class ScopeProviderExample extends StatelessWidget {@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text('ScopedProvider'),),body: ListView.builder(itemCount: 50,itemBuilder: (context, index) {return ProviderScope(overrides: [/// 修改valuecurrentProductIndex.overrideWithValue(index),],/// 使用'const'关键字实例化了“ProductItem”,/// 但仍然可以在内部动态获取内容。child: const ProductItem(),);},),);}
}class ProductItem extends ConsumerWidget {const ProductItem({Key key}): super(key: key);@overrideWidget build(BuildContext context, ScopedReader watch) {/// 获取相应indexfinal index = watch(currentProductIndex);return ListTile(title: Text('item $index'));}
}

4.修饰符

family

family的作用是可以在获取provider时可以添加一个参数。直接上例子,一看便知:

/// 使用family,可以在获取provider时传入city
final _weatherProvider = Provider.family<String, String>((ref, city) {return '$city (Sunny)';
});class FamilyExample extends StatelessWidget {@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text('Family')),body: Center(child: Consumer(builder: (context, watch, _) {/// 这里可以传参“London”final String weather = watch(_weatherProvider('London'));return Text('$weather',);},),),);}
}

意: 使用family时传入的参数是有限制的。比如bool 、 int 、 double 、 String 、常量或是重写了==和hashCode的不可变对象。

autoDispose
前面我们的例子中,创建的provider因为保存在Widget Tree的根部。所以即使页面关闭,再次进入页面时会获取之前的状态。

这显然是不灵活的,那么这里就可以使用autoDispose,它可以在我们不再使用provider时,自动将其销毁。那么合理的使用它可以避免内存泄漏。

比如之前的计数器例子,只需加一个autoDispose就可以避免此类问题。

final stateProvider = StateProvider.autoDispose((_) => 0);

如果你需要自定义dispose事件,可以使用onDispose。比如你的provider中有网络请求(使用Dio):

final myProvider = FutureProvider.autoDispose((ref) async {final cancelToken = CancelToken();// 当provider被销毁时,取消http请求ref.onDispose(() => cancelToken.cancel());// http请求final response = await dio.get('path', cancelToken: cancelToken);// 如果请求成功完成,则保持该状态。ref.maintainState = true;return response;
});

上面代码中出现了ref.maintainState,这个参数默认为false。如果用户离开页面并且请求失败,下次则将再次执行该请求。但是,如果请求成功完成(maintainState为true),则将保留状态,下次重新进入页面时不会触发新的请求。

使用autoDispose可以达到限制provider是全局还是局部作用。这样一来,可以更方便的解决跨页面使用provider的问题。

5.进阶使用
Combining providers
1.如果创建的provider需要另一个provider的状态,这时就需要使用ProviderReference的read方法。

下面的示例是,给予城市和国家的provider,当创建locationProvider时,获取城市和国家的状态。

final Provider<String> cityProvider = Provider((ref) => 'London');
final Provider<String> countryProvider = Provider((ref) => 'England');
final Provider<Location> locationProvider = Provider((ref) => Location(ref));class Location {Location(this._ref);final ProviderReference _ref;String get label {/// read 获取final city = _ref.read(cityProvider);final country = _ref.read(countryProvider);return '$city ($country)';}
}

使用Riverpod就可以提供多个相同类型的Provider,这也是相比Provider的一个优点。

2.如果获取的状态值会发生变化,我们需要监听它。可以使用ProviderReference的watch方法。

下面的示例是,给予城市provider,当城市变化时,天气也相应变化。

final StateProvider<String> cityProvider = StateProvider((ref) => 'London');
final StateProvider<String> weatherProvider = StateProvider((ref) {/// watch监听final String city = ref.watch(cityProvider).state;return '$city (Sunny)';
});class CombiningProviderExample2 extends StatelessWidget {@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text('CombiningProvider')),body: Center(child: Consumer(builder: (context, watch, _) {final String weather = watch(weatherProvider).state;return Text('$weather',);},),),floatingActionButton: FloatingActionButton(onPressed: () {String city = context.read(cityProvider).state;/// 修改状态if (city == 'London') {context.read(cityProvider).state = "Xi'an";} else {context.read(cityProvider).state = 'London';}},tooltip: 'Refresh',child: Icon(Icons.refresh),),);}
}

refresh

强制provider立即刷新,重新返回创建的值。这种适合列表下拉刷新,或者请求数据错误时重试。


final FutureProvider<List<String>> productsProvider = FutureProvider((_) async {/// 延时3sawait Future.delayed(const Duration(seconds: 3));return List.generate(50, (index) => 'Item $index');
});class RefreshProviderExample extends StatelessWidget {@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text('RefreshProvider'),),body: Center(child: Consumer(builder: (context, watch, _) {AsyncValue<List<String>> productsProviderValue = watch(productsProvider);return productsProviderValue.when(loading: () => CircularProgressIndicator(),error: (error, stack) => Text('Oops, something unexpected happened'),data: (list) => RefreshIndicator(onRefresh: () => context.refresh(productsProvider), /// 刷新child: ListView(children: [for (final item in list) ListTile(title: Text(item)),],),),);},),),);}
}

select
当状态中某一个值发生变化时,相应Consumer下的builder就会执行,重建widget。如果使用select可以指定某一值更改时进行刷新,精准控制刷新范围,避免不必要的rebuild。

不过目前(0.14.0+3版本),select这种局部监听只支持使用hooks_riverpod包的useProvider。所以这里需要引用hooks_riverpod。

final ChangeNotifierProvider<Person> personProvider = ChangeNotifierProvider((_) => Person());class Person extends ChangeNotifier {int _age = 0;int get age => _age;set age(int age) {_age = age;notifyListeners();}String _name = 'weilu';String get name => _name;set name(String name) {_name = name;notifyListeners();}
}class SelectExample extends StatelessWidget {@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text('Select Example'),),body: Center(child: Column(mainAxisAlignment: MainAxisAlignment.center,children: <Widget>[HookBuilder(builder: (_) {String name = useProvider(personProvider.select((p) => p.name));/// 如果使用下面的方式,则age变化时,这里的Text也会刷新。
//                String name = useProvider(personProvider).name;return Text('name:$name',);},),HookBuilder(builder: (_) {int age = useProvider(personProvider.select((p) => p.age));return Text('age:$age',);},),],),),floatingActionButton: FloatingActionButton(// 这里age变化时,只有对应的Text会变化。onPressed: () => context.read(personProvider).age = Random.secure().nextInt(255),tooltip: 'Refresh',child: Icon(Icons.refresh),),);}
}

其他
细心的你会发现,在使用read获取provider时还是使用了context。一开始不是说,没有使用InheritedWidget,所以也不需要BuildContext吗?

其实Riverpod本身确实如此,但是在Flutter的应用中,为了便于高效(时间复杂度O(1))的在Widget Tree中获取ProviderContainer(在ProviderScope中隐式创建,用来储存provider),需要在根部使用InheritedWidget,便于最终获取provider。

read、refresh、Consumer、ProviderListener等方法和Widget的内部其实都调用了ProviderScope.containerOf(context, listen = xx);,不同的是listen的值。

static ProviderContainer containerOf(BuildContext context, {bool listen = true,}) {UncontrolledProviderScope scope;if (listen) {scope = context //.dependOnInheritedWidgetOfExactType<UncontrolledProviderScope>();} else {scope = context.getElementForInheritedWidgetOfExactType<UncontrolledProviderScope>().widget as UncontrolledProviderScope;}return scope.container;}

比如read中listen的值为false,使用getElementForInheritedWidgetOfExactType方法,这样在数据发生变化时就不会掉用didChangeDependencies,避免不必要的rebuild。相对的,Consumer、ProviderListener中listen的值为ture,会实现我们需要的widget重建。

我们可以还可以通过Flutter Inspector检查已有的状态,所有状态汇总在ProviderScope下面,这也是Riverpod的一个优点。如下图所示:

发布本篇时,有关Riverpod的资料与讨论很少。本篇也是我在实践完官网文档后的理解,如有错误,欢迎指出!

个人认为Riverpod是相对更轻松便捷的一种状态管理方式,待它稳定时应该能被更多的人喜爱。

Riverpod的相关示例代码我已经上传至Github,有兴趣的可以看看。后面如果Riverpod有变动时,我也会及时更新。大家可以收藏起来,多多点赞支持一下,给我点更新动力!

5、参考

https://riverpod.dev/docs

相关文章:

flutter 专题二 Flutter状态管理之Riverpod 0.8.4

一 、flutter 有哪些状态管理方式 Flutter的状态管理方式有很多&#xff0c;Redux、 Bloc、 MobX、Provider等等。单单一个Provider&#xff0c;我也见到了各种组合&#xff0c;例如ChangeNotifier Provider / StateNotifier Provider&#xff08; freezed&#xff09;。各…...

【Linux】从零开始使用多路转接IO --- poll

碌碌无为&#xff0c;则余生太长&#xff1b; 欲有所为&#xff0c;则人生苦短。 --- 中岛敦 《山月记》--- 从零开始使用多路转接IO 1 前言1 poll接口介绍3 代码编写4 总结 1 前言 上一篇文章我们学习了多路转接中的Select&#xff0c;其操作很简单&#xff0c;但有一些缺…...

Docker配置宿主机目录和网络映射

容器挂载宿主机目录 在Docker中&#xff0c;你可以通过-v或--volume选项将宿主机的目录挂载到容器中。这可以让你在容器和宿主机之间共享文件。 例如&#xff0c;如果你想将宿主机的/home/user/data目录挂载到容器的/data目录&#xff0c;你可以使用以下命令&#xff1a; do…...

第十七课 component组件解析

component组件解析 component组件的写法在众多组件写法中算是比较简单的&#xff0c;component组件结构组成如下&#xff1a; 1&#xff09;组件名 2&#xff09;组件模板 3&#xff09;利用Vue对象进行生成 基础示例&#xff1a; <div id"app"><test>…...

求余和求模是不是一样的,就要看看计算机中的 fix 和 floor 区别

在计算机中&#xff0c;fix和floor是两个不同的取整函数&#xff0c;它们各自有不同的取整规则。以下是fix和floor的详细区别&#xff1a; 一、定义与功能 fix函数 定义&#xff1a;fix函数是朝零方向取整的函数&#xff0c;即它会返回小于或等于&#xff08;对于正数&#xf…...

00 递推和递归的核心讲解

递归的步骤 说 f(n)含义返回/xx f(n)等价式子在第二步中观察趋势&#xff0c;发现边界值&#xff08;分类递归&#xff09;和终止值&#xff08;return&#xff09; 递归优化思路 记忆化 递推/动态规划的步骤 说f(n)含义循环 关系式列 初值 综上&#xff0c;题目分为两类&a…...

深度学习常用开源数据集介绍【持续更新】

DIV2K 介绍&#xff1a;DIV2K是一个专为 图像超分辨率&#xff08;SR&#xff09; 任务设计的高质量数据集&#xff0c;广泛应用于计算机视觉领域的研究和开发。它包含800张高分辨率&#xff08;HR&#xff09;训练图像和100张高分辨率验证图像&#xff0c;每张图像都具有极高…...

rust编写的系统监测器

系统监测器 技术栈 rusttaurivue3vue-echartsrsbuild 软件介绍 用于查看电脑的硬件信息&#xff0c;实时监测cpu&#xff0c;内存&#xff0c;硬盘&#xff0c;网络&#xff0c;进程等系统资源 图形化&#xff0c;动态化展示&#xff0c;美观实用 软件截图 下载 https:/…...

【MyBatis源码】CacheKey缓存键的原理分析

文章目录 Mybatis缓存设计缓存KEY的设计CacheKey类主体CacheKey组成CacheKey如何保证缓存key的唯一性 Mybatis缓存设计 MyBatis 每秒过滤众多数据库查询操作&#xff0c;这对 MyBatis 缓存键的设计提出了很高的要求。MyBatis缓存键要满足以下几点。 无碰撞&#xff1a;必须保证…...

034_Structural_Transient_In_Matlab结构动力学问题求解

结构动态问题 问题描述 我们试着给前面已经做过的问题上加一点有趣的东西。 结构静力学求解 当时求解这个问题&#xff0c;在最外面的竖直切面加载了一个静态的固定的力。下面我们试试看在上方的表面增加一个脉冲压力载荷。 采用统一的有限元框架&#xff0c;定义问题&…...

项目模块十五:HttpResponse模块

一、模块设计思路 存储HTTP应答要素&#xff0c;提供简单接口 二、成员变量 int _status; // 应答状态码 unordered_map<string, string> _headers; // 报头字段 string _body; // 应答正文 bool _redirect_flag; // 是否重定向信息 stri…...

推荐一款优秀的pdf编辑器:Ashampoo PDF Pro

Ashampoo PDF Pro是管理和编辑 PDF 文档的完整解决方案。程序拥有您创建、转换、编辑和保护文档所需的一切功能。根据需要可以创建特定大小的文档&#xff0c;跨设备可读&#xff0c;还可以保护文件。现在您还能像编辑Word文档一样编辑PDF! 软件特点 轻松处理文字 如 Microso…...

【系统架构设计师】2024年上半年真题论文: 论模型驱动架构设计方法及其应用(包括解题思路和素材)

更多内容请见: 备考系统架构设计师-专栏介绍和目录 文章目录 真题题目(2024年上半年 试题3)解题思路1、模型驱动架构能够为软件开发带来的好处2、模型驱动架构的开发过程论文素材参考真题题目(2024年上半年 试题3) 模型驱动架构设计是一种用于应用系统开发的软件设计方法…...

国内短剧源码短剧系统搭建小程序部署H5、APP打造短剧平台

​在当今的互联网时代&#xff0c;短剧作为一种新兴的娱乐形式&#xff0c;受到了越来越多用户的喜爱。为了提供更好的用户体验和满足用户需求&#xff0c;一个好的短剧系统需要具备多元化的功能和优质的界面设计。 本文将介绍国内短剧源码短剧系统搭建小程序部署H5、APP所需的…...

Java集合框架面试指南

Java集合框架面试指南 文章目录 Java集合框架面试指南ArrayList特点&#xff1a;LinkedList特点&#xff1a;ArrayDeque特点&#xff1a;PriorityQueue特点&#xff1a;HashMap特点&#xff1a;HashSet特点&#xff1a;LinkedHashMap特点LinkedHashMap经典用法 TreeMap特点Conc…...

八、MapReduce 大规模数据处理深度剖析与实战指南

MapReduce 大规模数据处理深度剖析与实战指南 一、绪论 在当今的大数据时代背景下&#xff0c;海量数据的处理已然成为企业及科研机构所面临的重大挑战。MapReduce 作为一种高效的分布式计算模型&#xff0c;在大规模数据处理领域中发挥着至关重要的作用。本文将深入阐释 MapR…...

开源免费的API网关介绍与选型

api网关的主要作用 API网关在现代微服务架构中扮演着至关重要的角色&#xff0c;它作为内外部系统通信的桥梁&#xff0c;不仅简化了服务调用过程&#xff0c;还增强了系统的安全性与可管理性。例如&#xff0c;当企业希望将内部的服务开放给外部合作伙伴使用时&#xff0c;直…...

OpenCV视觉分析之目标跟踪(5)目标跟踪类TrackerMIL的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 MIL 算法以在线方式训练分类器&#xff0c;以将目标从背景中分离出来。多重实例学习&#xff08;Multiple Instance Learning&#xff09;通过在…...

二级列表联动

介绍 本示例主要介绍了List组件实现二级联动&#xff08;Cascading List&#xff09;的场景。 该场景多用于商品种类的选择、照片不同类型选择等场景。 效果图 使用说明&#xff1a; 滑动二级列表侧控件&#xff08;点击没用&#xff09;&#xff0c;一级列表随之滚动。&…...

「C/C++」C++ 标准库 之 #include<sstream> 字符串流库

✨博客主页何曾参静谧的博客&#x1f4cc;文章专栏「C/C」C/C程序设计&#x1f4da;全部专栏「VS」Visual Studio「C/C」C/C程序设计「UG/NX」BlockUI集合「Win」Windows程序设计「DSA」数据结构与算法「UG/NX」NX二次开发「QT」QT5程序设计「File」数据文件格式「PK」Parasoli…...

深入理解跨域资源共享(CORS)安全问题原理及解决思路

目录 引言 CORS 基础 CORS 安全问题原理 解决思路 结论 引言 跨域资源共享&#xff08;CORS, Cross-Origin Resource Sharing&#xff09;是现代Web应用中不可或缺的一部分&#xff0c;特别是在前后端分离的架构中。CORS允许一个域上的Web应用请求另一个域上的资源&#…...

【汽车租聘管理与推荐】Python+Django网页界面+推荐算法+管理系统网站

一、介绍 汽车租聘管理与推荐系统。本系统使用Python作为主要编程语言&#xff0c;前端采用HTML、CSS、BootStrap等技术搭建前端界面&#xff0c;后端采用Django框架处理用户的请求。创新点&#xff1a;使用协同过滤推荐算法实现对当前用户个性化推荐。 其主要功能如下&#…...

Linux常见指令大全(必要+知识点)

目录 ls 指令☑️ 在Windows中会自动显示当前目录当中的所有子目录与文件&#xff0c;但是在Linux中要用到ls指令。 语法&#xff1a; ls [选项][目录或文件] 功能&#xff1a;对于目录&#xff0c;该命令列出该目录下的所有子目录与文件。对于文件&#xff0c;将列出文件名以…...

iOS用rime且导入自制输入方案

iPhone 16 的 cantonese 只能打传统汉字&#xff0c;没有繁简转换&#xff0c;m d sh d。考虑用「仓」输入法 [1] 使用 Rime 打字&#xff0c;且希望导入自制方案 [2]。 仓输入法有几种导入方案的方法&#xff0c;见 [3]&#xff0c;此处记录 wifi 上传法。准备工作&#xff1…...

Linux 进程终止 进程等待

目录 进程终止 退出码 错误码 代码异常终止(信号详解) exit _exit 进程等待 概念 等待的原因 wait 函数原型 参数 返回值 监控脚本 waitpid 概念 函数原型 参数 返回值 WIFEXITED(status) WEXITSTATUS(status) 问题 为什么不用全局变量获得子进程的退出信…...

VBA 64位API声明语句第003讲

跟我学VBA&#xff0c;我这里专注VBA, 授人以渔。我98年开始&#xff0c;从源码接触VBA已经20余年了&#xff0c;随着年龄的增长&#xff0c;越来越觉得有必要把这项技能传递给需要这项技术的职场人员。希望职场和数据打交道的朋友&#xff0c;都来学习VBA,利用VBA,起码可以提高…...

【问题记录】解决VMware虚拟机中鼠标侧键无法使用的问题

前言 有项目需要在Linux系统中开发&#xff0c;因为要测试Linux中相关功能&#xff0c;要用到shell&#xff0c;在Windows中开发太麻烦了&#xff0c;因此我选择使用UbuntuXfce4桌面来开发&#xff0c;这里我用到了Linux版本的IDEA&#xff0c;除了快捷键经常和系统快捷键冲突…...

Naive UI 级联选择器 Cascader的:render-lable怎么使用(Vue3 + TS)(鼠标悬停该条数据的时候展示全部内容)

项目场景&#xff1a; 在渲染Cascader级联选择器后&#xff0c;当文字过长的时候&#xff0c;多出来的部分会显示成省略号&#xff0c;这使我们不能很清晰的看到该条数据的完整信息&#xff0c;就需要加一个鼠标悬停展示完整内容。 解决方案&#xff1a; vue&#xff1a; &l…...

vue元素里面的 js对象中,:style后面里属性名不支持这种带-的写法(background-color)

首先要知道&#xff0c;在这个:style里面&#xff0c;虽然可以用 {属性: 属性值 , 属性: 属性值} 这种方方式来写很多属性&#xff0c;但也仅限于width这种普通属性&#xff0c;像background-color这种带-的特殊标签是不支持直接写的&#xff1b; <div class"box&quo…...

Git 常用命令与开发流程总结

引言 在我之前面试过程中&#xff0c;经常会问到关于公司使用什么代码版本管理工具。 无非是考察咱们是否用过 Git和SVN。 现在公司选择的工具直接影响到项目的开发流程和协作效率。当前市面上&#xff0c;Git 和 SVN&#xff08;Subversion&#xff09;是两种流行的版本控制系…...