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

详解Jetpack Compose中的状态管理与使用

前言

  引用一段官方描述,如下

由于 Compose 是声明式工具集,因此更新它的唯一方法是通过新参数调用同一可组合项。这些参数是界面状态的表现形式。每当状态更新时,都会发生重组。因此,TextField 不会像在基于 XML 的命令式视图中那样自动更新。可组合项必须明确获知新状态,才能相应地进行更新。

  google想表达的是compose不会像xml布局一样,可以简单的在代码里主动调用方法(比如setText(),setImageResource()等等)就能去刷新UI内容,而是需要通过状态管理通知UI的内容需要更新。 

  而Compose的状态管理更符合MVVM思想的,虽然Jetpack很早之前就已经推出了ViewModel、LiveData、MutableLiveData、DataBinding(这个最有毒,开创了XML写逻辑的先河)作为状态管理。但是因为XML与Activity的定位原因,都让现在的Android编程很难说是MVVM模式,只能说是接近。

  其中尴尬的原因是:

  1.XML既是View层实现编写,但是又无法更新控制View层。并且XML的样式就已经表明它不适合编写逻辑控制View。

  2.Activity既是View层控制器,又不实现View的代码编写。

    他们本应该合二为一,但是却分开了。导致MVC,MVP,MVVM思想都无法完全契合Android平台,使很多新人在Android平台学习使用这3种思想时,会经常陷入困惑。

状态管理涉及到类与方法

  • remember:保存数据,并且在UI更新时会提供保存的值。但是Activity页面退出后会丢失保存的值
  • rememberSaveable:保存数据,并且将值写入到bundle中,然后重新构建Activity的时候,从bundle读数据。这表示Activity退出后也不会丢失值。
  • mutableStateOf :一个可变并且被Compose时刻观察的状态存储,作用就是让Compose可以获知数据已经改变,UI上的内容需要重新绘制。
  • mutableStateListOf:mutableStateOf只能观察单个类型数据的变化,无法观察到集合数据的变化。所以有了mutableStateListOf,方法参数带vararg关键字,所以它也可以是多个List组成的数组
  • mutableStateMapOf:同上,只不过是以哈希的形式,方法参数带vararg关键字,所以它也可以是数组
  • derivedStateOf:定义的对象状态依赖其他的对象状态时,需要使用derivedStateOf,当依赖对象状态发生改变,自己也可以跟着改变。

看完上面的可以明白,remember是用于临时保存数据的,而MutableState是用于通知与传递数据变化的。

remember与mutableStateOf 的使用例子(一个快速了解的Demo)

实现一个按键点击自增数值并且显示的Demo,一般情况下mutableStateOf 与 remember都是配合使用的(但是他们不是绑定关系,都可以单独使用)。直接使用mutableStateOf 与 remember组合使用的区别是什么?请看博客后面的”为什么mutableStateOf不能直接写到方法内部的例子“ 但是,建议你先保留疑问按顺序看下去。

下面代码里展示了3种创建方式,但是这3种方式都是不同的语法糖,结果是一样的。

代码:

class DeploymentActivity : ComponentActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContent {MyButton()}}@Preview(name = "按键自增计数")@Composablefun MyButton() {Column() {/*使用by需要引用import androidx.compose.runtime.getValueimport androidx.compose.runtime.setValue*/var count1 by remember { mutableStateOf(0) }Button(onClick = { count1++ }) {Text(text = "按键A = $count1")}var count2 = remember { mutableStateOf(0) }Button(onClick = { count2.value++ }) {Text(text = "按键B = ${count2.value}")}var (count3, setValue) = remember { mutableStateOf(0) }Button(onClick = { setValue.invoke(count3+1) }) {Text(text = "按键C = $count3")}}}
}

效果动图:

mutableStateListOf的使用例子

mutableStateListOf 是用在集合数据的情况下它能在集合数据变动的情况下触发重组,因为如果使用mutableStateOf将会无法观察到集合数据的变动,从而不触发重组。

private var mImageList = mutableStateListOf<String>()@Composableprivate fun collectContentList() {LazyVerticalGrid(columns = GridCells.Adaptive(minSize = 256.dp),verticalArrangement = Arrangement.spacedBy(20.dp),horizontalArrangement = Arrangement.spacedBy(20.dp),contentPadding = PaddingValues(top = 20.dp, start = 20.dp, end = 20.dp)) {items(mImageList.size) { index ->AsyncImage(model = mImageList[index],contentDescription = null,contentScale = ContentScale.Crop,modifier = Modifier.width(256.dp).height(128.dp).pointerInput(Unit) {detectTapGestures(onTap = {DrawFromActivity.jumpCarryFileImage(context = requireContext(),mImageList[index])},onLongPress = {mCurrentDeleteImagePath = mImageList[index]mIsShowDeleteDialog.value = true})}.clip(RoundedCornerShape(10.dp)))}}}

 

mutableStateMapOf的使用例子

    override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContent {MyMapList()}}@Composablefun MyMapList() {val dataMap = remember {mutableStateMapOf("key1" to "value1","key2" to "value2","key3" to "value3","key4" to "value4")}LazyColumn {items(dataMap.size){ index->val itemKey = dataMap.keys.toMutableList()[index]val itemValue = dataMap[itemKey]itemValue?.let { Text(text = it) }}}}

derivedStateOf的使用例子

 derivedStateOf的使用场景是,某个数据需要依靠其他状态管理的计算或者派生的情况。

代码例子如下:

我们需要计数,并且计数的结果派生一新的需求判断是奇数还是偶数。

    @Preview@Composablefun MyText() {val count = remember { mutableStateOf(0) }//是否是奇数val isOddNumber = remember {derivedStateOf {count.value % 2 != 0}}Text(text = "计数 = ${count.value} 是否是奇数 = ${isOddNumber.value}",color = Color.White,modifier = Modifier.clickable {count.value++})}

结果:

remember的带参使用例子

  remember不带参的使用例子已经在上面说明过了,不在重复举例。现在说说remember的带参使用例子。

  remember的代码块,只会在第一次创建的时候执行一次,后续就不会在执行了。如果我们有需求希望在Compose方法重组的时候remember的代码块在执行一次怎么办? 那就需要使用remember带参的情况,只要改变key就会让remember在compose重组的时候重新执行一次代码块。

举一个反面参考,不带参的代码例子:

    @Composablefun MyText() {//这个count是用来触发整个方法重组的val count = remember { mutableStateOf(0) }//不添加keyval randomNum = remember() {Log.e("zh", "remember被重新执行代码块了")(0..99).random()}Text(text = "按键A = ${count.value}", modifier = Modifier.clickable {count.value++Log.e("zh", "randomNum = ${randomNum}")})}

 点击Text后重组的结果,可以看到随机数没有变化,固定在51,并且在remember代码块里的log日志也没有打印。

带参的例子:

请注意,因为remember是在Compose内部的所以,想让带参remember重新执行代码块就需要让Compose发生一次重组,所以下面的count是用来触发重组的。

    var key = 0@Composablefun MyText() {//这个count是用来触发整个方法重组的val count = remember { mutableStateOf(0) }//添加keyval randomNum = remember(key) {Log.e("zh", "remember被重新执行代码块了")(0..99).random()}Text(text = "按键A = ${count.value}", modifier = Modifier.clickable {key++count.value++Log.e("zh", "key = ${key}")Log.e("zh", "randomNum = ${randomNum}")})}

点击Text后重组的结果,可以因为key的改变,randomNum的remember也被触发重新执行了代码块。从而更新了随机数的值。

rememberSaveable的使用例子

保存数据,并且将值写入到bundle中,然后重新构建Activity的时候,从bundle读数据。这表示Activity退出后也不会丢失值。

 代码:

    @Composablefun MyText() {val count = rememberSaveable {mutableStateOf(0)}Text(text = "计数 = ${count.value}  ",color = Color.Black,modifier = Modifier.clickable {count.value++})}

理解MutableState重组UI组件范围

mutableState的重组UI组件范围是在它读取与写入的范围里的。为了验证这个说法,请看下面的代码例子:

多个@Composable方法组合下的重组UI范围例子1:

下面的代码中,在Column被点击后,增加了count的数值但是并不会引起任何的UI重组。因为三个Text都没有引用count。

    override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContent {val count = remember { mutableStateOf(1) }Column(modifier = Modifier.clickable { count.value++ }) {Log.e("zh", "Column触发重组")Text1()Text2()Text3()}}}@Composablefun Text1() {Log.e("zh", "Text1触发重组")Text(text = "测试")}@Composablefun Text2() {Log.e("zh", "Text2触发重组")Text(text = "测试")}@Composablefun Text3() {Log.e("zh", "Text3触发重组")Text(text = "测试")}

多个@Composable方法组合下的重组UI范围例子2:

在下面的代码中Text1引用了count数据,所以在点击Columu增加了count数值后,重组范围只在自定义的Text1方法里,在外部的Column也没有触发重组。

    override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContent {val count = remember { mutableStateOf(1) }Column(modifier = Modifier.clickable { count.value++ }) {Log.e("zh", "Column触发重组")Text1(count)Text2(count)Text3(count)}}}@Composablefun Text1(count: MutableState<Int>) {Log.e("zh", "Text1触发重组 count = ${count.value} count内存地址= ${count}")Text(text = "测试 ${count.value}")}@Composablefun Text2(count: MutableState<Int>) {Log.e("zh", "Text2触发重组")Text(text = "测试")}@Composablefun Text3(count: MutableState<Int>) {Log.e("zh", "Text3触发重组")Text(text = "测试")}

@Composable方法内部的重组范围例子:

 在Text被点击后方法内部的所有组件都被重组了。但是有特例并不是所有情况下整个方法内部都会触发重组,在调用了Button、Surface、CompositionLocalProvider情况下重组范围只会被限制在这些组件的内部(其实Button、Surface内部含CompositionLocalProvider,导致的重组只会限制在他们的范围内)

    override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContent {MyButton()}}@Composablefun MyButton() {val count = remember { mutableStateOf(1) }Log.e("zh", "触发重组1")Column {Log.e("zh", "触发重组2")Column {Log.e("zh", "触发重组3")Text(text = "数值 = ${count.value}", modifier = Modifier.width(100.dp).height(100.dp).clickable { count.value++ })}}}

@Composable方法内部CompositionLocalProvider的重组范围例子:

Button、Surface内部含CompositionLocalProvider,所以一起举例。在下面的代码中,点击任何一个组件增加Count数值后,Column下的任何log都不会触发了,因为重组范围被限定在CompositionLocalProvider。

    override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContent {MyButton()}}@OptIn(ExperimentalMaterialApi::class)@Composablefun MyButton() {val count = remember { mutableStateOf(1) }Log.e("zh", "触发重组1")Column {Log.e("zh", "触发重组2")Column {Log.e("zh", "触发重组3")Button(onClick = { count.value++ }) {Log.e("zh", "Button触发重组")Text(text = "Button = ${count.value}")}CompositionLocalProvider(){Log.e("zh", "CompositionLocalProvider触发重组")Text(text = "CompositionLocalProvider = ${count.value}", modifier = Modifier.width(100.dp).height(100.dp).clickable { count.value++ })}Surface(onClick = { count.value++ },modifier = Modifier.width(100.dp).height(100.dp)) {Log.e("zh", "Surface触发重组")Text(text = "Surface = ${count.value}")}}}}

为什么mutableStateOf不能直接写到方法内部的例子 :

在上面的例子里所有创建mutableStateOf的外部都套了一个remember。 那么肯定有人会疑问,为什么要增加remember? 不直接在方法内部创建mutableStateOf呢? 其实这个问题的关键是理解组件的重组。因为组件方法的每一次重组都会导致 mutableStateOf 被重新创建一次。remember的文字意思是记住,所以remember的作用就是将mutableStateOf或者其他实体数据引用到保存到每个Compose的SlotTable中,不受其重组的影响。

 在下面的代码中,我们故意错误的在组件方法里直接创建mutableStateOf。看看在Text点击后让Count自增后,重组后会引起什么问题:

    @SuppressLint("UnrememberedMutableState") //在内部调用mutableStateOf会出现警告@Composablefun MyButton() {val count = mutableStateOf(1)Log.e("zh", "count地址 = ${count}")Column {//因为Button含有CompositionLocalProvider不会导致外部也触发重组,所以这里用Text替代Text(text = "按键A = ${count.value}", modifier = Modifier.clickable { count.value++ })Log.e("zh", "count = ${count.value}")}}

结果就是每次组件方法的重组也把MutableState重新创建了,导致数值不会自增,并且内存地址每次都是新的。

但是mutableStateOf可以写在外部,下面代码中mCount1是保存在Activity这个类的全局变量中,而count2是保存在Composable创建的Compose的SlotTable中,但是二者在使用上没有什么特别大的区别。

    val mCount1 = mutableStateOf(1)@Composablefun MyText() {val count2 = remember { mutableStateOf(1) }Column {Text(text = "mCount1 ${mCount1}")Text(text = "count2 ${count2}")}}

MutableState通知UI重组机制

这里用下面的图片可以简单了解一下...  .MutableState的机制相当复杂,想要深入了解特别烧脑。因为代码追踪并不好用,你得用到debug调试才能找到他们的观察者消息的发送与接收。个人认为只要了解SnapshotMutableStateImpl,简单的理解State与快照Snapshot的机制即可。

remember的原理

下面用贴源码方式,展示remember的流程,看看remember将数据缓存到哪里去了。

 源码一

 

/*** 记住高阶函数calculation执行后产生的值。重组将总是返回产生的值*/
@Composable
inline fun <T> remember(calculation: @DisallowComposableCalls () -> T): T =currentComposer.cache(false, calculation)

源码二

/*** A Compose compiler plugin API. DO NOT call directly.* 缓存记录,一个组合的组合数据中的值。编译器插件使用它来生成更有效的调用,以便在确定这些操作是安全的时候进行记录。*/
@ComposeCompilerApi
inline fun <T> Composer.cache(invalid: Boolean, block: @DisallowComposableCalls () -> T): T {@Suppress("UNCHECKED_CAST")return rememberedValue().let {if (invalid || it === Composer.Empty) {val value = block()updateRememberedValue(value)value} else it} as T
}

源码三

   override fun updateRememberedValue(value: Any?) = updateValue(value)

源码四

    /*** 将SlotTable的值更新为[value]的当前值。** @param value the value to schedule to be written to the slot table.*/@PublishedApi@OptIn(InternalComposeApi::class)internal fun updateValue(value: Any?) {if (inserting) {//插入新的值writer.update(value)if (value is RememberObserver) {record { _, _, rememberManager -> rememberManager.remembering(value) }abandonSet.add(value)}} else {//更新已经存在的值val groupSlotIndex = reader.groupSlotIndex - 1if (value is RememberObserver) {abandonSet.add(value)}recordSlotTableOperation(forParent = true) { _, slots, rememberManager ->if (value is RememberObserver) {rememberManager.remembering(value)}//这里的set方法可以将新值保存到SlotTable里,并且将旧的值返回when (val previous = slots.set(groupSlotIndex, value)) {is RememberObserver ->//观察者记录管理类,将以前的注册的RememberObserver观察者移除rememberManager.forgetting(previous)//重组范围实施类is RecomposeScopeImpl -> {val composition = previous.compositionif (composition != null) {//释放之前的值previous.release()//设置当前composition失效范围composition.pendingInvalidScopes = true}}}}}}

remember存在的意义是什么

      在文章上面的 “为什么mutableStateOf不能直接写到方法内部的例子” 中已经讲解了大部分。这边在重复啰嗦一下,意义就是给每个Compose保存一份需要缓存的数据,使其不受到Compose重组的影响。这种设计是因为移动平台的应用有切换前后台需求,从而有页面生命周期的概念。需要Compose缓存一份数据用于前后台切换后的数据恢复展示。

相关文章:

详解Jetpack Compose中的状态管理与使用

前言 引用一段官方描述&#xff0c;如下 由于 Compose 是声明式工具集&#xff0c;因此更新它的唯一方法是通过新参数调用同一可组合项。这些参数是界面状态的表现形式。每当状态更新时&#xff0c;都会发生重组。因此&#xff0c;TextField 不会像在基于 XML 的命令式视图中那…...

改进YOLOv7 | 头部解耦 | 将YOLOX解耦头添加到YOLOv7 | 涨点杀器

改进YOLOv7 | 头部解耦 | 将YOLOX解耦头添加到YOLOv7 论文地址:https://arxiv.org/abs/2107.08430 文章目录 改进YOLOv7 | 头部解耦 | 将YOLOX解耦头添加到YOLOv71. 解耦头原理2. 解耦头对收敛速度的影响3. 解耦头对精度的影响4. 代码改进方式第一步第二步第三步第四步第五步参…...

第七章 中断

中断是什么&#xff0c;为什么要有中断 并发是指单位时间内的累积工作量。 并行是指真正同时进行的工作量。 一个CPU在一个时间只能执行一个进程&#xff0c;任何瞬间任务只在一个核心上运行。 而CPU外的设备是独立于CPU的&#xff0c;它与CPU同步运行&#xff0c;CPU抽出一点…...

1116 Come on! Let‘s C(38行代码+详细注释)

分数 20 全屏浏览题目 作者 CHEN, Yue 单位 浙江大学 "Lets C" is a popular and fun programming contest hosted by the College of Computer Science and Technology, Zhejiang University. Since the idea of the contest is for fun, the award rules are f…...

深入学习《c语言函数》

&#x1f4d5;博主介绍&#xff1a;目前大一正在学习c语言&#xff0c;数据结构&#xff0c;计算机网络。 c语言学习&#xff0c;是为了更好的学习其他的编程语言&#xff0c;C语言是母体语言&#xff0c;是人机交互接近底层的桥梁。 本章学习函数。 让我们开启c语言学习之旅吧…...

Pytorch从零开始实现Vision Transformer (from scratch)

Pytorch从零开始实现Vision Transformer 前言一、Vision Transformer架构介绍1. Patch Embedding2. Multi-Head Attention3. Transformer BlockFeed Forward 二、预备知识1. Einsum2. Einops 三、Vision Transformer代码实现0. 导入库1. Patch Embedding2. Residual & Norm…...

ES6函数新增了哪些扩展?

目录 一、参数二、属性函数的length属性name属性 三、作用域四、严格模式五、箭头函数 一、参数 ES6允许为函数的参数设置默认值 function log(x, y World) {console.log(x, y); }console.log(Hello) // Hello World console.log(Hello, China) // Hello China console.log(…...

【firewalld防火墙】

目录 一、firewalld概述二、firewalld 与 iptables 的区别1、firewalld 区域的概念 三、firewalld防火墙默认的9个区域四、Firewalld 网络区域1、区域介绍2、firewalld数据处理流程 五、firewalld防火墙的配置方法1、使用firewall-cmd 命令行工具。2、使用firewall-config 图形…...

CNNs: ZFNet之CNN的可视化网络介绍

CNNs: ZFNet之CNN的可视化网络介绍 导言Deconvnet1. Unpooling2. ReLU3. Transpose conv AlexNet网络修改AlexNet Deconv网络介绍特征可视化 导言 上一个内容&#xff0c;我们主要学习了AlexNet网络的实现、超参数对网络结果的影响以及网络中涉及到一些其他的知识点&#xff0…...

云原生之深入解析Airbnb的动态Kubernetes集群扩缩容

一、前言 Airbnb 基础设施的一个重要作用是保证我们的云能够根据需求上升或下降进行自动扩缩容&#xff0c;我们每天的流量波动都非常大&#xff0c;需要依靠动态扩缩容来保证服务的正常运行。为了支持扩缩容&#xff0c;Airbnb 使用了 Kubernetes 编排系统&#xff0c;并且使…...

Django框架之模板其他补充

本篇文章是对django框架模板内容的一些补充。包含注释、html转义和csrf内容。 目录 注释 单行注释 多行注释 HTML转义 Escape Safe Autoescape CSRF 防止csrf方式 表单中使用 ajax请求添加 注释 单行注释 语法&#xff1a;{# 注释内容 #} 示例&#xff1a; {# 注…...

安装Maven 3.6.1:图文详细教程(适用于Windows系统)

一、官网下载对应版本 推荐使用maven3.6.1版本&#xff0c;对应下载链接&#xff1a; Maven3.6.1下载地址 或者&#xff0c;这里提供csdn下载地址&#xff0c;点击下载即可&#xff1a; Maven3.6.1直链下载 其他版本下载地址&#xff1a; 进入网址&#xff1a;http://mave…...

计算机图形学 | 实验八:Phong模型

计算机图形学 | 实验八&#xff1a;Phong模型 计算机图形学 | 实验八&#xff1a;Phong模型Phong模型光源设置 光照计算定向光点光源聚光 华中科技大学《计算机图形学》课程 MOOC地址&#xff1a;计算机图形学&#xff08;HUST&#xff09; 计算机图形学 | 实验八&#xff1a…...

第三十一回:GestureDetector Widget

文章目录 概念介绍使用方法示例代码 我们在上一章回中介绍了ListView响应事件的内容t,本章回中将介绍 GestureDetector Widget.闲话休提&#xff0c;让我们一起Talk Flutter吧。 概念介绍 我们在这里介绍的GestureDetector是一个事件响应Widget,它可以响应双击事件&#xff0…...

Java面试知识点(全)-Java并发-多线程JUC三- JUC集合/线程池

Java面试知识点(全) 导航&#xff1a; https://nanxiang.blog.csdn.net/article/details/130640392 注&#xff1a;随时更新 JUC集合类 为什么HashTable慢? 它的并发度是什么? 那么ConcurrentHashMap并发度是什么? Hashtable之所以效率低下主要是因为其实现使用了synchro…...

Android 如何获取有效的DeviceId

目录 前言官方唯一标识符建议使用广告 ID使用实例 ID 和 GUID不要使用 MAC 地址标识符特性常见用例和适用的标识符 解决方案DeviceIdANDROID_IDMac地址UUID补充 总结 前言 从 Android 10 开始&#xff0c;应用必须具有 READ_PRIVILEGED_PHONE_STATE 特许权限才能访问设备的不可…...

<SQL>《SQL命令(含例句)精心整理版(2)》

《SQL命令&#xff08;含例句&#xff09;精心整理版&#xff08;2&#xff09;》 跳转《SQL命令&#xff08;含例句&#xff09;精心整理版&#xff08;1&#xff09;8 函数8.1 文本处理函数8.2 数值处理函数8.3 时间处理函数8.3.1 时间戳转化为自定义格式from_unixtime8.3.2 …...

完全自主研发,聚芯微发布3D dToF图像传感器芯片!

日前&#xff0c;由中国半导体行业协会IC设计分会&#xff08;ICCAD&#xff09;、芯原股份、松山湖管委会主办的主题为“AR/VR/XR元宇宙”的“2023松山湖中国IC创新高峰论坛”正式在广东东莞松山湖召开。武汉市聚芯微电子有限责任公司发布了完全自主知识产权的3D dToF图像传感…...

MySQL 事物(w字)

目录 事物 首先我们来看一个简单的问题 什么是事务 为什么会出现事务 事务的版本支持 事务提交方式 事务常见操作方式 设置隔离级别 事物操作 事物结论 事务隔离级别 理解隔离性 隔离级别 查看与设置隔离性 注意可重复读【Repeatable Read】的可能问题&#xff…...

字节跳动测试岗四面总结....

字节一面 1、 简单做一下自我介绍 2、 简要介绍一下项目/你负责的模块/选一个模块说一下你设计的用例 3 、get请求和post请求的区别 4、 如何判断前后端bug/3xx是什么意思 5、 说一下XXX项目中你做的接口测试/做了多少次 6、 http和https的区别 7、 考了几个ADB命令/查看…...

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站&#xff0c;会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后&#xff0c;网站没有变化的情况。 不熟悉siteground主机的新手&#xff0c;遇到这个问题&#xff0c;就很抓狂&#xff0c;明明是哪都没操作错误&#x…...

web vue 项目 Docker化部署

Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段&#xff1a; 构建阶段&#xff08;Build Stage&#xff09;&#xff1a…...

使用VSCode开发Django指南

使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架&#xff0c;专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用&#xff0c;其中包含三个使用通用基本模板的页面。在此…...

1688商品列表API与其他数据源的对接思路

将1688商品列表API与其他数据源对接时&#xff0c;需结合业务场景设计数据流转链路&#xff0c;重点关注数据格式兼容性、接口调用频率控制及数据一致性维护。以下是具体对接思路及关键技术点&#xff1a; 一、核心对接场景与目标 商品数据同步 场景&#xff1a;将1688商品信息…...

系统设计 --- MongoDB亿级数据查询优化策略

系统设计 --- MongoDB亿级数据查询分表策略 背景Solution --- 分表 背景 使用audit log实现Audi Trail功能 Audit Trail范围: 六个月数据量: 每秒5-7条audi log&#xff0c;共计7千万 – 1亿条数据需要实现全文检索按照时间倒序因为license问题&#xff0c;不能使用ELK只能使用…...

WordPress插件:AI多语言写作与智能配图、免费AI模型、SEO文章生成

厌倦手动写WordPress文章&#xff1f;AI自动生成&#xff0c;效率提升10倍&#xff01; 支持多语言、自动配图、定时发布&#xff0c;让内容创作更轻松&#xff01; AI内容生成 → 不想每天写文章&#xff1f;AI一键生成高质量内容&#xff01;多语言支持 → 跨境电商必备&am…...

【HTML-16】深入理解HTML中的块元素与行内元素

HTML元素根据其显示特性可以分为两大类&#xff1a;块元素(Block-level Elements)和行内元素(Inline Elements)。理解这两者的区别对于构建良好的网页布局至关重要。本文将全面解析这两种元素的特性、区别以及实际应用场景。 1. 块元素(Block-level Elements) 1.1 基本特性 …...

数据库分批入库

今天在工作中&#xff0c;遇到一个问题&#xff0c;就是分批查询的时候&#xff0c;由于批次过大导致出现了一些问题&#xff0c;一下是问题描述和解决方案&#xff1a; 示例&#xff1a; // 假设已有数据列表 dataList 和 PreparedStatement pstmt int batchSize 1000; // …...

纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join

纯 Java 项目&#xff08;非 SpringBoot&#xff09;集成 Mybatis-Plus 和 Mybatis-Plus-Join 1、依赖1.1、依赖版本1.2、pom.xml 2、代码2.1、SqlSession 构造器2.2、MybatisPlus代码生成器2.3、获取 config.yml 配置2.3.1、config.yml2.3.2、项目配置类 2.4、ftl 模板2.4.1、…...

JavaScript 数据类型详解

JavaScript 数据类型详解 JavaScript 数据类型分为 原始类型&#xff08;Primitive&#xff09; 和 对象类型&#xff08;Object&#xff09; 两大类&#xff0c;共 8 种&#xff08;ES11&#xff09;&#xff1a; 一、原始类型&#xff08;7种&#xff09; 1. undefined 定…...