【Harmony】状态管理(V1)
一、概述
文章目录
- 一、概述
- 二、组件状态管理
- 1、@State
- 1.1、@State简介
- 1.2、@State简单示例
- 2、@Prop
- 2.1、@Prop简介
- 2.2、@Prop底层实现原理
- 2.3、@Prop简单示例
- 3、@Link
- 3.1、@Link简介
- 3.2、@Link底层实现原理
- 3.3、@Link简单示例
- 4、@Provide @Consume
- 4.1、@Provide @Consume简介
- 4.2、@Provide @Consume底层实现原理
- 4.3、常见应用场景
- 5、@Observed @ObjectLink
- 5.1、@Observed @ObjectLink简介
- 5.2、@Observed @ObjectLink底层实现原理
- 三、应用状态管理
- 1、LocalStorage
- 1.1、LocalStorage简介
- 1.2、LocalStorage相关原理
- 1.3、LocalStorage简单示例
- 2、AppStorage
- 2.1、AppStorage简介
- 2.2、AppStorage相关原理
- 2.3、AppStorage简单示例
- 3、PersistentStorage
- 3.1、PersistentStorage简介
- 3.2、PersistentStorage相关原理
- 3.3、PersistentStorage简单示例
- 4、Environment
- 4.1、Environment简介
- 4.2、Environment简单示例
- 四、其他状态管理
- 1、@Watch
- 1.1、@Watch简介
- 1.2、@Watch相关原理
- 1.3、@Watch简单示例
- 2、$$语法
- 3、@Track
- 3.1、@Track简介
- 3.2、@Track相关原理
- 3.3、@Track简单示例
- 4、自定义组件冻结功能
- 五、面试题回答模板(待完善)
- 1、简述@Link实现原理
- 2、简述@Prop实现原理
- 3、简述@Prop和@Link的区别
- 4、简述@Provide @Consume和@Link的区别
组件状态管理:@State@Prop@Link@Provide @Consume@Observed @ObjectLink 受限 对象、数组
应用状态管理:LocalStorageAppStoragePersistentStorage@StorageProp @StorageLink
官方文档地址:状态管理(V1)
二、组件状态管理
1、@State
1.1、@State简介
@State装饰的变量为状态变量,在组件内进行状态管理。一旦拥有了状态属性,就会和组件的渲染绑定起来,当状态变化的时候,会调用当前绑定状态变量的组件的build方法刷新UI
使用@State要注意:
1、@State只能在组件内访问
2、支持多种变量的类型
3、需要初始化
4、组件不同实例,数据独立
修饰数组或者对象的时候,可以驱动UI更新,但是,只能更新一层,不能做深层渲染
1.2、@State简单示例
/*** 装饰器@State 详解*/
@Entry
@Component
struct Demo01Page {//修饰基本类型的变量//修饰变量的时候要赋值@State name:string ="孙悟空"@State age:number =19//修饰对象@State emp:Emp = {id:1,name:"喜洋洋",age:2,car:{carId:666,carBrand:"法拉利"}}//修饰普通类型的数组// @State arr:string[] = ["lucy",'lily',"david"]@State arr:Array<string> = ["lucy",'lily',"david"]//修饰的事汽车数组@State carList:Car[] = [{carId:1,carBrand:"法拉利"},{carId:2,carBrand:"劳斯莱斯幻影"},{carId:3,carBrand:"宝马"},{carId:4,carBrand:"奔驰"},]build() {Column({space:5}){Text("基本类型的变量")Text(this.name).fontSize(25).onClick(()=>{this.name = "猪八戒"console.log(this.name)})//如果name没有state,也会在某些情况发生渲染//age发生了变化,age发生了变化就会通知Text组件的build刷新UI//name跟着沾光Text(this.name+this.age+"").onClick(()=>{this.age++})Divider()Text("修饰对象class interface")Text(JSON.stringify(this.emp)).onClick(()=>{//没有car的情况一,更新本身// this.emp = {id:2,name:"灰太狼",age:18}//没有car情况二:更新对象的属性(一级属性)//this.emp.name="灰太狼"//有car 更新emp 更新本身// this.emp = {id:3,name:"tom",age:18,car:{carId:111,carBrand:"劳斯莱斯"}}//更新对象的一级属性(对象的直接属性)//this.emp.car = {carId:333,carBrand:"宝马"}//员工的汽车属于一级属性//员工的汽车的属性属于二级属性,嵌套属性,嵌套属性的变化不会产生渲染this.emp.car.carBrand ="奥迪"})Divider()Text("修饰普通类型的数组")Text(JSON.stringify(this.arr)).onClick(()=>{//数组一级属性指的是下标对应的值//本身是会引起渲染的// this.arr = ['a','b','c']//一级属性也会引起渲染// this.arr[0]='张三'// this.arr.push("zhang")this.arr.splice(0,1)})Divider()Text("修饰的对象数组")Text(JSON.stringify(this.carList)).onClick(()=>{//直接改变数组本身没有问题// this.carList =[]//修改数组的一级属性,也会发生变化// this.carList[3] = {carId:5,carBrand:"奥迪"}//修改数组中的某个对象的属性 属于深层嵌套,不会发生渲染this.carList[3].carBrand="奥迪"})}}
}//声明一个类的时候,属性必须赋值,否则报错
/*class Emp{id:numbername:stringage:number
}*/
//解决方案一 属性可选
// class Emp{
// id?:number
// name?:string
// age?:number
// }
// let emp:Emp = {id:10,name:"",age:30}
// let emp2:Emp = new Emp()
// emp2.id =10;
// emp2.name=""
// emp2.age =19
//解决方案二 给赋值
// class Emp{
// id:number = -1
// name:string =""
// age:number = -1
// }
// let emp:Emp = {id:10,name:"",age:30}
// let emp2:Emp = new Emp()
// emp2.id =10;
// emp2.name=""
// emp2.age =19
//方案三使用构造函数
// class Emp{
// id:number
// name:string
// age:number
// constructor(id:number,name:string,age:number) {
// this.id =id
// this.name=name
// this.age =age
// }
// }
// let emp =new Emp(1,"z行三",19)interface Car{carId:numbercarBrand:string
}interface Emp{id:numbername:stringage:number//员工拥有一辆汽车car:Car
}
// let emp:Emp = {id:-1,name:"zhangsan",age:18}//修饰普通类型属性,值变化直接引起渲染
//修饰对象的时候,对象本身,以及对象属性的变化都会引起渲染
//修改对象A,并且对象A里面还有对象B的时候,
// 修改对象A本身,以及对象A的属性会引起渲染
// 修改对象A 中的对象B的属性的时候属于深层渲染不会引起渲染// 修改数组的时候,更改数值本身,或者下标对应的内容会引起渲染
// 但是修改数组中对象的属性的时候不会引起渲染
2、@Prop
2.1、@Prop简介
@Prop 装饰的变量和父组件建立单向的同步关系:
@Prop 变量允许在本地修改,但修改后的变化不会同步回父组件。
当父组件中的数据源更改时,与之相关的 @Prop 装饰的变量都会自动更新。
2.2、@Prop底层实现原理
简单来说就是当父组件给子组件的**@Prop变量传数据时,ArkUI框架会自动触发深拷贝**。深拷贝后的新实例会覆盖掉子组件原来的旧实例,实现一次单向传递。如果子组件的数据发生变化,修改的是深拷贝后的实例,和父组件的源数据没有关系,所以子组件刷新而父组件不刷新。
补充:
1、深拷贝会递归遍历对象的所有层级属性,逐一复制基本类型的值,遇到引用类型(对象、数组、map、set等)不进行引用传递,而是会new出新的实例,然后再将旧实例里的属性进行复制,和之前一样遇到引用类型就new新的实例然后复制属性,递归重复这个操作直到深拷贝结束。
2、在深拷贝时,基本类型数据是值传递,引用类型没有传递地址,而是new出新实例打破引用链。这样就实现了父组件数据源和子组件数据在内存中完全分离,符合数据单向流动的需求。
2.3、@Prop简单示例
import { webview } from '@kit.ArkWeb'@Entry
@Component
struct Demo02Prop {//父组件有用State状态变量@State emp:Emp = {id:1,name:"开心",age:18,car:{carId:11,carBrand:"法拉利"}}build() {Column(){Text("父组件").fontSize(20)Text(JSON.stringify(this.emp)).onClick(()=>{//直接修改一级属性// this.emp.name="开心果"//修改二级属性父组件都不会产生渲染,子组件自然也就不会产生渲染this.emp.car.carBrand = "大众"})Divider()//使用子组件将值传递时候,父组件的值会自动覆盖子组件的值Zi({ziemp:this.emp})}}
}
@Component
struct Zi {//子组件已经使用@Prop修饰了,所以可以不用赋值@Propziemp:Empbuild() {Row() {Column() {Text("子组件").fontSize(20)Text(JSON.stringify(this.ziemp)).onClick(()=>{//子组件如果发生了渲染不会通知到父组件//换句话 子组件发生了渲染,父组件也不会发生渲染// this.ziemp.name="Kaixin"this.ziemp.car.carId=10})}.width("100%").height("100%")}.width("100%").height("100%")}
}
interface Car{carId:numbercarBrand:string
}
interface Emp{id:numbername:stringage:number//员工拥有一辆汽车car:Car
}
3、@Link
3.1、@Link简介
父子双向同步@Link,子组件中被 @Link 装饰的变量与其父组件中对应的数据源建立双向数据绑定。父组件数据变化引起渲染,那么子组件跟着渲染,反之子组件数据变化引起渲染,那么父组件也跟着渲染。
注意:
@Link 装饰器不能在 @Entry 装饰的自定义组件中使用。
3.2、@Link底层实现原理
简单来说@Link的核心是直接绑定父组件的原始数据引用,而非拷贝数据。当父组件将数据传递给子组件的@Link变量时,实际传递的是数据的内存地址引用。父子组件操作的是同一块内存数据,任何一方的修改都会实时同步到另一方。
扩展:
1、依赖收集:ArkUI框架在初始化时,会为每个@Link变量创建依赖关系图,记录哪些子组件的@Link绑定了父组件的具体数据。2、变更通知”:当父组件数据变化(如通过@State修饰的变量更新),框架通过发布-订阅模式通知所有关联的@Link变量,触发子组件重新渲染。
3、数据劫持与响应式触发:父组件的数据(如@State变量)会被框架用Proxy或Object.defineProperty包装,拦截
get
和set
操作。当子组件通过@Link读取数据时,触发get
,自动建立父子组件的依赖关系。当子组件修改@Link数据时,触发set
,直接修改父组件数据源,并通知所有关联组件更新。
3.3、@Link简单示例
import { webview } from '@kit.ArkWeb'@Entry
@Component
struct Demo03Link {//父组件有用State状态变量@State emp:Emp = {id:1,name:"开心",age:18,car:{carId:11,carBrand:"法拉利"}}build() {Column(){Text("父组件").fontSize(20)Text(JSON.stringify(this.emp)).onClick(()=>{//直接修改一级属性this.emp.name="开心果"//修改二级属性父组件都不会产生渲染,子组件自然也就不会产生渲染// this.emp.car.carBrand = "大众"})Divider()//使用子组件将值传递时候,父组件的值会自动覆盖子组件的值Zi({ziemp:this.emp})}}
}/****/
@Component
struct Zi {//子组件已经使用@Link修饰了,所以可以不用赋值@Linkziemp:Empbuild() {Row() {Column() {Text("子组件").fontSize(20)Text(JSON.stringify(this.ziemp)).onClick(()=>{//子组件如果发生了渲染会通知到父组件//换句话 子组件发生渲染,父组件也会发生渲染// this.ziemp.name="Kaixin"//子组件如果数据变化了,子组件不产生渲染,父组件也不会产生渲染this.ziemp.car.carId=10})}.width("100%").height("100%")}.width("100%").height("100%")}
}interface Car{carId:numbercarBrand:string
}interface Emp{id:numbername:stringage:number//员工拥有一辆汽车car:Car
}
// build函数里面书写的内容要注意什么规范?
//1、只能有一个根组件
//2、不能定义变量
//3、不能直接console.log
//4、不能直接调用普通函数,如果要调用普通函数有返回值可以在组件或者是属性中调用
//5、不能写switch
//6、如果要调用函数,函数得用@Builder进行修饰/*** 函数的按值传递*/
// let a =2
// let b = 3
// function fun1(a:number,b:number){
// a=a+a
// b=b+b
// }
// fun1(a,b)
// console.log("值是:",a,b)/*
按值赋值*/
/*let a =2
let b= a
a =100
console.log("b",b)*///数值 布尔 字符串 都是按值传递
//对象、数组 按引用/*函数的按引用传递*/
/*
let arr:number[] = [2,3]function fun2(arr:number[]){arr[0]= arr[0]+arr[0]arr[1] = arr[1]+arr[1]
}
fun2(arr)
console.log("数组中的值:",arr[0],arr[1])
*//*变量按引用赋值*/
let arr1:number[]=[2,3]
let arr2:number[]=arr1
arr1[0] =10
console.log(JSON.stringify(arr2))
4、@Provide @Consume
4.1、@Provide @Consume简介
@Provide @Consume组合使用可以跨层级双向同步和绑定,而无需通过调用组件传参实现传递绑定。
- @Provide 在祖先组件中定义数据源,允许所有后代组件直接访问。
- @Consume 在后代组件中消费数据,修改数据时会同步到所有关联组件。
- 数据更新时,任意一方的修改都会触发全链路的响应式同步。
4.2、@Provide @Consume底层实现原理
- 引用传递与共享内存
- 无深拷贝机制:与
@Prop
不同,@Provide
和@Consume
直接传递数据的内存引用,父子组件共享同一份数据实例。 - 上下文注入:框架通过隐式的上下文对象(Context)管理数据引用,使后代组件能直接访问祖先的数据源。
- 无深拷贝机制:与
- 依赖追踪与更新传播
- 订阅机制:当组件通过
@Consume
声明依赖时,框架自动建立数据与组件的订阅关系。 - 变更广播:数据发生修改时,框架通过依赖树精准通知所有关联组件,触发局部刷新。
- 订阅机制:当组件通过
- 双向同步流程
- 祖先修改:
@Provide
数据更新时,通过引用直接同步到所有@Consume
组件。 - 后代修改:
@Consume
组件修改数据时,直接修改共享内存的实例,触发祖先和同级消费者的同步更新。
- 祖先修改:
4.3、常见应用场景
- 全局主题配置:祖先组件提供主题色,后代组件直接消费并实时响应变化。
- 跨层级表单控制:表单根组件管理数据,深层嵌套的表单项直接修改数据源。
- 状态共享中间件:替代 Redux 等状态管理库,简化组件间共享状态的逻辑。
5、@Observed @ObjectLink
限制条件巨多,能不使用就不使用,或者直接使用@ObservedV2版本
5.1、@Observed @ObjectLink简介
嵌套对象属性级同步,@Observed和
@ObjectLink 用于实现嵌套对象属性的响应式更新,解决复杂对象(对象数组,嵌套对象等)的局部数据变更同步问题。
- @Observed:装饰类,标记该类为可观察对象,其属性变化可被框架追踪。
- @ObjectLink:装饰变量,用于接收被观察对象的引用,支持对嵌套属性的直接修改和同步。
- 父子组件间通过共享对象引用,实现对象属性变化的双向同步。
限制条件:
1.使用new创建被@Observed装饰的类,才可以被观察到属性的变化。
2.@ObjectLink装饰的变量不能被赋值,如果要使用赋值操作,请使用@Prop。
2.1@Prop装饰的变量和数据源的关系是是单向同步,@Prop装饰的变量在本地拷贝了数据源,所以它允许本地更改,如果父组件中的数据源有更新,@Prop装饰的变量本地的修改将被覆盖。
2.2@ObjectLink装饰的变量和数据源的关系是双向同步,@ObjectLink装饰的变量相当于指向数据源的指针。禁止对@ObjectLink装饰的变量赋值,如果一旦发生@ObjectLink装饰的变量的赋值,则同步链将被打断。因为@ObjectLink装饰的变量通过数据源 (Object)引用来初始化。对于实现双向数据同步的@ObjectLink,赋值相当于更新父组件中的数组项或者class的属性, TypeScript/JavaScript不能实现,会发生运行时报错。
3.@Observed装饰的类,如果其属性为非简单类型,比如class、Object或者数组,也需要被@Observed装饰,否则将观察不到其属性的变化。
4.使用@Observed装饰class会改变class原始的原型链,@Observed和其他类装饰器装饰同一个class可能会带来问题。
5.@ObjectLink装饰器不能在@Entry装饰的自定义组件中使用。只能在子组件中使用
5.2、@Observed @ObjectLink底层实现原理
- 可观察类包装
- @Observed 的作用:框架会为被装饰的类生成代理,拦截属性的
getter/setter
方法,自动追踪属性读写操作。 - 依赖收集:当组件通过
@ObjectLink
访问对象属性时,框架记录属性与组件的依赖关系。
- @Observed 的作用:框架会为被装饰的类生成代理,拦截属性的
- 引用传递与属性监听
- 属性级更新:仅当被监听的具体属性发生变更时,触发关联组件的精准更新(非全对象刷新)。
- 双向同步机制
- 子组件修改属性:通过
@ObjectLink
直接修改对象属性,触发父组件和同级组件的同步更新。 - 父组件修改属性:若父组件修改被观察对象的属性,所有持有该对象引用的
@ObjectLink
变量自动更新。
- 子组件修改属性:通过
三、应用状态管理
1、LocalStorage
1.1、LocalStorage简介
页面级状态共享,用于同一页面内多个组件间的数据同步(非跨页面),也就是1个父组件和N个子组件之间的同步。(通过GetShared
接口实现跨页面共享)
补充:大多数情况都是同一页面使用,但是它也支持多页面使用:
1.将LocalStorage实例从UIAbility共享到一个或多个视图:在uiAbility生命周期中注入实例官方示例
2.除了根节点可通过@Entry来接收LocalStorage实例,自定义组件(子节点)也可以通过构造参数来传递LocalStorage实例:子组件被调用时也可通过构造参数来传递官方示例
let ls = new LocalStorage({ 'fontSize': 16 }); // 必须指定初始值
@Entry(ls) // 根组件必须通过@Entry绑定
关键限制:
- 属性值类型不可动态变更(初始化后禁止修改类型)
- 不同UIAbility间的LocalStorage实例相互隔离
1.2、LocalStorage相关原理
- 存储机制:数据存储在页面内存中,生命周期与页面绑定(页面销毁时释放)。
- 同步机制:
- 单向同步:
@LocalStorageProp
装饰的变量仅从LocalStorage接收更新,本地修改不反馈回LocalStorage。 - 双向同步:
@LocalStorageLink
装饰的变量与LocalStorage双向绑定,修改会同步到LocalStorage及其他绑定组件。
- 单向同步:
- 实例管理:
- 根组件通过
@Entry
绑定LocalStorage实例,子组件继承访问。 - 每个页面可创建多个实例,但组件树仅能访问一个实例。
- 根组件通过
1.3、LocalStorage简单示例
import { User } from '../../model/Person'
import { UserInfo } from '../../model/User'//1.准备数据
let parms:Record<number,number> ={"123":123456}
let user:UserInfo = {name:"lisi",age:123}
//2.实例化LocalStorage,他有构造函数可以在实例化时初始话参数
let local:LocalStorage=new LocalStorage(parms)//3.setOrCreate可以追加和修改参数,但key值存在时就修改,当key不存在时就创建
local.setOrCreate("123",123)
local.setOrCreate("user",user)//4.将LocalStorage传入@Entry的括号中,LocalStorage只能通过父组件传入,其后代都可以使用,
// 通过@LocalStorageLink("key")双向和@LocalStorageProp("key")单向同步数据
@Entry(local)
@Component
struct Page_01_LocalStarge {//LocalStorageLink("key") 如果key存在于LocalStorage就使用存储的值替换这里的本地初始化的值,//如果key不存在于LocalStorage,那么就会用这个key在LocalStorage创建一条新的记录,并且值为本地初始化的值@LocalStorageLink("user")user:UserInfo = {} as User;@LocalStorageLink("123")num:number = 0;build() {Column(){Text(JSON.stringify(this.user)).onClick(()=>{this.user.name="wangwu"})Text(this.num+"").onClick(()=>{this.num++})Divider()LocalStorage_01()LocalStorage_01()}.height('100%').width('100%')}
}@Component
struct LocalStorage_01{@LocalStorageLink("user")user_localstorage:UserInfo = {} as User;@LocalStorageLink("123")num_num:number = 0;build() {Column(){Text(JSON.stringify(this.user_localstorage)).onClick(()=>{this.user_localstorage.name="zhaoliu"})Text(this.num_num+"").onClick(()=>{this.num_num++})}}
}
2、AppStorage
2.1、AppStorage简介
应用级全局单例存储,支持跨页面、跨UIAbility数据共享,数据生命周期与应用进程一致
AppStorage.setOrCreate('user', { name: 'John' }); // 动态创建属性
AppStorage.delete('tempData'); // 手动删除属性
2.2、AppStorage相关原理
- 存储机制:数据存储在应用内存中,应用退出时销毁(非持久化)。
- 同步机制:
- 单向同步:
@StorageProp
仅接收AppStorage更新,本地修改不反馈。 - 双向同步:
@StorageLink
实现双向同步,修改会触发全局UI更新。
- 单向同步:
- 协作机制:
- 可与LocalStorage通过
LocalStorage.getShared()
选择性同步数据。 - 与PersistentStorage协作时,AppStorage中的值会覆盖PersistentStorage同名属性。
- 可与LocalStorage通过
- 全局共享原理
- 数据覆盖规则
- 应用启动时:PersistentStorage的数据会覆盖AppStorage同名属性
- 运行时:AppStorage的修改会覆盖PersistentStorage的值
- 环境变量联动
- 通过
Environment.EnvProp
将设备环境变量(如语言/区域)自动同步到AppStorage - 使用
@StorageProp('language')
可直接读取环境参数
- 通过
- 数据覆盖规则
2.3、AppStorage简单示例
从UI内部使用:官方示例
从应用逻辑使用:官方示例
3、PersistentStorage
3.1、PersistentStorage简介
持久化存储AppStorage中的指定属性,应用重启后保留数据(如用户配置)。
// 正确示例:未在AppStorage预定义属性
PersistentStorage.persistProp('num', 123); // 报错:'theme'未定义// 错误流程1.AppStorage在PersistentStorage之前定义
AppStorage.setOrCreate('num', 987); //每次应用重启,AppStorage的值(987)会覆盖PersistentStorage里的值(123)
PersistentStorage.persistProp('num',123);// 错误流程2.AppStorage在PersistentStorage之后定义
PersistentStorage.persistProp('num',123); //每次应用重启,AppStorage的值(987)会覆盖PersistentStorage里的值(123)
AppStorage.setOrCreate('num', 987); //正确流程2.AppStorage在PersistentStorage之后定义,通过判断来决定是否覆盖
PersistentStorage.persistProp('num',123); //先定义
if(AppStorage.get('num'>130)){//通过判断决定是否覆盖AppStorage.setOrCreate('num', 987);
}
3.2、PersistentStorage相关原理
1.存储机制:
- 数据异步写入本地磁盘,避免阻塞UI。
- 仅支持基础类型(string、number、boolean等),复杂数据需序列化。
- 数据变更后延迟300ms异步写入磁盘(防频繁IO操作)
- 应用退出时强制执行未完成的写入操作
2.绑定规则:
- 通过
PersistentStorage.PersistProp()
绑定AppStorage中的属性键值。 - 初始化时优先读取磁盘数据,若AppStorage已存在同名属性,则覆盖磁盘值。
3.限制条件
- 必须在AppStorage中先定义属性,再调用
PersistProp()
。 - 异步写入可能导致数据更新延迟,需注意一致性处理。
3.3、PersistentStorage简单示例
PersistentStorage文档地址:官方示例
4、Environment
4.1、Environment简介
开发者如果需要应用程序运行的设备的环境参数,以此来作出不同的场景判断,比如多语言,深浅色模式等,需要用到Environment设备环境查询。
Environment是ArkUI框架在应用程序启动时创建的单例对象。它为AppStorage提供了一系列描述应用程序运行状态的属性。Environment的所有属性都是不可变的(即应用不可写入),所有的属性都是简单类型。
Environment提供了读取系统某些环境变量的能力,具体见Environment内置参数,并将其值写入AppStorage里,所以开发者需要通过AppStorage才能获取环境变量的值。
Environment内置参数:
键 | 数据类型 | 描述 |
---|---|---|
accessibilityEnabled | boolean | 获取无障碍屏幕读取是否启用。 |
colorMode | ColorMode | 色彩模型类型:选项为ColorMode.LIGHT: 浅色,ColorMode.DARK: 深色。 |
fontScale | number | 字体大小比例。开发者需要配置configuration使fontScale跟随系统变化。 |
fontWeightScale | number | 字体粗细程度。 |
layoutDirection | LayoutDirection | 布局方向类型:包括LayoutDirection.LTR: 从左到右,LayoutDirection.RTL: 从右到左。 |
languageCode | string | 当前系统语言值,取值必须为小写字母, 例如zh。 |
4.2、Environment简单示例
文档地址:官方示例
四、其他状态管理
1、@Watch
1.1、@Watch简介
@Watch用于监听状态变量的变化,当状态变量变化时,@Watch的回调方法将被调用。@Watch在ArkUI框架内部判断数值有无更新使用的是严格相等(===),遵循严格相等规范。当严格相等判断的结果是false(即不相等)的情况下,就会触发@Watch的回调。
装饰器说明
@Watch补充变量装饰器 | 说明 |
---|---|
装饰器参数 | 必填。常量字符串,字符串需要有引号。是自定义成员函数的方法的引用。 |
可装饰的自定义组件变量 | 可监听所有装饰器装饰的状态变量。不允许监听常规变量。 |
装饰器的顺序 | 装饰器顺序不影响实际功能,开发者可以根据自己的需要决定装饰器顺序的先后。建议@State、@Prop、@Link等装饰器在@Watch装饰器之前,以保持整体风格的一致。 |
@Watch触发时机 | 使用@Watch来监听状态变量变化时,回调触发时间是变量真正变化、被赋值的时间。详细示例请参考使用场景中的@Watch的触发时机。 |
1.2、@Watch相关原理
观察变化和行为表现:
- 当观察到状态变量的变化(包括双向绑定的AppStorage和LocalStorage中对应的key发生的变化)的时候,对应的@Watch的回调方法将被触发;
- @Watch方法在自定义组件的属性变更之后同步执行;
- 如果在@Watch的方法里改变了其他的状态变量,也会引起状态变更和@Watch的执行;
- 在第一次初始化的时候,@Watch装饰的方法不会被调用,即认为初始化不是状态变量的改变。只有在后续状态改变时,才会调用@Watch回调方法。
限制条件:
- 建议开发者避免无限循环。循环可能是因为在@Watch的回调方法里直接或者间接地修改了同一个状态变量引起的。为了避免循环的产生,建议不要在@Watch的回调方法里修改当前装饰的状态变量;
- 开发者应关注性能,属性值更新函数会延迟组件的重新渲染(具体请见上面的行为表现),因此,回调函数应仅执行快速运算;
- 不建议在@Watch函数中调用async await,因为@Watch设计的用途是为了快速的计算,异步行为可能会导致重新渲染速度的性能问题。
1.3、@Watch简单示例
@Component
struct TotalView {@Prop @Watch('onCountUpdated') count: number = 0;@State total: number = 0;// @Watch 回调onCountUpdated(propName: string): void {this.total += this.count;}build() {Text(`Total: ${this.total}`)}
}@Entry
@Component
struct CountModifier {@State count: number = 0;build() {Column() {Button('add to basket').onClick(() => {this.count++})TotalView({ count: this.count })}}
}
2、$$语法
$$运算符为系统内置组件提供TS变量的引用,使得TS变量和系统内置组件的内部状态保持同步。内部状态具体指什么取决于组件。例如,TextInput组件的text参数。
补充:
$$还用于@Builder装饰器的按引用传递参数,开发者需要注意两种用法的区别。
使用规则:
-
当前$$支持基础类型变量,以及@State、@Link和@Prop装饰的变量。
-
当前$$支持的组件:
组件 支持的参数/属性 起始API版本 Checkbox select 10 CheckboxGroup selectAll 10 DatePicker selected 10 TimePicker selected 10 MenuItem selected 10 Panel mode 10 Radio checked 10 Rating rating 10 Search value 10 SideBarContainer showSideBar 10 Slider value 10 Stepper index 10 Swiper index 10 Tabs index 10 TextArea text 10 TextInput text 10 TextPicker selected、value 10 Toggle isOn 10 AlphabetIndexer selected 10 Select selected、value 10 BindSheet isShow 10 BindContentCover isShow 10 Refresh refreshing 8 GridItem selected 10 ListItem selected 10 -
$$绑定的变量变化时,会触发UI的同步刷新。
3、@Track
3.1、@Track简介
@Track应用于class对象的属性级更新。@Track装饰的属性变化时,只会触发该属性关联的UI更新。
@Track是class对象的属性装饰器。当一个class对象是状态变量时,@Track装饰的属性发生变化,只会触发该属性关联的UI更新;如果class类中使用了@Track装饰器,则未被@Track装饰器装饰的属性不能在UI中使用,如果使用,会发生运行时报错。
3.2、@Track相关原理
3.3、@Track简单示例
使用@Track装饰器可以避免冗余刷新。
class LogTrack {@Track str1: string;@Track str2: string;constructor(str1: string) {this.str1 = str1;this.str2 = 'World';}
}class LogNotTrack {str1: string;str2: string;constructor(str1: string) {this.str1 = str1;this.str2 = '世界';}
}@Entry
@Component
struct AddLog {@State logTrack: LogTrack = new LogTrack('Hello');@State logNotTrack: LogNotTrack = new LogNotTrack('你好');isRender(index: number) {console.log(`Text ${index} is rendered`);return 50;}build() {Row() {Column() {Text(this.logTrack.str1) // Text1.fontSize(this.isRender(1)).fontWeight(FontWeight.Bold)Text(this.logTrack.str2) // Text2.fontSize(this.isRender(2)).fontWeight(FontWeight.Bold)Button('change logTrack.str1').onClick(() => {this.logTrack.str1 = 'Bye';})Text(this.logNotTrack.str1) // Text3.fontSize(this.isRender(3)).fontWeight(FontWeight.Bold)Text(this.logNotTrack.str2) // Text4.fontSize(this.isRender(4)).fontWeight(FontWeight.Bold)Button('change logNotTrack.str1').onClick(() => {this.logNotTrack.str1 = '再见';})}.width('100%')}.height('100%')}
}
在上面的示例中:
-
类LogTrack中的属性均被@Track装饰器装饰,点击按钮"change logTrack.str1",此时Text1刷新,Text2不刷新,只有一条日志输出,避免了冗余刷新。
Text 1 is rendered
-
类logNotTrack中的属性均未被@Track装饰器装饰,点击按钮"change logNotTrack.str1",此时Text3、Text4均会刷新,有两条日志输出,存在冗余刷新。
Text 3 is renderedText 4 is rendered
4、自定义组件冻结功能
内容颇多,这里只做介绍,详情自行查看,文档地址:自定义组件冻结功能的官方文档
自定义组件冻结功能专为优化复杂UI页面的性能而设计,尤其适用于包含多个页面栈、长列表或宫格布局的场景。在这些情况下,当状态变量绑定了多个UI组件,其变化可能触发大量UI组件的刷新,进而导致界面卡顿和响应延迟。为了提升这类负载UI界面的刷新性能,开发者可以选择尝试使用自定义组件冻结功能。
组件冻结的工作原理是:
- 开发者通过设置freezeWhenInactive属性,即可激活组件冻结机制。
- 启用后,系统将仅对处于激活状态的自定义组件进行更新,这使得UI框架可以尽量缩小更新范围,仅限于用户可见范围内(激活状态)的自定义组件,从而提高复杂UI场景下的刷新效率。
- 当之前处于inactive状态的自定义组件重新变为active状态时,状态管理框架会对其执行必要的刷新操作,确保UI的正确展示。
简而言之,组件冻结旨在优化复杂界面下的UI刷新性能。在存在多个不可见自定义组件的情况下,如多页面栈、长列表或宫格,通过组件冻结可以实现按需刷新,即仅刷新当前可见的自定义组件,而将不可见自定义组件的刷新延迟至它们变为可见时。
需要注意,组件active/inactive并不等同于其可见性。组件冻结目前仅适用于以下场景:
-
页面路由:当前栈顶页面为active状态,非栈顶不可见页面为inactive状态。
-
TabContent:只有当前显示的TabContent中的自定义组件处于active状态,其余则为inactive。
-
LazyForEach:仅当前显示的LazyForEach中的自定义组件为active状态,而缓存节点的组件则为inactive状态。
-
Navigation:当前显示的NavDestination中的自定义组件为active状态,而其他未显示的NavDestination组件则为inactive状态。
-
组件复用:进入复用池的组件为inactive状态,从复用池上树的节点为active状态。
其他场景,如堆叠布局(Stack)下的被遮罩的组件,这些组件尽管不可见,但并不被视为inactive状态,因此不在组件冻结的适用范围内。
五、面试题回答模板(待完善)
1、简述@Link实现原理
@Link的底层实现可以分为三个方面理解:
- 引用传递:父组件把数据的‘内存地址’直接传给子组件,双方操作同一份数据。
- 响应式监听:框架通过Proxy监控数据变化,无论父子哪边修改,都能立刻触发对方更新。
- 依赖管理:每个@Link变量背后都有一个依赖列表,数据变化时精准通知相关组件,避免无效渲染。
这就像父子组件共用一张草稿纸,任何一方写字,另一方都能实时看到。
2、简述@Prop实现原理
@Link的底层实现可以分为两个方面理解:
1. 核心机制:深拷贝
“@Prop的核心是深拷贝隔离。当父组件给子组件的@Prop传数据时,框架会像‘复印机’一样,递归复制所有层级的数据,生成一个完全独立的新对象。
- 基本类型:直接复制值(比如数字、字符串)。
- 引用类型:遇到对象、数组,会先
new
一个空壳,再把里面的值一层层扒开复制,彻底斩断和原数据的关联。
最终子组件拿到的是一个‘只读副本’,和父组件数据在内存里完全隔离。
2. 更新触发规则
@Prop的数据流动是单向的,只有父改子,子不能反向改父:
- 父组件更新:父数据变化时,会触发新一轮深拷贝,生成新副本覆盖子组件的旧数据,导致父子组件一起刷新。
- 子组件修改:子组件可以改自己的@Prop副本,但改的是‘复印件’,父组件的‘原件’完全不受影响,所以只有子组件自己会刷新。
3、简述@Prop和@Link的区别
@Prop和@Link的区别主要体现在数据传递方式上:
- 数据隔离性:@Prop会深拷贝父数据,生成独立副本,子组件修改不影响父组件;@Link直接绑定父数据的引用,双方共享同一份数据。
- 同步方向:@Prop是单向同步,只能父传子;@Link是双向同步,父子互相实时同步。
- 适用场景:@Prop适合需要数据隔离的静态展示组件,比如商品卡片;@Link适合需要双向交互的控件,比如表单输入框。
4、简述@Provide @Consume和@Link的区别
@Provide/@Consume 和 @Link 都用于实现双向数据同步,但设计目标不同。@Link 专注直接父子组件的高效通信,通过显式参数传递共享内存引用,适合简单层级的高性能场景;@Provide/@Consume 则通过上下文机制实现跨层级数据穿透,省去中间组件的传递成本,但需要维护更复杂的依赖关系,适合深层组件共享低频数据的场景。二者在性能与便利性之间做了权衡,因此需要根据组件层级和数据使用范围选择合适的方案。
可补充说明:
- 在 ArkUI 的响应式系统中,优先使用
@Link
保持数据流透明性 - 对全局状态(如 Redux 管理的状态),可用
@Provide/@Consume
替代部分状态管理库的功能 - 超深层级(5层以上)优先考虑
@Provide/@Consume
,但需警惕过度使用导致的维护成本
相关文章:
【Harmony】状态管理(V1)
一、概述 文章目录 一、概述二、组件状态管理1、State1.1、State简介1.2、State简单示例 2、Prop2.1、Prop简介2.2、Prop底层实现原理2.3、Prop简单示例 3、Link3.1、Link简介3.2、Link底层实现原理3.3、Link简单示例 4、Provide Consume4.1、Provide Consume简介4.2、Provide …...
udev规则实例:监听usb插拔事件并做出相应
在 Linux 和 Android 系统中,USB 插拔事件的判断涉及从内核到用户空间的多层协作。以下是源码中关键判断点的梳理: 事件流程 内核层:UEvent 机制 USB 插拔事件首先由内核通过 UEvent 机制 上报。内核中的 USB 驱动(如 drivers/…...
【算法】【蓝桥23国A软件C】四版代码思路分析与逐步优化
题目来源:第十四届蓝桥杯大赛软件赛国赛C/C 大学 A 组 题目描述: 问题描述 给定一个 WH 的长方形,两边长度均为整数。小蓝想把它切割为很多个边长为整数的小正方形。假设切割没有任何损耗,正方形的边长至少为 2,不允…...
程序设计竞赛1
题目1 2025年春节期间,DeepSeek作为“AI界的天降紫微星”成为新晋效率神器,热度席卷全球,其团队主创成员也迅速引起了大家的关注。 DeepSeek之所以能在短时间内取得如此不凡成绩,与其团队成员的背景密不可分。团队汇聚了来自清华…...
android studio 2022打开了v1 签名但是生成的apk没有v1签名问题
我使用了Android Studio Flamingo | 2022.2.1 Patch 2版本的IDE编译了一个apk,但是apksigner查看apk的签名信息时,发现只有v2签名,没有v1签名。 apksigner verify -v app-debug.apk Verifies Verified using v1 scheme (JAR signing): false Verified usin…...
EPGAN:融合高效注意力的生成对抗网络图像修复算法
简介 简介:利用掩码设计来遮掉输入图像的一部分,将这类图像输入给生成器。生成器结合ECA注意力机制架构,利用感知损失、对抗损失和均方误差损失的加权和来作为生成器的损失计算。鉴别器分别对应掩码和整张图做损失计算。 论文题目:融合高效注意力的生成对抗网络图像修复算…...
使用模板报错:_G.unicode.len(orgline.text_stripped:gsub(“ “,““))
使用aegisub制作歌词特效,白嫖大佬的自动化模板时,经常会遇到如下报错: Runtime error in template code: Expected 1 arguments, got 2 Code producing error: ci {0,0}; cn _G.unicode.len(orgline.text_stripped:gsub(" ",&q…...
linux入门六:Linux Shell 编程
一、Shell 概述 1. 什么是 Shell? Shell 是 Linux 系统中用户与内核之间的桥梁,作为 命令解析器,它负责将用户输入的文本命令转换为计算机可执行的机器指令。 本质:Shell 是一个程序(如常见的 Bash、Zsh)…...
Franka 机器人x Dexterity Gen引领遥操作精细任务新时代
教授机器人工具灵活操作难题 在教授机器人灵活使用工具方面,目前主要有两种策略:一是人类遥控(用于模仿学习),二是模拟到现实的强化学习。然而,这两种方法均存在明显的局限性。 1、人类遥控(用…...
网络通讯协议UDP转发TCP工具_UdpToTcpRelay_双向版
UDP/TCP网络转发器程序说明书 1. 程序概述 本程序是一个高性能网络数据转发工具,支持UDP和TCP协议之间的双向数据转发,并具备以下核心功能: 协议转换:实现UDP↔TCP协议转换数据转换:支持十六进制/ASCII格式的数据转…...
深入理解 RxSwift 中的 Driver:用法与实践
目录 前言 一、什么是Driver 1.不会发出错误 2.主线程保证 3.可重放 4.易于绑定 二、Driver vs Observable 三、使用场景 1.绑定数据到UI控件 2.响应用户交互 3.需要线程安全的逻辑 4.如何使用Driver? 1.绑定文本输入到Label 2.处理按钮点击事件 3…...
【XML基础-3】深入理解XML Schema:XML的强大语义约束机制
XML(可扩展标记语言)作为数据交换的标准格式,在当今信息技术领域扮演着重要角色。然而,仅有基本的XML语法规则往往不足以满足复杂的数据验证需求。这正是XML Schema发挥作用的地方——它为XML文档提供了强大的语义约束能力。本文将…...
神经网络语言模型与统计语言模型的比较
神经网络语言模型(Neural Language Models, NLMs)与统计语言模型(Statistical Language Models, SLMs)是自然语言处理(NLP)中两类核心的语言建模方法,其核心差异体现在建模方式、表示能力、数据…...
大模型论文:CRAMMING TRAINING A LANGUAGE MODEL ON ASINGLE GPU IN ONE DAY(效率提升)-final
大模型论文:CRAMMING: TRAINING A LANGUAGE MODEL ON ASINGLE GPU IN ONE DAY(效率提升) 文章地址:https://arxiv.org/abs/2212.14034 摘要 近年来,语言建模的研究趋势集中在通过大规模扩展来提升性能,导致训练语言模型的成本变…...
构建AI应用(持续更新)
常用的框架: dify、coze:低代码模块化编程 langchain:面向程序人员 常规的应用: 语音转文字ASR,文字转语音TTS,下一步问题建议, 旅游计划,买点提取,情感陪聊&#x…...
【JAVA】JVM 堆内存“缓冲空间”的压缩机制及调整方法
1. 缓冲空间是否可压缩? 是的,JVM 会在满足条件时自动收缩堆内存,将未使用的缓冲空间释放回操作系统。但需满足以下条件: GC 触发堆收缩:某些垃圾回收器(如 G1、Serial、Parallel)在 Full GC …...
NLP高频面试题(三十八)——什么是LLM的灾难性遗忘?如何避免灾难性遗忘?
近年来,大语言模型在人工智能领域取得了显著进展。然而,随着模型的不断更新和新任务的引入,出现了一个重要的问题,即灾难性遗忘(Catastrophic Forgetting)。灾难性遗忘指的是大模型在连续学习新知识或新任务时,先前掌握的旧知识会迅速被覆盖或遗忘,从而导致模型在旧任务…...
Keepalived+LVS高可用集群实战:从原理到落地
在分布式系统架构中,服务的高可用性和负载均衡是保障业务连续性的核心要素。本文通过一次实验,深入探索了基于KeepalivedLVS的高可用负载均衡集群方案,带您从零开始理解原理、动手实践配置,并验证其可靠性。 一、实验目标 本次实…...
【JVM】JVM调优实战
😀大家好,我是白晨,一个不是很能熬夜😫,但是也想日更的人✈。如果喜欢这篇文章,点个赞👍,关注一下👀白晨吧!你的支持就是我最大的动力!Ǵ…...
Linux系统安全-开发中注意哪些操作系统安全
Hey小伙伴们~👋 在Linux开发中,确保操作系统的安全真的太太太重要啦!🛡️ 今天就来和大家聊聊几个超关键的注意事项,记得拿小本本记下来哦!📝 1️⃣ 用户管理与权限控制👥 合理…...
Qt问题之 告别软件因系统默认中文输入法导致错误退出的烦恼
1. 问题 使用Qt进行研发时,遇到一个问题,当在系统默认输入法中文(英文输入法或者搜狗就不会触发闪退)的情况下,选中QTableWidget控件(QTableWidgetItem有焦点,但是不双击)ÿ…...
2025 年“认证杯”数学中国数学建模网络挑战赛 D题 无人机送货规划
在快递和外卖等短途递送小件货物的业务中,无人机或许大有可为。现 有一个城市的快递仓库准备使用若干无人机进行派件,设有若干架无人机从 仓库出发,分别装载了若干快递包裹。每架无人机装载的包裹的收货地点会 被排列为一个目的地列表&#x…...
【2025年认证杯数学中国数学建模网络挑战赛】A题解题思路与模型代码
【2025年认证杯数学建模挑战赛】A题 该题为典型的空间几何建模轨道动力学建模预测问题。 ⚙ 问题一:利用多个天文台的同步观测,确定小行星与地球的相对距离 问题分析 已知若干地面天文台的观测数据:方位角 (Azimuth) 和 高度角 (Altitude)&…...
Redhat红帽 RHCE8.0认证体系课程
课程大小:7.7G 课程下载:https://download.csdn.net/download/m0_66047725/90546064 更多资源下载:关注我 红帽企业 Linux 系统的管理技能已经成为现代数据中心的核心竞争力。 Linux 在支持混合云、跨物理服务器、虚机、私有云和公共云计…...
Python 实现的运筹优化系统数学建模详解(最大最小化模型)
一、引言 在数学建模的实际应用里,最大最小化模型是一种极为关键的优化模型。它的核心目标是找出一组决策变量,让多个目标函数值里的最大值尽可能小。该模型在诸多领域,如资源分配、选址规划等,都有广泛的应用。本文将深入剖析最大…...
MySQL快速入门
MySQL快速入门 SQL语句 SQL语句概述 1.SQL 是用于访问和处理数据库的标准的计算机语言。 2.SQL指结构化查询语言,全称是 Structured Query Language。 3.SQL 可以访问和处理数据库。 4.SQL 是一种 ANSI(American National Standards Institute 美国…...
离线安装 nvidia-docker2(nvidia-container-toolkit)
很多时候大家都有用docker使用gpu的需求,但是因为网络等原因不是那么好用,这里留了一个给ubuntu的安装包,网络好的话也提供了在线安装方式 安装 nvidia-docker2 1 离线安装 (推荐) unzip解压后进入目录 dpkg -i *.d…...
【自然语言处理】深度学习中文本分类实现
文本分类是NLP中最基础也是应用最广泛的任务之一,从无用的邮件过滤到情感分析,从新闻分类到智能客服,都离不开高效准确的文本分类技术。本文将带您全面了解文本分类的技术演进,从传统机器学习到深度学习,手把手实现一套…...
云原生运维在 2025 年的发展蓝图
随着云计算技术的不断发展和普及,云原生已经成为了现代应用开发和运维的主流趋势。云原生运维是指在云原生环境下,对应用进行部署、监控、管理和优化的过程。在 2025 年,云原生运维将迎来更加广阔的发展前景,同时也将面临着一系列…...
Windows系统Python多版本运行解决TensorFlow安装问题(附详细图文)
Windows系统Python多版本运行解决TensorFlow安装问题(附详细图文) 摘要 TensorFlow 无法安装?Python版本太高是元凶! 本文针对Windows系统中因Python版本过高导致TensorFlow安装失败的问题,提供三种降级解决方案&…...
银行业务知识序言
银行业务知识体系全景解析 第一章 金融创新浪潮下的银行业务知识革命 1.1 数字化转型驱动金融业态重构 在区块链、人工智能、物联网等技术的叠加作用下,全球银行业正经历着"服务无形化、流程智能化、风控穿透化"的深刻变革。根据麦肯锡《2023全球银行业…...
《深度剖析分布式软总线:软时钟与时间同步机制探秘》
在分布式系统不断发展的进程中,设备间的协同合作变得愈发紧密和复杂。为了确保各个设备在协同工作时能够有条不紊地进行,就像一场精准的交响乐演出,每个乐器都要在正确的时间奏响音符,分布式软总线中的软时钟与时间同步机制应运而…...
RK3588 android12 适配 ilitek i2c接口TP
一,Ilitek 触摸屏简介 Ilitek 提供多种型号的触控屏控制器,如 ILI6480、ILI9341 等,采用 I2C 接口。 这些控制器能够支持多点触控,并具有优秀的灵敏度和响应速度。 Ilitek 的触摸屏控制器监测屏幕上的触摸事件。 当触摸发生时&a…...
pgsql:关联查询union(并集)、except(差集)、intersect(交集)
pgsql:关联查询union(并集)、except(差集)、intersect(交集)_pgsql except-CSDN博客...
模型材质共享导致的问题
问题:当我选中其中某个网格模型并设置color的时候,相同种类的颜色都被改变,但是打印我选中的网格模型数据其实只有一个。 导致问题的原因: 加载Blender模型修改材质颜色 Blender创建一个模型对象,设置颜色࿰…...
ThinkpPHP生成二维码
导入依赖 composer require endroid/qr-code 封装成函数,传入二维码包含的值,存储路径,二维码大小,二维码边距 private function getCode($content, $directory, $size 300, $margin 10){// 创建二维码对象// $content: 二…...
FLINK框架:流式处理框架Flink简介
在大数据时代,数据的价值不言而喻,谁能利用好数据,谁就掌握了整个行业的先机。面对海量的数据,如何处理数据成为了一个难题。除了海量数据外,实时性也是一个重要的课题,所以流式数据处理便登上了技术舞台&a…...
使用Python从零开始构建生成型TransformerLM并训练
在人工智能的浩瀚宇宙中,有一种神奇的生物,它拥有着强大的语言魔法,能够生成各种各样的文本,仿佛拥有无尽的创造力。它就是——Transformer 模型!Transformer 模型的出现,为人工智能领域带来了一场“语言魔…...
xtrabackup备份
安装: https://downloads.percona.com/downloads/Percona-XtraBackup-8.0/Percona-XtraBackup-8.0.35-30/binary/tarball/percona-xtrabackup-8.0.35-30-Linux-x86_64.glibc2.17.tar.gz?_gl1*1ud2oby*_gcl_au*MTMyODM4NTk1NS4xNzM3MjUwNjQ2https://downloads.perc…...
2.3 Spark运行架构与流程
Spark运行架构与流程包括几个核心概念:Driver负责提交应用并初始化作业,Executor在工作节点上执行任务,作业是一系列计算任务,任务是作业的基本执行单元,阶段是一组并行任务。Spark支持多种运行模式,包括单…...
【Pandas】pandas DataFrame head
Pandas2.2 DataFrame Indexing, iteration 方法描述DataFrame.head([n])用于返回 DataFrame 的前几行 pandas.DataFrame.head pandas.DataFrame.head 是一个方法,用于返回 DataFrame 的前几行。这个方法非常有用,特别是在需要快速查看 DataFrame 的前…...
从递归入手一维动态规划
从递归入手一维动态规划 1. 509. 斐波那契数 1.1 思路 递归 F(i) F(i-1) F(i-2) 每个点都往下展开两个分支,时间复杂度为 O(2n) 。 在上图中我们可以看到 F(6) F(5) F(4)。 计算 F(6) 的时候已经展开计算过 F(5)了。而在计算 F(7)的时候,还需要…...
鸿蒙HarmonyOS埋点SDK,ClkLog适配鸿蒙埋点分析
ClkLog埋点分析系统,是一种全新的、开源的洞察方案,它能够帮助您捕捉每一个关键数据点,确保您的决策基于最准确的用户行为分析。技术人员可快速搭建私有的分析系统。 ClkLog鸿蒙埋点SDK通过手动埋点的方式实现HarmonyOS 原生应用的前端数据采…...
HarmonyOS:HMPermission权限请求框架
前段时间利用空余时间写了一个权限请求库:HMPermission。 一,简介 HMPermission 是鸿蒙系统上的一款权限请求框架,封装了权限请求逻辑,采用链式调用的方式请求权限,简化了权限请求的代码。 二,使用方法 …...
【书籍】DeepSeek谈《持续交付2.0》
目录 一、深入理解1. 核心理念升级:从"自动化"到"双环模型"2. 数字化转型的五大核心能力3. 关键实践与案例4. 组织与文化变革5. 与其它框架的关系6. 实际应用建议 二、对于开发实习生的帮助1. 立刻提升你的代码交付质量(技术验证环实…...
Spring AOP 扫盲
🧑 博主简介:CSDN博客专家,历代文学网(PC端可以访问:https://literature.sinhy.com/#/literature?__c1000,移动端可微信小程序搜索“历代文学”)总架构师,15年工作经验,…...
银河麒麟v10(arm架构)部署Embedding模型bge-m3【简单版本】
硬件 服务器配置:鲲鹏2 * 920(32c) 4 * Atlas300I duo卡 参考文章 https://www.hiascend.com/developer/ascendhub/detail/07a016975cc341f3a5ae131f2b52399d 鲲鹏昇腾Atlas300Iduo部署Embedding模型和Rerank模型并连接Dify(自…...
如何通过流程管理优化企业运营?
流程管理的本质是“用确定性的规则应对不确定性的业务”。 那么,具体该如何通过流程管理来优化企业的运作呢?以下是一些关键步骤和思路,或许能给到一些启发。 1. 从流程梳理开始:摸清现状,找准问题 想要管理好企业的…...
ZYNQ笔记(四):AXI GPIO
版本:Vivado2020.2(Vitis) 任务:使用 AXI GPIO IP 核实现按键 KEY 控制 LED 亮灭(两个都在PL端) 一、介绍 AXI GPIO (Advanced eXtensible Interface General Purpose Input/Output) 是 Xilinx 提供的一个可…...
Java学习手册:JVM、JRE和JDK的关系
在Java生态系统中,JVM(Java虚拟机)、JRE(Java运行时环境)和JDK(Java开发工具包)是三个核心概念。它们共同构成了Java语言运行和开发的基础。理解它们之间的关系对于Java开发者来说至关重要。本文…...