免费微商城网站建设,黑龙江城乡建设厅官网,做网站赔钱了,网站开发岗位目录#xff1a; 1、UIAbility的冷启动和UIAbility热启动2、静态资源和动态资源的访问3、页面跳转3.1、页面返回跳转 4、HAR的ArkUI组件、接口、资源#xff0c;供其他应用或当前应用的其他模块引用4.1、导出HAR的ArkUI组件4.2、引用HAR的ArkUI组件 5、循环渲染6、状态管理最… 目录 1、UIAbility的冷启动和UIAbility热启动2、静态资源和动态资源的访问3、页面跳转3.1、页面返回跳转 4、HAR的ArkUI组件、接口、资源供其他应用或当前应用的其他模块引用4.1、导出HAR的ArkUI组件4.2、引用HAR的ArkUI组件 5、循环渲染6、状态管理最佳实践7、AppStorage的使用精细化拆分复杂状态 1、UIAbility的冷启动和UIAbility热启动
UIAbility冷启动指的是UIAbility实例处于完全关闭状态下被启动这需要完整地加载和初始化UIAbility实例的代码、资源等。 目标UIAbility冷启动时在目标UIAbility的onCreate()生命周期回调中接收调用方传过来的参数。然后在目标UIAbility的onWindowStageCreate()生命周期回调中解析调用方传递过来的want参数获取到需要加载的页面信息url传入windowStage.loadContent()方法。 import { AbilityConstant, Want, UIAbility } from kit.AbilityKit;
import { hilog } from kit.PerformanceAnalysisKit;
import { window, UIContext } from kit.ArkUI;const DOMAIN_NUMBER: number 0xFF00;
const TAG: string [EntryAbility];export default class EntryAbility extends UIAbility {funcAbilityWant: Want | undefined undefined;uiContext: UIContext | undefined undefined;onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {// 接收调用方UIAbility传过来的参数this.funcAbilityWant want;}onWindowStageCreate(windowStage: window.WindowStage): void {// Main window is created, set main page for this abilityhilog.info(DOMAIN_NUMBER, TAG, %{public}s, Ability onWindowStageCreate);// Main window is created, set main page for this abilitylet url pages/Index;if (this.funcAbilityWant?.parameters?.router this.funcAbilityWant.parameters.router funcA) {url pages/Page_ColdStartUp;}windowStage.loadContent(url, (err, data) {// ...});}
}UIAbility热启动指的是UIAbility实例已经启动并在前台运行过由于某些原因切换到后台再次启动该UIAbility实例这种情况下可以快速恢复UIAbility实例的状态。 在应用开发中会遇到目标UIAbility实例之前已经启动过的场景这时再次启动目标UIAbility时不会重新走初始化逻辑只会直接触发onNewWant()生命周期方法。为了实现跳转到指定页面需要在onNewWant()中解析参数进行处理。 例如短信应用和联系人应用配合使用的场景。 1、用户先打开短信应用短信应用的UIAbility实例启动显示短信应用的主页。 2、用户将设备回到桌面界面短信应用进入后台运行状态。 3、用户打开联系人应用找到联系人张三。 4、用户点击联系人张三的短信按钮会重新启动短信应用的UIAbility实例。 5、由于短信应用的UIAbility实例已经启动过了此时会触发该UIAbility的onNewWant()回调而不会再走onCreate()和onWindowStageCreate()等初始化逻辑。 import { AbilityConstant, Want, UIAbility } from kit.AbilityKit;
import { hilog } from kit.PerformanceAnalysisKit;
import type { Router, UIContext } from kit.ArkUI;
import type { BusinessError } from kit.BasicServicesKit;const DOMAIN_NUMBER: number 0xFF00;
const TAG: string [EntryAbility];export default class EntryAbility extends UIAbility {funcAbilityWant: Want | undefined undefined;uiContext: UIContext | undefined undefined;// ...onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {if (want?.parameters?.router want.parameters.router funcA) {let funcAUrl pages/Page_HotStartUp;if (this.uiContext) {let router: Router this.uiContext.getRouter();router.pushUrl({url: funcAUrl}).catch((err: BusinessError) {hilog.error(DOMAIN_NUMBER, TAG, Failed to push url. Code is ${err.code}, message is ${err.message});});}}}
}热启动是前提得有冷启动就是应用启动后处于后台运行当从其他位置点击进来需要进入后台运行的应用程序时就需要使用到热启动。
2、静态资源和动态资源的访问 在组件中经常需要使用字符串、图片等资源。HSP中的组件需要使用资源时一般将其所用资源放在HSP包内而非放在HSP的使用方处以符合高内聚低耦合的原则。 在工程中常通过 r / r/ r/rawfile的形式引用应用资源。可以用 r / r/ r/rawfile访问本模块resources目录下的资源如访问resources目录下定义的图片src/main/resources/base/media/example.png时可以用$r(“app.media.example”) // library/src/main/ets/pages/Index.ets
// 正确用例
Image($r(app.media.example)).id(example).borderRadius(48px)
// 错误用例
Image(../../resources/base/media/example.png).id(example).borderRadius(48px)将需要对外提供的资源封装为一个资源管理类
// library/src/main/ets/ResManager.ets
export class ResManager{static getPic(): Resource{return $r(app.media.pic);}static getDesc(): Resource{return $r(app.string.shared_desc);}
}对外暴露的接口需要在入口文件index.ets中声明
// library/index.ets
export { ResManager } from ./src/main/ets/ResManager;3、页面跳转 其中router.pushUrl方法的入参中url的内容为
bundle:com.samples.hspsample/library/ets/pages/Menuurl内容的模板为
bundle:包名bundleName/模块名moduleName/路径/页面所在的文件名(不加.ets后缀)3.1、页面返回跳转 页面返回router.back方法的入参中url说明
如果从HSP页面返回HAP页面url的内容为
pages/Indexurl内容的模板为
页面所在的文件名(不加.ets后缀)如果从HSP1的页面跳到HSP2的页面后需要返回到HSP1的页面url的内容为
bundle:com.samples.hspsample/library/ets/pages/Menuurl内容的模板为
bundle:包名bundleName/模块名moduleName/路径/页面所在的文件名(不加.ets后缀)4、HAR的ArkUI组件、接口、资源供其他应用或当前应用的其他模块引用
4.1、导出HAR的ArkUI组件
Index.ets文件是HAR导出声明文件的入口HAR需要导出的接口统一在Index.ets文件中导出。Index.ets文件是DevEco Studio默认自动生成的用户也可以自定义在模块的oh-package.json5文件中的main字段配置入口声明文件配置如下所示
{main: Index.ets
}导出ArkUI组件 ArkUI组件的导出方式与ts的导出方式一致通过export导出ArkUI组件示例如下
// library/src/main/ets/components/mainpage/MainPage.ets
Component
export struct MainPage {State message: string HAR MainPage;build() {Column() {Row() {Text(this.message).fontSize(32).fontWeight(FontWeight.Bold)}.margin({ top: 32px }).height(56).width(624px)Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center, alignContent: FlexAlign.Center }) {Column() {Image($r(app.media.pic_empty)).width(33%)Text($r(app.string.empty)).fontSize(14).fontColor($r(app.color.text_color))}}.width(100%).height(90%)}.width(100%).height(100%).backgroundColor($r(app.color.page_background))}
}HAR对外暴露的接口在Index.ets导出文件中声明如下所示
// library/Index.ets
export { MainPage } from ./src/main/ets/components/mainpage/MainPage;4.2、引用HAR的ArkUI组件
HAR的依赖配置成功后可以引用HAR的ArkUI组件。ArkUI组件的导入方式与ts的导入方式一致通过import引入HAR导出的ArkUI组件示例如下所示
// entry/src/main/ets/pages/IndexSec.ets
import { MainPage } from library;Entry
Component
struct IndexSec {build() {Row() {// 引用HAR的ArkUI组件MainPage()}.height(100%)}
}5、循环渲染
Entry
Component
struct Parent {State simpleList: Arraystring [one, two, three];build() {Row() {Column() {ForEach(this.simpleList, (item: string) {ChildItem({ item: item })}, (item: string) item)}.width(100%).height(100%)}.height(100%).backgroundColor(0xF1F3F5)}
}Component
struct ChildItem {Prop item: string;build() {Text(this.item).fontSize(50)}
}运行效果如图所示 此次是循环遍历数据通过Prop监听父组件中的值进行字体大小为50的逻辑处理。
6、状态管理最佳实践
Observed
class Translate {translateX: number 20;
}Entry
Component
struct UnnecessaryState1 {State translateObj: Translate new Translate(); // 同时存在读写操作并关联了Button组件推荐使用状态变量buttonMsg I am button; // 仅读取变量buttonMsg的值没有任何写的操作直接使用一般变量即可build() {Column() {Button(this.buttonMsg).onClick(() {animateTo({duration: 50}, () {this.translateObj.translateX (this.translateObj.translateX 50) % 150; // 点击时给变量translateObj重新赋值})})}.translate({x: this.translateObj.translateX // 读取translateObj中的值})}
}没有关联任何UI组件的状态变量和没有修改过的状态变量不应该定义为状态变量直接使用一般变量即可否则会影响性能。 7、AppStorage的使用精细化拆分复杂状态
对AppStorage的使用以“HMOS世界App”中共享用户信息和用户收藏信息为例描述如何拆分状态存储。用户信息和用户收藏信息涉及的模块和界面展示如下
“我的”模块顶部有展示用户信息的组件“UserInfoView”底部有展示用户收藏列表列表卡片上需要高亮展示用户是否点赞了当前文章。“探索”模块首页展示技术文章列表列表卡片上同样需要展示用户是否点赞了当前文章。当两个模块中任一模块的卡片有点赞交互时需要同步用户是否对文章点赞的状态给另一个模块。
当前项目中已经使用AppStorage存储用户信息UserDataUserData的数据结构和“UserInfoView”组件使用UserData状态展示用户信息的代码如下
//用户信息UserData的数据结构
export interface UserData {id: string;username: string;description: string;// ...
}//在业务类中获取服务端用户信息
getUserData(): void {this.userAccountRepository.getUserData().then((data: UserData) {//1.将用户信息数据存储到AppStorage中AppStorage.setOrCreate(userData, data);})
}//“我的”模块顶部展示用户信息的视图组件
Component
struct UserInfoView {//2.在UI中使用StorageLink装饰器接收AppStorage中存储的用户信息StorageLink(userData) userData: UserData | null null;build() {Column() {Row({ space: Constants.MIDDLE_SPACE }) {// ...Column() {//3.展示用户信息userData中的用户名Text(this.userData? this.userData.username : $r(app.string.default_login))// ...}}// ...}// ...}
}现在“探索“模块和“我的“模块需要共享用户的收藏列表信息只需要共享收藏的文章id数组即可。不同模块间的状态共享考虑将其也存储在AppStorage中有如下两种存储方案 收藏信息也是用户信息的一部分将收藏信息作为用户信息userData的一个属性存储在当前AppStorage里key值为“userData”的变量上。 收藏信息单独存入AppStorage中不与用户信息userData绑定。
第一种方案的代码实现如下
export interface UserData {id: string;username: string;description: string;// 1. 在用户信息UserData上增加用户收藏的资源列表id信息类型定义collectedIds: string[];// ...
}//在业务类中获取服务端用户信息
getUserData(): void {this.userAccountRepository.getUserData().then((data: UserData) {//2.将用户信息数据存储到AppStorage中AppStorage.setOrCreate(userData, data);})
}// 探索模块的文章卡片组件
Component
export struct ArticleCardView {//3.在探索文章列表卡片上通过StorageLink装饰器获取用户信息对象userDataStorageLink(userData) userData: UserData | null null;Prop articleItem: LearningResource new LearningResource();//4.根据收藏信息数组计算当前文章是否被收藏isCollected(): boolean {return this.userData this.userData.collectedIds.some((id: string) id this.articleItem.id);}//7.处理界面点赞交互逻辑使用StorageLink装饰器接收的userData状态子属性collectedIds被修改后新值会同步到AppStorage中handleCollected(): void {const index this.userData?.collectedIds.findIndex((id: string) id this.articleItem.id);if (index -1) {this.userData?.collectedIds.push(resourceId);} else {this.userData?.collectedIds.splice(index, 1);}// ...}build(){ActionButtonView({//5.根据当前文章是否被用户收藏判断收藏图标是否高亮imgResource: this.isCollected() ? $r(app.media.btn_favorites_on) : $r(app.media.btn_favorites_normal),count: this.articleItem.collectionCount,textWidth: $r(app.float.star_icon_width)}).onClick(() {//6.用户点击收藏图标时调用处理收藏状态修改的函数this.handleCollected();})}
}这种实现方案下当用户在“UserInfoView ”组件上重新修改用户描述信息userData.description属性值时属性值变化将同步回AppStorage中。ArkUI监听到AppStorage中key值为“userData”的值变化随后通知所有使用了AppStorage中key值为“userData”的组件重新渲染。
在上述界面中“我的“模块中展示用户信息的组件“UserInfoView ”会重新渲染。由于“探索”模块的文章卡片组件ArticleCardView 通过StorageLink装饰器绑定了AppStorage中key值为“userData”的变量所有的文章卡片组件也都会重新渲染。而这些组件与用户描述信息无关不应该被描述信息的修改变化影响从而导致渲染刷新。
改为使用上述第二种方案实现代码如下
//在业务类中获取用户信息
getUserData(): void {this.userAccountRepository.getUserData().then((data: UserData) {//1.将用户收藏信息单独数据存储到AppStorage中AppStorage.setOrCreate(collectedIds, data.collectedIds);AppStorage.setOrCreate(userData, data);})
}// 探索模块的文章卡片组件
Component
export struct ArticleCardView {//2.通过StorageLink装饰器获取AppStorage中存储的收藏信息StorageLink(collectedIds) collectedIds: string[] [];Prop articleItem: LearningResource new LearningResource();//3.根据收藏信息数组计算当前文章是否被收藏 isCollected(): boolean {return this.collectedIds.some((id: string) id this.articleItem.id);}//6.处理界面点赞交互逻辑使用StorageLink装饰器接收的状态collectedIds被修改后新值会同步到AppStorage中handleCollected(): void {const index this.collectedIds.findIndex((id: string) id this.articleItem.id);if (index -1) {this.collectedIds.push(resourceId);} else {this.collectedIds.splice(index, 1);}// ...}build(){ActionButtonView({//4.根据当前文章是否被用户收藏判断收藏图标是否高亮imgResource: this.isCollected() ? $r(app.media.btn_favorites_on) : $r(app.media.btn_favorites_normal),count: this.articleItem.collectionCount,textWidth: $r(app.float.star_icon_width)}).onClick(() {//5.用户点击收藏图标时调用处理收藏状态修改的函数this.handleCollected();})}
}在此方案中由于文章卡片组件没有绑定AppStorage中key值为“userData”的变量当用户编辑修改了用户描述userData.description的值时 文章卡片组件不会重新渲染。
并且当用户点击文章卡片上的收藏按钮修改文章收藏状态时变化同步回AppStorage中的key值为“collectedIds”的变量。ArkUI监听到AppStorage中key值为“collectedIds”的值变化只会通知所有绑定了AppStorage中key值为“collectedIds”变量的组件重新渲染不会造成“我的“模块用户信息组件“UserInfoView ”重新渲染。
因此从性能的角度考虑在使用LocalStorage或AppStorage装饰器存储状态变量时需要合理设计状态的数据结构避免无意义的渲染刷新。
说明 过分追求状态结构拆分可能在某些场景导致组件设计过度不利于维护。此时可以将对象或类上经常一起改变的几个属性聚合成一个新的对象或类模型并使用Observed装饰器修饰再作为属性挂载到之前的对象或类上。通过此方法当属性变化时ArkUI只会通知变化给新的对象或类不会通知最上层的对象。这样既可以有效的减少无用渲染次数又能使代码更好维护。
如类ClassA上存在属性b、c、d。其中c和d经常一起发生变化即当c的状态修改时同时也要修改d的状态。
class ClassA{b: string;c: number;d: boolean;
}此时将c和d组合在一起做为新的类ClassE的属性并使用Observed装饰器修饰。对于ClassA去掉c、d属性新增属性e且其类型为ClassE设计如下
class ClassA{b: string;e: ClassE;
}Observed
class ClassE{c: number;d: boolean;
}使用此方案在AppStorage中存入数据结构为ClassA的变量。当ClassA实例的属性e中的属性c的值变化时状态变化会通知使用ClassE实例的组件重新渲染不会通知所有使用AppStorage中ClassA实例的组件更新即只使用了ClassA实例b属性的组件不会重新渲染。
总结 上述状态变量案例也可以使用此方法去实现防止资源的浪费使用Observed修饰一个新类里面包含collectedIds当collectedIds发生改变只会刷新新类而不会刷新UserData从而避免了资源的浪费。
伪代码如下
export interface UserData {id: string;username: string;description: string;e: ClassE;Observed
class ClassE{// 1. 在用户信息UserData上增加用户收藏的资源列表id信息类型定义collectedIds: string[];// ...
}
}