HarmonyOS:删除多层ForEach循环渲染的复杂数据而导致的一系列问题

目录

1.页面效果及需求

2.遇到问题时的初始代码及问题

代码

问题

3.状态变化不能深层监听?

解答

4.使用了@ObjectLink装饰器后为什么数据仍然无法被监听?

Demo

结论

代码修改

5.子组件中定义一个箭头函数,在父组件中通过this.传入方法存在this的指向问题?

原因

解决

6.数组在删除item后,index发生混乱?

7.在更新了forEach的源数据后,为什么页面没有被重新渲染?

原因

值类型变量与引用类型变量

数据的深拷贝与浅拷贝

什么事深拷贝与浅拷贝?

如何实现浅拷贝与深拷贝

解决方案


前言:

小菜鸟又记起了CSDN,还是老生常谈,先介绍一下为什么写这篇文章。最近在学习鸿蒙ArkTs语法,略知皮毛后仿造淘宝想写一个应用商城,前方一切很顺利,在写到购物车中的商品删除的时候,遇到了一点问题,oh,不对,应该是通过一个小问题,引出了一系列的小问题,因为这些小问题让我的进度降低,en.....开玩笑,因为它引出了很多小的知识点,在一次又一次的修正中,通过一个小问题,不断地去发散思维,寻求解决办法的这个过程才是我真正想写这篇博客的原因。

(ps这玩意儿是我解决完问题后写的,文章中出现的代码,我都是通过回忆写的,可能会有一点误差,但重要的是解决这个问题的思路嘛)

好了,正文开始,先列一下,问题的大概顺序大致如下:

  • @State装饰器修饰变量的状态变化只能监听第一层?
  • 使用了@ObjectLink装饰器后为什么数据仍然无法被监听?
  • 子组件中定义一个箭头函数,在父组件中通过this.传入方法存在this的指向问题?
  • 数组在删除item后,index发生混乱?
  • 在更新了forEach的源数据后,为什么页面没有被重新渲染?
  • 数据的深拷贝与浅拷贝

1.页面效果及需求

1.点击‘-’图标,对商品数量进行消减,点击‘+’图标,增加商品数量

2.如果商品数量为0,则从购物车中删除这个商品

3.如果这个商家下的商品数量为0,则删除商家这一整项

2.遇到问题时的初始代码及问题

代码

Model:

@Observed
export class CarGoodsQuantity{//商品信息goods:GoodsInformation//商品被添加到购物车的数量addtoCarQuantity : numberconstructor(goods:GoodsInformation,addtoCarQuantity : number) {this.goods=goodsthis.addtoCarQuantity=addtoCarQuantity}
}@Observed
export class CarDataModel{//商家名称businessName:string//这个商家被添加到购物车的商品列表goods: CarGoodsQuantity[]constructor(businessName:string,goods:CarGoodsQuantity[]) {this.businessName=businessNamethis.goods=goods}
}

页面:ShoppingCartPage

@Component
@Preview
export struct ShoppingCartPage{@State currentGoodsList:CarDataModel[]=InformationModel.getGoodsCarModel() //购物车中每个商家及旗下商品列表@State isManaged:boolean = false //是否是管理页面@State checkoutMoney:number=0 //结算总额build(){column(){...List({space:6}){//购物车列表ForEach(this.currentGoodsList,(itemGroup:CarDataModel,parentIndex:number)=>{ListItemGroup({header:this.groupItemTitleBuilder(itemGroup.businessName,parentIndex)}){ForEach(itemGroup.goods,(item:CarGoodsQuantity,index:number)=>{ListItem(){this.GoodsItemBuilder(item,parentIndex,index)}})}.backgroundColor('#fff6f4f4')})}.width('100%').layoutWeight(1)}
}//购物车中,每个商品item@BuilderGoodsItemBuilder(item:CarGoodsQuantity,parentIndex:number,index:number){Row(){Checkbox({name:'goods'+index,group:'checkboxGroup'+parentIndex}).selectedColor(Color.Orange).onChange((isChecked:boolean)=>{if(isChecked){this.checkoutMoney += item.goods.price*item.addtoCarQuantityconsole.log('kkk',item.goods.goodsDiscription+'商品被选中')}else {this.checkoutMoney -= item.goods.price*item.addtoCarQuantityconsole.log('kkk','未选中')}})Image(item.goods.image[0]).width(100).height(100).borderRadius(5).margin({left:10})Column(){Row(){Text(item.goods.goodsDiscription).fontSize(14).maxLines(1).textOverflow({overflow:TextOverflow.Ellipsis}).width('80%')Text('X '+item.addtoCarQuantity).fontSize(14).fontColor(Color.Gray)}.width('100%').justifyContent(FlexAlign.SpaceBetween)Row(){Text('¥'+item.goods.price).fontSize(18).fontColor(Color.Orange)if(this.isManaged){Text('-').width(24).height(24).borderRadius(12).textAlign(TextAlign.Center).backgroundColor(Color.Red).fontColor(Color.White).onClick(()=>{if(item.addtoCarQuantity>0){item.addtoCarQuantity-- console.log('kkk','这个商品数量-1:'item.addtoCarQuantity)} else{}})Text('+').width(24).height(24).margin({left:20}).borderRadius(12).textAlign(TextAlign.Center).backgroundColor(Color.Red).fontColor(Color.White).onClick(()=>{item.addtoCarQuantity++console.log('kkk','这个商品数量+1 :'+item.addtoCarQuantity)})}}.width('100%').justifyContent(FlexAlign.SpaceBetween)}.margin({left:10}).height('100%').layoutWeight(1).justifyContent(FlexAlign.SpaceEvenly)}.width('100%').height(120).padding({top:10,bottom:10}).alignItems(VerticalAlign.Center)}}

问题

通过log可以看出,商品的数量是减少了,但是页面上显示的商品数量没有发生变化

3.状态变化不能深层监听?

解答

通过官方文档的这一段可以看出,由@State装饰的变量,它只能够监视第一层,如我们上方的currentGoodsList:CarDateModel[]数组,它只能监听其中CarDataModel对象的变化,但是无法监听到CarDataModel对象中属性的变化

@State装饰的变量只能监视第一层,通过查阅官方文档可以知道@ObjectLink和@Observed类装饰器用于在涉及嵌套对象或数组的场景中进行双向数据同步

然后我修改了我的代码,通过@ObjectLink去监听我们的数组,代码如下:

ShoppingCartPage:
@Component
@Preview
export struct ShoppingCartPage{@State currentGoodsList:CarDataModel[]=InformationModel.getGoodsCarModel() //购物车中每个商家及旗下商品列表
build(){column(){....List({space:6}){//购物车列表ForEach(this.currentGoodsList,(itemGroup:CarDataModel,parentIndex:number)=>{ListItemGroup({header:this.groupItemTitleBuilder(itemGroup.businessName,parentIndex)}){ForEach(itemGroup.goods,(item:CarGoodsQuantity,index:number)=>{ListItem(){// this.GoodsItemBuilder(item,parentIndex,index)GoodsCarItemComponent({carGoodsModel:itemGroup,parentIndex:parentIndex,index:index,checkoutMoney:this.checkoutMoney,isManaged:this.isManaged,})}})}.backgroundColor('#fff6f4f4')})}.width('100%').layoutWeight(1)}
}}
GoodsCarItemComponent
@Component
export struct GoodsCarItemComponent{@ObjectLink carGoodsModel:CarDataModelprivate parentIndex:number=0private index:number=0@Link checkoutMoney:number//是否为管理界面@Prop isManaged:booleanbuild(){...Column(){Row(){Text(this.carGoodsModel.goods[this.index].goods.goodsDiscription).fontSize(14).maxLines(1).textOverflow({overflow:TextOverflow.Ellipsis}).width('80%')Text('X '+this.carGoodsModel.goods[this.index].addtoCarQuantity).fontSize(14).fontColor(Color.Gray)}.width('100%').justifyContent(FlexAlign.SpaceBetween)Row(){Text('¥'+this.carGoodsModel.goods[this.index].goods.price).fontSize(18).fontColor(Color.Orange)if(this.isManaged){Text('-').width(24).height(24).borderRadius(12).textAlign(TextAlign.Center).backgroundColor(Color.Red).fontColor(Color.White).onClick(()=>{if(this.carGoodsModel.goods[this.index].addtoCarQuantity>=1){this.carGoodsModel.goods[this.index].addtoCarQuantity--console.log('kkk','这个商品数量-1: '+this.carGoodsModel.goods[this.index].addtoCarQuantity)}})Text('+').width(24).height(24).margin({left:20}).borderRadius(12).textAlign(TextAlign.Center).backgroundColor(Color.Red).fontColor(Color.White).onClick(()=>{this.carGoodsModel.goods[this.index].addtoCarQuantity++console.log('kkk','这个商品数量+1 :')})}}.width('100%').justifyContent(FlexAlign.SpaceBetween)}.margin({left:10}).height('100%').layoutWeight(1).justifyContent(FlexAlign.SpaceEvenly)}.width('100%').height(120).padding({top:10,bottom:10}).alignItems(VerticalAlign.Center)}
}

操作结果:通过log可以看出这个购物车商品的数量确实增加以及减少了,但是,状态管理器似乎没有监听到变化,页面中显示的商品数量没有变化
 

Tips:

@ObjectLink装饰的变量不能被赋值,如果要使用赋值操作,请使用@Prop。

  • @Prop装饰的变量和数据源的关系是是单向同步,@Prop装饰的变量在本地拷贝了数据源,所以它允许本地更改,如果父组件中的数据源有更新,@Prop装饰的变量本地的修改将被覆盖;
  • @ObjectLink装饰的变量和数据源的关系是双向同步,@ObjectLink装饰的变量相当于指向数据源的指针。如果一旦发生@ObjectLink装饰的变量的赋值,则同步链将被打断。

那么就引入了下一个问题

4.使用了@ObjectLink装饰器后为什么数据仍然无法被监听?

再次回看我的的数据结构:

class CarDataModel{businessName:stringgoods: CarGoodsQuantity[]}
class CarGoodsQuantity{goods:GoodsInformationaddtoCarQuantity : number}

并且通过在子组件中,修改carDataModel的值,结合页面变化来看发现

carDataModel.businessName以及carDataModel.goods的变化可以被监听到,再往下就监听不到了

为了防止是因为我项目中有些地方的错误导致得到错误的结论,所以新写一个demo来观察

Demo

数据

@Observed
export class ModelA{name:stringinformation : ModelB[]constructor(name:string,information:ModelB[]) {this.name=namethis.information=information}
}@Observed
export class ModelB{subject:stringscore: numberconstructor(subject:string,score: number) {this.subject=subjectthis.score=score}
}

页面

import { ModelA, ModelB } from '../Model/ModelA';@Entry
@Component
struct Index {@State modelList:ModelA[]=[new ModelA('小王',[new ModelB('英语',90),new ModelB('数学',98)])]build() {Column() {ForEach(this.modelList,(item:ModelA,index:number)=>{ViewSon({modelA:item}).margin({bottom:30})})}.height('100%').width('100%')}
}@Component
struct ViewSon{@ObjectLink modelA:ModelAbuild() {Column({space:30}){Text('修改名称 : ' + this.modelA.name).onClick(()=>{this.modelA.name='狗狗'console.log('kkk','修改名称后:' +this.modelA.name)}).backgroundColor('#f3f3')Text('修改'+this.modelA.information[0].subject+'成绩 :' + this.modelA.information[0].score ).onClick(()=>{this.modelA.information[0].score=100console.log('kkk','修改成绩后: ' + this.modelA.information[0].score)}).backgroundColor('#f3f3')Text('删除英语成绩 ' + JSON.stringify(this.modelA.information)).onClick(()=>{this.modelA.information.splice(0,1)console.log('kkk','删除英语学科成绩后:' + JSON.stringify(this.modelA.information))}).backgroundColor('#f3f3')Text('给学科数据重新赋值 :' +JSON.stringify(this.modelA.information)).onClick(()=>{this.modelA.information=[new ModelB('政治',80)]console.log('kkk','给学科数据重新赋值后 :'+JSON.stringify(this.modelA.information))}).backgroundColor('#f3f3')}}
}

页面展示:

依次点击前3个Text组件后,页面显示:

可以看出,只有第一个Text组件中的内容发生了变化

再点击第四个Text组件后页面显示:

结论

由上可以得出结论,@ObjectLink-@Observed装饰器,@ObjectLink装饰了数组item, 在监听数组时,它只能监听到数组item中第一层属性值的变化,更深层次的也是监听不到的

那如果我们需要监听

@State modelList:ModelA[]=[new ModelA('小王',[new ModelB('英语',90),new ModelB('数学',98)])]

中ModelB对象的数据修改怎么办呢?

可以通过两层forEach循环,在子组件中用@ObjectLink组件装饰 ModelB对象即可

代码修改

通过以上结论,我们修改一下我们的代码

当时为什么在子组件中传入CarDataModel对象,而不是CarGoodsQuantity对象,是因为当商品的数量减为0,需要删除这个商品对象,所以当时传入了CarDataModel对象

但是现在给子组件传入CarDataModel对象,在CarGoodsQuantity对象的属性值发生改变的时候,无法被监听到,所以还是给子组件传入CarGoodsQuantity对象,然后再定义一个箭头函数,将删除商品对象的处理交给父组件进行处理

export struct ShoppingCartPage{@State currentGoodsList:CarDataModel[]=InformationModel.getGoodsCarModel() //购物车中每个商家及旗下商品列表deleteGoodsItem(parentIndex:number,index:number){//删除购物车中的这个商品this.currentGoodsList[parentIndex].goods.splice(index,1)console.log('kkk','删除此商品后:'+JSON.stringify(this.currentGoodsList[parentIndex].goods))//如果这个商家下的商品没有了,则删除这一个商家if(this.currentGoodsList[parentIndex].goods.length==0){this.currentGoodsList.splice(parentIndex,1)console.log('kkk','删除商家后:'+JSON.stringify(this.currentGoodsList[parentIndex]))}}build(){
....
List({space:6}){//购物车列表ForEach(this.currentGoodsList,(itemGroup:CarDataModel,parentIndex:number)=>{ListItemGroup({header:this.groupItemTitleBuilder(itemGroup.businessName,parentIndex)}){ForEach(itemGroup.goods,(item:CarGoodsQuantity,index:number)=>{ListItem(){// this.GoodsItemBuilder(item,parentIndex,index)GoodsCarItemComponent({item:item,parentIndex:parentIndex,index:index,checkoutMoney:this.checkoutMoney,isManaged:this.isManaged,deleteGoodsItem: this.deleteGoodsItem}})}})}.backgroundColor('#fff6f4f4')})}.width('100%').layoutWeight(1)
}
}

GoodsCarItemComponent
@Component
export struct GoodsCarItemComponent{@ObjectLink item:CarGoodsQuantityprivate parentIndex:number=0private index:number=0@Link checkoutMoney:number//是否为管理界面@Prop isManaged:booleandeleteGoodsItem=(parentIndex:number,index:number)=>{}build(){....Column(){Row(){Text(this.item.goods.goodsDiscription).fontSize(14).maxLines(1).textOverflow({overflow:TextOverflow.Ellipsis}).width('80%')Text('X '+this.item.addtoCarQuantity).fontSize(14).fontColor(Color.Gray)}.width('100%').justifyContent(FlexAlign.SpaceBetween)Row(){Text('¥'+this.item.goods.price).fontSize(18).fontColor(Color.Orange)if(this.isManaged){Text('-').width(24).height(24).borderRadius(12).textAlign(TextAlign.Center).backgroundColor(Color.Red).fontColor(Color.White).onClick(()=>{if(this.item.addtoCarQuantity>=1){this.item.addtoCarQuantity--console.log('kkk','这个商品数量-1: '+this.item.addtoCarQuantity)if(this.item.addtoCarQuantity==0){//在购物车中删除这个商品this.deleteGoodsItem(this.parentIndex,this.index)}}})Text('+').width(24).height(24).margin({left:20}).borderRadius(12).textAlign(TextAlign.Center).backgroundColor(Color.Red).fontColor(Color.White).onClick(()=>{this.item.addtoCarQuantity++console.log('kkk','这个商品数量+1 :')})}}.width('100%').justifyContent(FlexAlign.SpaceBetween)}.margin({left:10}).height('100%').layoutWeight(1).justifyContent(FlexAlign.SpaceEvenly)
}

上面的代码可以看出,我们在给子组件传值时,通过deleteGoodsItem:this.deleteGoodsItem的方式给子组件传递一个箭头函数,这样的写法,会得到如下报错:

引出了下一个问题

5.子组件中定义一个箭头函数,在父组件中通过this.传入方法存在this的指向问题?

原因

this一般指向调用者

deleteGoodsItem()这个方法是被子组件所调用,所以deleteGoodsItem : this.deleteGoodsItem中这个this指向的是子组件,那在deleteGoodsItem()里通过this.调用父组件的属性,会报错undefined

解决

所以在给子组件的箭头函数传值时的写法应修改为以下代码:

GoodsCarItemComponent({item:item,parentIndex:parentIndex,index:index,checkoutMoney:this.checkoutMoney,isManaged:this.isManaged,deleteGoodsItem:(parentIndex:number,index:number)=>{this.deleteGoodsItem(parentIndex,index)}})}})

这时的this指向父组件。

接着我们再次重新运行,在点击删除商品的时候,发现了新的问题,有的商品删除了,有的商品无法删除

6.数组在删除item后,index发生混乱?

再次看一下我们deleteGoodsItem()方法

deleteGoodsItem(parentIndex:number,index:number){//删除购物车中的这个商品this.currentGoodsList[parentIndex].goods.splice(index,1)console.log('kkk','删除此商品后:'+JSON.stringify(this.currentGoodsList[parentIndex].goods))//如果这个商家下的商品没有了,则删除这一个商家if(this.currentGoodsList[parentIndex].goods.length==0){this.currentGoodsList.splice(parentIndex,1)console.log('kkk','删除商家后:'+JSON.stringify(this.currentGoodsList[parentIndex]))}}

数组删除item后,index发生混乱的原因还是比较简单清晰的

无非就是有一个数组ListA=[item0,item1,item2,item4], 此时我们想删除它的第二项和第三项

理想状态下:

删第二项item1:listA.splice(1,1)

删第三项item2:listA.splice(2,1)

但事实是,删除第二项item1后,item2的下标变为1,此时如果再用listA.splice(2,1)进行删除的话,只会删除item3

这样该如何解决呢?

如果是循环删除的话,不妨试一下倒序法,就是从后往前删,这样不会影响item的下标,自然也就不会有index混乱的问题

但是循环删除的实际应用还是比较少的,像现在这样,在数组中删除任意一项,又怎样保证数组的index不发生混乱呢?

在删除item后让List组件重新渲染,使得每个item的下标更新为最新,这样再进行删除的时候,也不会发生index错误的情况

如何让List组件重新渲染呢?

我们知道,当List的源数据监听到数据变化时List会重新渲染

然后我将deleteGoodsItem()方法进行了如下修改:

deleteGoodsItem(parentIndex:number,index:number){//删除购物车中的这个商品let news=this.currentGoodsList[parentIndex]news.goods.splice(index,1)this.currentGoodsList[parentIndex]=newsconsole.log('kkk','删除此商品后:'+JSON.stringify(this.currentGoodsList[parentIndex].goods))//如果这个商家下的商品没有了,则删除这一个商家if(this.currentGoodsList[parentIndex].goods.length==0){this.currentGoodsList.splice(parentIndex,1)console.log('kkk','删除商家后:'+JSON.stringify(this.currentGoodsList[parentIndex]))}}

我们通过@State装饰器可以知道,它装饰的currentGoodsList中的item变化是可以被监听道德,所以先将需要修改的currentGoodsList[parentIndex]赋值给news,然后对news进行数据修改后,重新赋值给this.currentGoodsList[parentIndex],这样就达到了数组中item的变化,这样应该可以被监听到了吧?

我们运行一下,看下结果:

我们删除‘手工粗棒针毛线帽子’这一项的时候,发现在页面中当它的数量变为0的时候,这一项并没有被删除,但是在我们打出的log可以看到,这一项是被删除了的

所以我们产生了新的疑问:

7.在更新了forEach的源数据后,为什么页面没有被重新渲染?

原因

@State只是监听数组的地址值,所以只有地址值改变时才会重新渲染页面

也就是如下方法,数组的地址值没有发生变化,所以没有监听到,也就不会重新渲染页面

let news=this.currentGoodsList[parentIndex]
news.goods.splice(index,1)
this.currentGoodsList[parentIndex]=news

也就是news和this.currentGoodsList[parentIndex]指的是同一个对象

我们可以通过一个demo来看一下

@State ListA:Array<number> =[1,2,3,4]build() {Column() {Text(this.ListA.toString()).fontSize(20).onClick(()=>{let ListB = this.ListAListB.splice(1,1,100)console.log('kkk','ListA: '+this.ListA.toString())console.log('kkk','ListB: '+ListB.toString())})}}

可以看出通过=号被赋值的ListB与ListA其实是同一个对象,指向同一个内存地址,在修改ListB的时候,ListA也会被同样修改

值类型变量与引用类型变量

值类型变量(如int、float、struct等)在赋值时,会复制变量的完整内容到新的变量中。这意味着两个变量在内存中存储的是不同的数据副本,对其中一个变量的修改不会影响到另一个。

引用类型变量(如class、interface等)在赋值时,复制的是变量的引用(即内存地址),而非对象本身。因此,两个引用类型变量可能指向同一个对象实例。对通过任一变量对对象所做的修改都会反映到所有指向该对象的变量上。

所以以下通过=号赋值的news没有任何意义,因为news和this.currentGoodsList[parentIndex]的内存地址是一样的

let news=this.currentGoodsList[parentIndex]
news.goods.splice(index,1)
this.currentGoodsList[parentIndex]=news

那如何赋值给一个新的数组,让其的内存地址是不同的呢?

接下来引入最后一个问题

数据的深拷贝与浅拷贝

官方文档给出了详细的解释:

什么事深拷贝与浅拷贝?

浅拷贝:

  • 在堆中为新对象重新创建内存空间。
  • 基本数据类型的值被直接拷贝,互不影响。
  • 引用类型的内存地址被拷贝,因此拷贝前后的对象共享相同的引用类型数据。
  • 修改共享引用类型的数据会影响所有指向该数据的对象。

深拷贝:

  • 完整地从内存中拷贝一个对象,包括其所有属性和嵌套的对象。
  • 在堆内存中开辟新的区域来存储新对象,确保新对象和原始对象完全独立。
  • 修改新对象不会影响原始对象,反之亦然。

如何实现浅拷贝与深拷贝

文档中心icon-default.png?t=O83Ahttps://developer.huawei.com/consumer/cn/doc/harmonyos-faqs-V5/faqs-arkts-94-V5

解决方案

方法1:
//先复制一个临时的列表
let tempList: Array<A> = [...this.dataList]
// 修改tempList数组中的值
tempList[index].key = value;
//将tempList赋值给数组, 页面即可重新渲染
this.List = [...tempList]
方法2:
this.List[index].key = value; //修改数据
this.List = this.List.map(item=>item); //map方法会返回一个新数组,故页面也会重新渲染

最后的deleteGoodsItem()修改为:

deleteGoodsItem(parentIndex:number,index:number){//删除购物车中的这个商品let news=[...this.currentGoodsList]news[parentIndex].goods.splice(index,1)this.currentGoodsList=newsconsole.log('kkk','删除此商品后:'+JSON.stringify(this.currentGoodsList[parentIndex].goods))//如果这个商家下的商品没有了,则删除这一个商家if(this.currentGoodsList[parentIndex].goods.length==0){this.currentGoodsList.splice(parentIndex,1)console.log('kkk','删除商家后:'+JSON.stringify(this.currentGoodsList[parentIndex]))}}

运行效果如下:

商品的数量为0时,商品被删除,商家下的商品为0时,商家这一项被删除。

结束语:

写到这里就结束啦,希望对看到这篇文章的你有所帮助~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/65860.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

leecode188.买卖股票的最佳时机IV

这道题目我在买卖股票III就已经得出规律了&#xff0c;具体可看买卖股票的最佳时机||| class Solution { public:int maxProfit(int k, vector<int>& prices) {int nprices.size();vector<vector<int>> dp(n,vector<int>(2*k1,0));for(int j1;j&l…

如何通过深度学习提升大分辨率图像预测准确率?

随着科技的不断进步&#xff0c;图像处理在各个领域的应用日益广泛&#xff0c;特别是在医疗影像、卫星遥感、自动驾驶、安防监控等领域中&#xff0c;大分辨率图像的使用已经成为了一项不可或缺的技术。然而&#xff0c;大分辨率图像带来了巨大的计算和存储压力&#xff0c;同…

探索电商数据:爬取不同平台商品信息的Python实践

在数字化时代&#xff0c;电商平台的商品信息成为了宝贵的数据资源。除了亚马逊&#xff0c;全球还有许多电商平台的商品信息值得爬取。本文将介绍几个值得关注的电商平台&#xff0c;并提供Python代码示例&#xff0c;展示如何爬取这些平台的商品信息。 1. 京东 (JD.com) 京…

数据结构与算法学习笔记----欧拉函数

数据结构与算法学习笔记----欧拉函数 author: 明月清了个风 first publish time: 2025.1.1 ps⭐️欧拉函数的定义及求法&#xff0c;第二题是在线性筛法的过程中维护欧拉函数。 欧拉函数 通常用符号 φ ( n ) \varphi(n) φ(n)表示&#xff0c;定义为小于或等于 n n n且与 n…

软件测试之非功能测试设计

非功能测试设计 非功能:除了软件功能测试&#xff0c;其他都是非功能测试。 1.兼容 2.易用 3.性能(专项) 4.安全(专项) Web浏览器 兼容:Chrome浏览器、Edge浏览器、Firefox浏览器、Safari苹果浏览器 易用:参考竞品&#xff0c;主观感受为主 总结 1.非功能测试范围 兼容性、…

【Spring Boot】SpringBoot自动装配-Import

目录 一、前言二、 定义三、使用说明 3.1 创建项目 3.1.1 导入依赖3.1.2 创建User类 3.2 测试导入Bean 3.2.1 修改启动类 3.3 测试导入配置类 3.3.1 创建UserConfig类3.3.2 修改启动类 3.4 测试导入ImportSelector 3.4.1 创建UseImportSelector类3.4.2 修改启动类3.4.3 启动测试…

异步爬虫之aiohttp的使用

在上一篇博客我们介绍了异步爬虫的基本原理和 asyncio 的基本用法&#xff0c;并且在最后简单提及了使用aiohttp 实现网页爬取的过程。本篇博客我们介绍一下 aiohttp 的常见用法。 基本介绍 前面介绍的 asyncio模块&#xff0c;其内部实现了对 TCP、UDP、SSL协议的异步操作&a…

操作系统课后题总复习

目录 一、第一章 1.1填空题 1.2单项选择题 1.3多项选择题 1.4判断题 1.5名词解释 1.6简答题 二、第二章 2.1填空题 2.2单项选择题 2.3 多项选择题 2.4判断题 2.5名词解释 2.6简答题 三、第三章 3.1填空题 3.2单项选择题 3.3多项选择题 3.4判断题 3.5名词解…

《探寻真正开源的大模型:开启AI创新新纪元》

《探寻真正开源的大模型&#xff1a;开启AI创新新纪元》 一、开源大模型崛起&#xff1a;AI 发展的新曙光二、开源大模型的 “庐山真面目”三、明星开源大模型闪耀登场&#xff08;一&#xff09;LLaMA 3&#xff1a;实力强劲的开源先锋&#xff08;二&#xff09;Phi-3&#x…

Debian-linux运维-ssh配置(兼容Jenkins插件的ssh连接公钥类型)

系统版本&#xff1a;Debian 12.5、11.1 1 生成密钥对 可以用云服务商控制台生成的密钥对&#xff0c;也可以自己在客户端或者服务器上生成&#xff0c; 已经有密钥对就可以跳过这步 用户默认密钥文件路径为 ~/.ssh/id_rsa&#xff0c;可以在交互中指定路径&#xff0c;也可…

基于服务器部署的综合视频安防系统的智慧快消开源了。

智慧快消视频监控平台是一款功能强大且简单易用的实时算法视频监控系统。它的愿景是最底层打通各大芯片厂商相互间的壁垒&#xff0c;省去繁琐重复的适配流程&#xff0c;实现芯片、算法、应用的全流程组合&#xff0c;从而大大减少企业级应用约95%的开发成本。国产化人工智能“…

小程序学习05——uniapp路由和菜单配置

目录 一、路由 二、如何管理页面及路由&#xff1f; 三、pages.json 页面路由 四、 tabBar 一、路由 路由&#xff1a;在前端&#xff0c;往往指代用不同地址请求不同页面&#xff0c;决定了用户如何在应用的不同页面之间导航。 菜单&#xff1a;对于每个路径&#xff08;…

【网络安全实验室】SQL注入实战详情

如果额头终将刻上皱纹&#xff0c;你只能做到&#xff0c;不让皱纹刻在你的心上 1.最简单的SQL注入 查看源代码&#xff0c;登录名为admin 最简单的SQL注入&#xff0c;登录名写入一个常规的注入语句&#xff1a; 密码随便填&#xff0c;验证码填正确的&#xff0c;点击登录…

使用 ThinkPHP 和 Vue.js 开发现代 Web 应用的指南

使用 ThinkPHP 和 Vue.js 开发现代 Web 应用的指南 在当今的 Web 开发中&#xff0c;前后端分离架构逐渐成为一种主流趋势。结合 ThinkPHP 和 Vue.js&#xff0c;可以高效地构建现代化的 Web 应用。本文将详细介绍如何使用这两种技术栈&#xff0c;从环境搭建到基本功能实现&a…

_使用CLion的Vcpkg安装SDL2,添加至CMakelists时报错,编译报错

语言&#xff1a;C20 编译器&#xff1a;gcc 14.2 摘要&#xff1a;初次使用Vcpkg添加SDL2&#xff0c;出现CMakelists找不到错误、编译缺失main错误、运行失败错误。 CMakelists缺失错误&#xff1a; 使用CLion的Vcpkg安装SDL2时&#xff0c;按照指示把对应代码添加至CMakel…

可解释性:走向透明与可信的人工智能

随着深度学习和机器学习技术的迅速发展&#xff0c;越来越多的行业和领域开始应用这些技术。然而&#xff0c;这些技术的“黑盒”特性也带来了不容忽视的挑战&#x1f3b2;。在许多任务中&#xff0c;尽管这些模型表现出色&#xff0c;取得了相当高的精度&#xff0c;但其决策过…

SQL Server导出和导入可选的数据库表和数据,以sql脚本形式

一、导出 1. 打开SQL Server Management Studio&#xff0c;在需要导出表的数据库上单击右键 → 任务 → 生成脚本 2. 在生成脚本的窗口中单击进入下一步 3. 如果只需要导出部分表&#xff0c;则选择第二项**“选择具体的数据库对象(Select specific database objects)”**&am…

uniapp——App下载文件,打开文档(一)

uniapp如何下载文件、打开文件 文章目录 uniapp如何下载文件、打开文件下载文件下载文件成功返回数据格式 打开文档处理 iOS 打开文件可能失败问题 相关API&#xff1a; uni.downloadFileuni.openDocument 注意&#xff1a; 只支持 GET 请求&#xff0c;需要 POST的&#xff…

Eclipse下载安装图文教程

一、下载Eclipse 1、打开 Eclipse官网 2、下载免安装版&#xff1b; 3、切换国内下载源 4、下载压缩包到本地&#xff1b; 5、下载完成后直接解压就可以使用了&#xff1b; 二、汉化 1、打开eclipse&#xff0c;点击 ‘Help’ → ‘Install new software…’ 2、点击A…

【开源免费】基于SpringBoot+Vue.JS音乐网站(JAVA毕业设计)

本文项目编号 T 109 &#xff0c;文末自助获取源码 \color{red}{T109&#xff0c;文末自助获取源码} T109&#xff0c;文末自助获取源码 目录 一、系统介绍二、数据库设计三、配套教程3.1 启动教程3.2 讲解视频3.3 二次开发教程 四、功能截图五、文案资料5.1 选题背景5.2 国内…