HarmonyOS:ForEach:循环渲染
一、前言
ForEach接口基于数组类型数据来进行循环渲染,需要与容器组件配合使用,且接口返回的组件应当是允许包含在ForEach父容器组件中的子组件。例如,ListItem组件要求ForEach的父容器组件必须为List组件。
API参数说明见:ForEach API参数说明。
说明
从API version 9开始,该接口支持在ArkTS卡片中使用。
二、键值生成规则
在ForEach循环渲染过程中,系统会为每个数组元素生成一个唯一且持久的键值,用于标识对应的组件。当这个键值变化时,ArkUI框架将视为该数组元素已被替换或修改,并会基于新的键值创建一个新的组件。
ForEach提供了一个名为keyGenerator的参数,这是一个函数,开发者可以通过它自定义键值的生成规则。如果开发者没有定义keyGenerator函数,则ArkUI框架会使用默认的键值生成函数,即(item: Object, index: number) => { return index + ‘__’ + JSON.stringify(item); }。
ArkUI框架对于ForEach的键值生成有一套特定的判断规则,这主要与itemGenerator函数的第二个参数index以及keyGenerator函数的第二个参数index有关,具体的键值生成规则判断逻辑如下图所示。
ForEach键值生成规则
说明
ArkUI框架会对重复的键值发出警告。在UI更新的场景下,如果出现重复的键值,框架可能无法正常工作,具体请参见渲染结果非预期。
三、组件创建规则
在确定键值生成规则后,ForEach的第二个参数itemGenerator函数会根据键值生成规则为数据源的每个数组项创建组件。组件的创建包括两种情况:ForEach首次渲染和ForEach非首次渲染。
3.1 首次渲染
在ForEach首次渲染时,会根据前述键值生成规则为数据源的每个数组项生成唯一键值,并创建相应的组件。
@Component
struct ChildItem {@Prop item: stringbuild() {Text(this.item).fontSize(20).fontWeight(FontWeight.Medium).fontColor($r('app.color.c_black')).backgroundColor("#f5f5f5").borderRadius(8).padding(16).width('100%')}
}@Entry
@Component
struct TestForEach1 {@State message: string = 'ForEach:循环渲染';@State simpleList: Array<string> = ['one', 'two', 'three'];build() {Column({ space: 10 }) {ForEach(this.simpleList, (item: string) => {ChildItem({ item: item })}, (item: string) => item)}.height('100%').width('100%').padding(16).backgroundColor("#ffffff")}
}
ForEach数据源不存在相同值案例首次渲染运行效果图
在上述代码中,键值生成规则是keyGenerator函数的返回值item。在ForEach渲染循环时,为数据源数组项依次生成键值one、two和three,并创建对应的ChildItem组件渲染到界面上。
当不同数组项按照键值生成规则生成的键值相同时,框架的行为是未定义的。例如,在以下代码中,ForEach渲染相同的数据项two时,只创建了一个ChildItem组件,而没有创建多个具有相同键值的组件。
@Component
struct ChildItem {@Prop item: stringbuild() {Text(this.item).fontSize(20).fontWeight(FontWeight.Medium).fontColor($r('app.color.c_black')).backgroundColor("#f5f5f5").borderRadius(8).padding(16).width('100%')}
}@Entry
@Component
struct TestForEach1 {@State message: string = 'ForEach:循环渲染';// @State simpleList: Array<string> = ['one', 'two', 'three'];@State simpleList: Array<string> = ['one', 'two', 'two', 'three'];build() {Column({ space: 10 }) {ForEach(this.simpleList, (item: string) => {ChildItem({ item: item })}, (item: string) => item)}.height('100%').width('100%').padding(16).backgroundColor("#ffffff")}
}
ForEach数据源存在相同值案例首次渲染运行效果图
在该示例中,最终键值生成规则为item。当ForEach遍历数据源simpleList,遍历到索引为1的two时,按照最终键值生成规则生成键值为two的组件并进行标记。当遍历到索引为2的two时,按照最终键值生成规则当前项的键值也为two,此时不再创建新的组件。
3.2 非首次渲染
在ForEach组件进行非首次渲染时,它会检查新生成的键值是否在上次渲染中已经存在。如果键值不存在,则会创建一个新的组件;如果键值存在,则不会创建新的组件,而是直接渲染该键值所对应的组件。例如,在以下的代码示例中,通过点击事件修改了数组的第三项值为"new three",这将触发ForEach组件进行非首次渲染。
@Component
struct ChildItem {@Prop item: stringbuild() {Text(this.item).fontSize(20).fontWeight(FontWeight.Medium).fontColor($r('app.color.c_black')).backgroundColor("#f5f5f5").borderRadius(8).padding(16).width('100%')}
}@Entry
@Component
struct TestForEach1 {@State message: string = 'ForEach:循环渲染';@State simpleList: Array<string> = ['one', 'two', 'three'];// @State simpleList: Array<string> = ['one', 'two', 'two', 'three'];build() {Column({ space: 10 }) {Button('点击修改第3个数组项的值').fontSize(24).fontColor(Color.White).onClick(() => {this.simpleList[2] = 'new three';console.log("点击了 修改第3个数组项的值 事件")})ForEach(this.simpleList, (item: string) => {ChildItem({ item: item })}, (item: string) => item)}.height('100%').width('100%').padding(16).backgroundColor("#ffffff")}
}
ForEach非首次渲染案例运行效果图
从本例可以看出@State 能够监听到简单数据类型数组数据源 simpleList 数组项的变化。
- 当 simpleList 数组项发生变化时,会触发 ForEach 进行重新渲染。
- ForEach 遍历新的数据源 [‘one’, ‘two’, ‘new three’],并生成对应的键值one、two和new three。
- 其中,键值one和two在上次渲染中已经存在,所以 ForEach 复用了对应的组件并进行了渲染。对于第三个数组项 “new three”,由于其通过键值生成规则 item 生成的键值new three在上次渲染中不存在,因此 ForEach 为该数组项创建了一个新的组件。
四、使用场景
ForEach组件在开发过程中的主要应用场景包括:数据源不变、数据源数组项发生变化(如插入、删除操作)、数据源数组项子属性变化。
4.1数据源不变
在数据源保持不变的场景中,数据源可以直接采用基本数据类型。例如,在页面加载状态时,可以使用骨架屏列表进行渲染展示。
@Entry
@Component
struct TestForEach2 {@State simpleList: Array<number> = [1, 2, 3, 4, 5];build() {Column() {ForEach(this.simpleList, (item: number) => {ArticleSkeletonView().margin({ top: 20 })}, (item: number) => item.toString())}.padding(20).width('100%').height('100%')}
}@Builder
function textArea(width: number | Resource | string = '100%', height: number | Resource | string = '100%') {Row().width(width).height(height).backgroundColor('#FFF2F3F4')
}@Component
struct ArticleSkeletonView {build() {Row() {Column() {textArea(80, 80)}.margin({ right: 20 })Column() {textArea('60%', 20)textArea('50%', 20)}.alignItems(HorizontalAlign.Start).justifyContent(FlexAlign.SpaceAround).height('100%')}.padding(20).borderRadius(12).backgroundColor('#FFECECEC').height(120).width('100%').justifyContent(FlexAlign.SpaceBetween)}
}
骨架屏运行效果图
在本示例中,采用数据项item作为键值生成规则,由于数据源simpleList的数组项各不相同,因此能够保证键值的唯一性。
4.2 数据源数组项发生变化
在数据源数组项发生变化的场景下,例如进行数组插入、删除操作或者数组项索引位置发生交换时,数据源应为对象数组类型,并使用对象的唯一ID作为最终键值。例如,当在页面上通过手势上滑加载下一页数据时,会在数据源数组尾部新增新获取的数据项,从而使得数据源数组长度增大。
class Article33 {id: string;title: string;brief: string;constructor(id: string, title: string, brief: string) {this.id = id;this.title = title;this.brief = brief;}
}@Entry
@Component
struct TestForEach3 {@State isListReachEnd: boolean = false;@State articleList: Array<Article33> = [new Article33('001', '第1篇文章', '文章简介内容'),new Article33('002', '第2篇文章', '文章简介内容'),new Article33('003', '第3篇文章', '文章简介内容'),new Article33('004', '第4篇文章', '文章简介内容'),new Article33('005', '第5篇文章', '文章简介内容'),new Article33('006', '第6篇文章', '文章简介内容')]loadMoreArticles() {this.articleList.push(new Article33('007', '加载的新文章', '文章简介内容'));}build() {Column({ space: 5 }) {List() {ForEach(this.articleList, (item: Article33) => {ListItem() {ArticleCard({ article: item }).margin({ top: 20 })}}, (item: Article33) => item.id)}.onReachEnd(() => {this.isListReachEnd = true;}).parallelGesture(PanGesture({ direction: PanDirection.Up, distance: 80 }).onActionStart(() => {if (this.isListReachEnd) {this.loadMoreArticles();this.isListReachEnd = false;}})).padding(20).scrollBar(BarState.Off)}.width('100%').height('100%').backgroundColor(0xF1F3F5)}
}@Component
struct ArticleCard {@Prop article: Article33;build() {Row() {// 此处'app.media.icon'仅作示例,请开发者自行替换,否则imageSource创建失败会导致后续无法正常执行。Image($r('app.media.icon')).width(80).height(80).margin({ right: 20 })Column() {Text(this.article.title).fontSize(20).margin({ bottom: 8 })Text(this.article.brief).fontSize(16).fontColor(Color.Gray).margin({ bottom: 8 })}.alignItems(HorizontalAlign.Start).width('80%').height('100%')}.padding(20).borderRadius(12).backgroundColor('#FFECECEC').height(120).width('100%').justifyContent(FlexAlign.SpaceBetween)}
}
数据源数组项变化案例运行效果图
在本示例中,ArticleCard组件作为ArticleListView组件的子组件,通过@Prop装饰器接收一个Article对象,用于渲染文章卡片。
- 当列表滚动到底部时,如果手势滑动距离超过指定的80,将触发loadMoreArticle()函数。此函数会在articleList数据源的尾部添加一个新的数据项,从而增加数据源的长度。
- 数据源被@State装饰器修饰,ArkUI框架能够感知到数据源长度的变化,并触发ForEach进行重新渲染。
4.3 数据源数组项子属性变化
当数据源的数组项为对象数据类型,并且只修改某个数组项的属性值时,由于数据源为复杂数据类型,ArkUI框架无法监听到@State装饰器修饰的数据源数组项的属性变化,从而无法触发ForEach的重新渲染。为实现ForEach重新渲染,需要结合@Observed和@ObjectLink装饰器使用。例如,在文章列表卡片上点击“点赞”按钮,从而修改文章的点赞数量。
@Observed
class Article34 {id: string;title: string;brief: string;isLiked: boolean;likesCount: number;constructor(id: string, title: string, brief: string, isLiked: boolean, likesCount: number) {this.id = id;this.title = title;this.brief = brief;this.isLiked = isLiked;this.likesCount = likesCount;}
}@Entry
@Component
struct TestForEach4 {@State isListReachEnd: boolean = false;@State articleList: Array<Article34> = [new Article34('001', '第0篇文章', '文章简介内容', false, 100),new Article34('002', '第1篇文章', '文章简介内容', false, 100),new Article34('003', '第2篇文章', '文章简介内容', false, 100),new Article34('004', '第4篇文章', '文章简介内容', false, 100),new Article34('005', '第5篇文章', '文章简介内容', false, 100),new Article34('006', '第6篇文章', '文章简介内容', false, 100),]loadMoreArticles() {this.articleList.push(new Article34('007', '加载的新文章', '文章简介内容', true, 200));}build() {Column({ space: 5 }) {List() {ForEach(this.articleList, (item: Article34) => {ListItem() {ArticleCard2({ article: item }).margin({ top: 20 })}}, (item: Article34) => item.id)}.onReachEnd(() => {this.isListReachEnd = true;}).parallelGesture(PanGesture({ direction: PanDirection.Up, distance: 80 }).onActionStart(() => {if (this.isListReachEnd) {this.loadMoreArticles();this.isListReachEnd = false;}})).padding(20).scrollBar(BarState.Off)}.width('100%').height('100%').backgroundColor(0xF1F3F5)}
}@Component
struct ArticleCard2 {// @Prop article: Article34;@ObjectLink article: Article34;handleLiked() {this.article.isLiked = !this.article.isLiked;this.article.likesCount = this.article.isLiked ? this.article.likesCount + 1 : this.article.likesCount - 1;}build() {Row() {// 此处'app.media.icon'仅作示例,请开发者自行替换,否则imageSource创建失败会导致后续无法正常执行。Image($r('app.media.icon')).width(80).height(80).margin({ right: 20 })Column() {Text(this.article.title).fontSize(20).margin({ bottom: 8 })Text(this.article.brief).fontSize(16).fontColor(Color.Gray).margin({ bottom: 8 })Row() {// 此处app.media.icon_like','app.media.icon_unlike'仅作示例,请开发者自行替换,否则imageSource创建失败会导致后续无法正常执行。Image(this.article.isLiked ? $r('app.media.icon_like') : $r('app.media.icon_unlike')).width(24).height(24).margin({ right: 8 })Text(this.article.likesCount.toString()).fontSize(16)}.onClick(() => this.handleLiked()).justifyContent(FlexAlign.Center)}.alignItems(HorizontalAlign.Start).width('80%').height('100%')}.padding(20).borderRadius(12).backgroundColor('#FFECECEC').height(120).width('100%').justifyContent(FlexAlign.SpaceBetween)}
}
数据源数组项子属性变化案例运行效果图
在本示例中,Article类被@Observed装饰器修饰。父组件ArticleListView传入Article对象实例给子组件ArticleCard,子组件使用@ObjectLink装饰器接收该实例。
- 当点击第1个文章卡片上的点赞图标时,会触发ArticleCard组件的handleLiked函数。该函数修改第1个卡片对应组件里article实例的isLiked和likesCount属性值。
- 由于子组件ArticleCard中的article使用了@ObjectLink装饰器,父子组件共享同一份article数据。因此,父组件中articleList
- 第1个数组项的isLiked和likedCounts数值也会同步修改。
- 当父组件监听到数据源数组项属性值变化时,会触发ForEach重新渲染。
- 在此处,ForEach键值生成规则为数组项的id属性值。当ForEach遍历新数据源时,数组项的id均没有变化,不会新建组件。
渲染第1个数组项对应的ArticleCard组件时,读取到的isLiked和likesCount为修改后的新值。
4.4 拖拽排序
当ForEach在List组件下使用,并且设置了onMove事件,ForEach每次迭代都生成一个ListItem时,可以使能拖拽排序。拖拽排序离手后,如果数据位置发生变化,则会触发onMove事件,上报数据移动原始索引号和目标索引号。在onMove事件中,需要根据上报的起始索引号和目标索引号修改数据源。数据源修改前后,要保持每个数据的键值不变,只是顺序发生变化,才能保证落位动画正常执行。
@Entry
@Component
struct TestForEachSort {@State arr: Array<string> = [];build() {Row() {List() {ForEach(this.arr, (item: string) => {ListItem() {Text(item.toString()).fontSize(16).textAlign(TextAlign.Center).size({height: 100, width: "100%"})}.margin(10).borderRadius(10).backgroundColor("#FFFFFFFF")}, (item: string) => item).onMove((from:number, to:number) => {let tmp = this.arr.splice(from, 1);this.arr.splice(to, 0, tmp[0])})}.width('100%').height('100%').backgroundColor("#FFDCDCDC")}}aboutToAppear(): void {for (let i = 0; i < 100; i++) {this.arr.push(i.toString())}}
}
ForEach拖拽排序效果图
4.5 使用建议
- 为满足键值的唯一性,对于对象数据类型,建议使用对象数据中的唯一id作为键值。
- 尽量避免在最终的键值生成规则中包含数据项索引index,以防止出现渲染结果非预期和渲染性能降低。如果业务确实需要使用index,例如列表需要通过index进行条件渲染,开发者需要接受ForEach在改变数据源后重新创建组件所带来的性能损耗。
- 基本数据类型的数据项没有唯一ID属性。如果使用基本数据类型本身作为键值,必须确保数组项无重复。因此,对于数据源会发生变化的场景,建议将基本数据类型数组转化为具备唯一ID属性的对象数据类型数组,再使用ID属性作为键值生成规则。
- 对于以上限制规则,index参数存在的意义为:index是开发者保证键值唯一性的最终手段;对数据项进行修改时,由于itemGenerator中的item参数是不可修改的,所以须用index索引值对数据源进行修改,进而触发UI重新渲染。
- ForEach在下列容器组件 List、Grid、Swiper以及WaterFlow 内使用的时候,不要与LazyForEach 混用。 以List为例,同时包含ForEach、LazyForEach的情形是不推荐的。
4.6 不推荐案例
开发者在使用ForEach的过程中,若对于键值生成规则的理解不够充分,可能会出现错误的使用方式。错误使用一方面会导致功能层面问题,例如渲染结果非预期,另一方面会导致性能层面问题,例如渲染性能降低。
4.7 渲染结果非预期
在本示例中,通过设置ForEach的第三个参数KeyGenerator函数,自定义键值生成规则为数据源的索引index的字符串类型值。当点击父组件Parent中“在第1项后插入新项”文本组件后,界面会出现非预期的结果。
@Entry
@Component
struct TestForEach5 {@State simpleList: Array<string> = ['one', 'two', 'three'];build() {Column() {Button('在第1项后插入新项').fontSize(20).fontWeight(FontWeight.Medium).type(ButtonType.Normal).onClick(() => {this.simpleList.splice(1, 0, "new item");})ForEach(this.simpleList, (item: string) => {ChildItem5({ item: item });}, (item: string, index: number) => index.toString())}.height('100%').width('100%')}
}@Component
struct ChildItem5 {@Prop item: string;build() {Text(this.item).fontSize(20).fontWeight(FontWeight.Medium).fontColor($r('app.color.c_black'))}
}
渲染结果非预期运行效果图
ForEach在首次渲染时,创建的键值依次为"0"、“1”、“2”。
插入新项后,数据源simpleList变为[‘one’, ‘new item’, ‘two’, ‘three’],框架监听到@State装饰的数据源长度变化触发ForEach重新渲染。
ForEach依次遍历新数据源,遍历数据项"one"时生成键值"0",存在相同键值,因此不创建新组件。继续遍历数据项"new item"时生成键值"1",存在相同键值,因此不创建新组件。继续遍历数据项"two"生成键值"2",存在相同键值,因此不创建新组件。最后遍历数据项"three"时生成键值"3",不存在相同键值,创建内容为"three"的新组件并渲染。
从以上可以看出,当最终键值生成规则包含index时,期望的界面渲染结果为[‘one’, ‘new item’, ‘two’, ‘three’],而实际的渲染结果为[‘one’, ‘two’, ‘three’, ‘three’],渲染结果不符合开发者预期。因此,开发者在使用ForEach时应尽量避免最终键值生成规则中包含index。
4.8 渲染性能降低
在本示例中,ForEach的第三个参数KeyGenerator函数处于缺省状态。根据上述键值生成规则,此例使用框架默认的键值生成规则,即最终键值为字符串index + ‘__’ + JSON.stringify(item)。当点击“在第1项后插入新项”文本组件后,ForEach将需要为第2个数组项以及其后的所有项重新创建组件。
@Entry
@Component
struct TestForEach6 {@State simpleList: Array<string> = ['one', 'two', 'three'];build() {Column() {Button('在第1项后插入新项').fontSize(20).fontWeight(FontWeight.Medium).type(ButtonType.Capsule).onClick(() => {this.simpleList.splice(1, 0, "new item");console.log(`[onClick]: simpleList is ${JSON.stringify(this.simpleList)}`);})ForEach(this.simpleList, (item: string) => {ChildItem6({ item: item });})}// .justifyContent(FlexAlign.Center).height('100%').width('100%')}
}@Component
struct ChildItem6 {@Prop item: string;aboutToAppear(): void {console.log(`ChildItem6 [aboutToAppear]: item is ${this.item}`);}build() {Text(this.item).fontSize(20).fontWeight(FontWeight.Medium).fontColor($r('app.color.c_black'))}
}
渲染性能降低案例运行效果图
插入新项后,ForEach为new item、 two、 three三个数组项创建了对应的组件ChildItem,并执行了组件的aboutToAppear()生命周期函数。这是因为:
- 在ForEach首次渲染时,创建的键值依次为0__one、1__two、2__three。
- 插入新项后,数据源simpleList变为[‘one’, ‘new item’, ‘two’, ‘three’],ArkUI框架监听到@State装饰的数据源长度变化触发ForEach重新渲染。
- ForEach依次遍历新数据源,遍历数据项one时生成键值0__one,键值已存在,因此不创建新组件。继续遍历数据项new item时生成键值1__new item,不存在相同键值,创建内容为new item的新组件并渲染。继续遍历数据项two生成键值2__two,不存在相同键值,创建内容为two的新组件并渲染。最后遍历数据项three时生成键值3__three,不存在相同键值,创建内容为three的新组件并渲染。
尽管此示例中界面渲染的结果符合预期,但每次插入一条新数组项时,ForEach都会为从该数组项起后面的所有数组项全部重新创建组件。当数据源数据量较大或组件结构复杂时,由于组件无法得到复用,将导致性能体验不佳。因此,除非必要,否则不推荐将第三个参数KeyGenerator函数处于缺省状态,以及在键值生成规则中包含数据项索引index。
正确渲染并保证效率的ForEach写法是:
ForEach(this.simpleList, (item: string) => {ChildItem({ item: item })
}, (item: string) => item) // 需要保证key唯一
提供了第三个参数KeyGenerator,在这个例子中,对数据源的不同数据项生成不同的key,并且对同一个数据项每次生成相同的key
相关文章:

HarmonyOS:ForEach:循环渲染
一、前言 ForEach接口基于数组类型数据来进行循环渲染,需要与容器组件配合使用,且接口返回的组件应当是允许包含在ForEach父容器组件中的子组件。例如,ListItem组件要求ForEach的父容器组件必须为List组件。 API参数说明见:ForEa…...

Python3 【函数】项目实战:5 个新颖的学习案例
Python3 【函数】项目实战:5 个新颖的学习案例 本文包含5编程学习案例,具体项目如下: 简易聊天机器人待办事项提醒器密码生成器简易文本分析工具简易文件加密解密工具 项目 1:简易聊天机器人 功能描述: 实现一个简易…...

XSS 漏洞全面解析:原理、危害与防范
目录 前言编辑 漏洞原理 XSS 漏洞的危害 检测 XSS 漏洞的方法 防范 XSS 漏洞的措施 前言 在网络安全的复杂版图中,XSS 漏洞,即跨站脚本攻击(Cross - Site Scripting),是一类极为普遍且威胁巨大的安全隐患。随着互…...

从 GShard 到 DeepSeek-V3:回顾 MoE 大模型负载均衡策略演进
作者:小天狼星不来客 原文:https://zhuanlan.zhihu.com/p/19117825360 故事要从 GShard 说起——当时,人们意识到拥有数十亿甚至数万亿参数的模型可以通过某种形式的“稀疏化(sparsified)”来在保持高精度的同时加速训…...

【回溯+剪枝】回溯算法的概念 全排列问题
文章目录 46. 全排列Ⅰ. 什么是回溯算法❓❓❓Ⅱ. 回溯算法的应用1、组合问题2、排列问题3、子集问题 Ⅲ. 解题思路:回溯 剪枝 46. 全排列 46. 全排列 给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。 …...

Flutter解决macbook M芯片Android Studio中不显示IOS真机的问题
下载了最新的Android Studio LadyBug 下载了最新的xcode16.2 结果,只有安卓真机才在Android studio显示, IOS真机只在xcode显示 IOS真机不在android studio显示。 解决方法是: 在终端运行如下命令: sudo xcode-select -s /Applic…...

自签证书的dockerfile中from命令无法拉取镜像而docker的pull命令能拉取镜像
问题现象: docker pull images拉取镜像正常 dockerfile中的from命令拉取镜像就会报出证书错误。报错信息如下: [bjxtbwj-kvm-test-jenkins-6-243 ceshi_dockerfile]$ docker build . [] Building 0.4s (3/3) FINISHED …...

【MySQL】--- 复合查询 内外连接
Welcome to 9ilks Code World (๑•́ ₃ •̀๑) 个人主页: 9ilk (๑•́ ₃ •̀๑) 文章专栏: MySQL 🏠 基本查询回顾 假设有以下表结构: 查询工资高于500或岗位为MANAGER的雇员,同时还要满足他们的姓名首字母为…...

QT TLS initialization failed
qt使用QNetworkAccessManager下载文件(给出的链接可以在浏览器里面下载文件),下载失败, 提示“TLS initialization failed”通常是由于Qt在使用HTTPS进行文件下载时,未能正确初始化TLS(安全传输层协议&…...

系统学英语 — 句法 — 复合句
目录 文章目录 目录复合句型主语从句宾语从句表语从句定语从句状语从句同位语从句 复合句型 复合句型,即:从句。在英语中,除了谓语之外的所有句子成分都可以使用从句来充当。 主语从句 充当主语的句子,通常位于谓语之前&#x…...

指针的介绍2前
1.数组名的理解 #define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h>int main() {int arr[] { 1,2,3,4,5,6,7,8,9 };printf("&arr[0] %p\n", &arr[0]);printf("arr %p\n", arr);return 0; } 观察得到,数组名就是数组首…...

16.Word:石油化工设备技术❗【28】
目录 题目 NO1.2 NO3 NO4 题目 NO1.2 F12:另存为将“Word素材.docx”文件另存为“Word. docx”(“docx”为文件扩展名) 光标来到表格上方→插入→形状→新建画布→单击选中→格式→高度/宽度(格式→大小对话框→取消勾选✔锁定…...

Python-基础环境(01) 虚拟环境,Python 基础环境之虚拟环境,一篇文章助你完全搞懂!
Python的虚拟环境是一种工具,它能够创建一个隔离的独立Python环境。每个虚拟环境都有自己独立的Python解释器和安装的包,不会与其他虚拟环境或系统的全局Python环境发生冲突。虚拟环境特别适用于以下情况: 项目隔离:不同的项目可…...

Dest1ny漏洞库:用友 U8-CRM 系统 ajaxgetborrowdata.php 存在 SQL 注入漏洞
用友U8-CRM系统ajaxgetborrowdata.php存在SQL注入漏洞,文件多个方法存在SQL注入漏洞,未经身份验证的攻击者通过漏洞执行任意SQL语句,调用xp_cmdshell写入后门文件,执行任意代码,从而获取到服务器权限。 hunter app.n…...

java.sql.Date 弃用分析与替代方案
引言 java.sql.Date 是 Java 标准库中的一个类,它继承自 java.util.Date,主要用于在 Java 应用程序与数据库之间进行日期数据的传输。然而,随着 Java 语言的发展,java.sql.Date 以及其父类 java.util.Date 逐渐被认为存在设计缺陷…...

HarmonyOS:状态管理最佳实践
一、概述 在声明式UI编程范式中,UI是应用程序状态的函数,应用程序状态的修改会更新相应的UI界面。ArkUI采用了MVVM模式,其中ViewModel将数据与视图绑定在一起,更新数据的时候直接更新视图。如下图所示: ArkUI的MVVM模式…...

如何提高新产品研发效率
优化研发流程、采用先进工具、提升团队协作、持续学习与改进,是提高新产品研发效率的关键。其中,优化研发流程尤为重要。通过简化流程,减少不必要的环节和复杂性,企业可以显著提升研发效率。例如,采用自动化测试工具和…...

MongoDB平替数据库对比
背景 项目一直是与实时在线监测相关,特点数据量大,读写操作大,所以选用的是MongoDB。但按趋势来讲,需要有一款国产数据库可替代,实现信创要求。选型对比如下 1. IoTDB 这款是由清华大学主导的开源时序数据库&#x…...

JavaScript系列(46)-- WebGL图形编程详解
JavaScript WebGL图形编程详解 🎨 今天,让我们深入探讨JavaScript的WebGL图形编程。WebGL是一种基于OpenGL ES的JavaScript API,它允许我们在浏览器中渲染高性能的2D和3D图形。 WebGL基础概念 🌟 💡 小知识ÿ…...

YOLO目标检测4
一. 参考资料 《YOLO目标检测》 by 杨建华博士 本篇文章的主要内容来自于这本书,只是作为学习记录进行分享。 二. 环境搭建 (1) ubuntu20.04 anaconda安装方法 (2) 搭建yolo训练环境 # 首先,我们建议使用Anaconda来创建一个conda的虚拟环境 conda cre…...

十三先天记
没有一刻,只有当下在我心里。我像星星之间的空间一样空虚。他们是我看到的第一件事,我知道的第一件事。 在接下来的时间里,我意识到我是谁,我是谁。我知道星星在我上方,星球的固体金属体在我脚下。这个支持我的世界是泰…...

【论文阅读笔记】“万字”关于深度学习的图像和视频阴影检测、去除和生成的综述笔记 | 2024.9.3
论文“Unveiling Deep Shadows: A Survey on Image and Video Shadow Detection, Removal, and Generation in the Era of Deep Learning”内容包含第1节简介、第2-5节分别对阴影检测、实例阴影检测、阴影去除和阴影生成进行了全面的综述。第6节深入讨论了阴影分析࿰…...

Android AOP:aspectjx
加入引用 在整个项目的 build.gradle 中,添加 classpath "com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.10" 可以看到测试demo的 gradle 版本是很低的。 基于 github 上的文档,可以看到原版只支持到 gradle 4.4 。后续需要使…...

前端【11】HTML+CSS+jQUery实战项目--实现一个简单的todolist
前端【8】HTMLCSSjavascript实战项目----实现一个简单的待办事项列表 (To-Do List)-CSDN博客 学过jQUery可以极大简化js代码的编写,基于之前实现的todolist小demo,了解如何使用 jQuery 来实现常见的动态交互功能。 修改后的js代码 关键点解析 动态添加…...

2025课题推荐——USBL与DVL数据融合的实时定位系统
准确的定位技术是现代海洋探测、海洋工程和水下机器人操作的基础。超短基线(USBL)和多普勒速度计(DVL)是常用的水下定位技术,但单一技术难以应对复杂环境。因此,USBL与DVL的数据融合以构建实时定位系统&…...

滑动窗口详解:解决无重复字符的最长子串问题
滑动窗口详解:解决无重复字符的最长子串问题 在算法面试中,“无重复字符的最长子串”问题是一个经典题目,不仅考察基础数据结构的运用,还能够反映你的逻辑思维能力。而在解决这个问题时,滑动窗口(Sliding …...

第05章 11 动量剖面可视化代码一则
在计算流体力学(CFD)中,动量剖面(Momentum Profiles)通常用于描述流体在流动方向上的动量分布。在 VTK 中,可以通过读取速度场数据,并计算和展示动量剖面来可视化呈现速度场信息。 示例代码 以…...

MySQL的复制
一、概述 1.复制解决的问题是让一台服务器的数据与其他服务器保持同步,即主库的数据可以同步到多台备库上,备库也可以配置成另外一台服务器的主库。这种操作一般不会增加主库的开销,主要是启用二进制日志带来的开销。 2.两种复制方式…...

Cpp::IO流(37)
文章目录 前言一、C语言的输入与输出二、什么是流?三、C IO流C标准IO流C文件IO流以写方式打开文件以读方式打开文件 四、stringstream的简单介绍总结 前言 芜湖,要结束喽! 一、C语言的输入与输出 C语言中我们用到的最频繁的输入输出方式就是 …...

基于OpenCV实现的答题卡自动判卷系统
一、图像预处理 🌄 二、查找答题卡轮廓 📏 三、透视变换 🔄 四、判卷与评分 🎯 五、主函数 六、完整代码+测试图像集 总结 🌟 在这篇博客中,我将分享如何使用Python结合OpenCV库开发一个答题卡自动判卷系统。这个系统能够自动从扫描的答题卡中提取信…...