鸿蒙ArkTS 一次开发 多端部署 三层架构 LazyForEach懒加载 Account Kit华为一键登录

一、页面级一多

1.自适应布局

  • flex自动换行wrap(FlexWrap)、layoutWeight、justifyContext(FlexAlign)、alignItems(VerticalAlign)、displayPriority显示优先级
  • flexGrow(1)一般放在中间 flexShrink(1)Flex时默认1 拉伸能力指的是容器尺寸发生变化时:将变化的空间,分配给容器内的【指定区域】
  • aspectRatio = width/height 等等这些都属于自适应布局

2.响应式布局(重点)

① 全局断点

typescript 复制代码
export default class EntryAbility extends UIAbility {
  private curBp: string = ''
  // 目标页面
  private targetPage: string = ''

  // 根据当前窗口尺寸更新断点
  private updateBreakpoint(windowWidth: number): void {
    // 将长度的单位由px换算为vp
    let windowWidthVp = px2vp(windowWidth)
    let newBp: string = ''
    if (windowWidthVp < 320) {
      newBp = BreakpointConstants.XS
    } else if (windowWidthVp < 600) {
      newBp = BreakpointConstants.SM
    } else if (windowWidthVp < 840) {
      newBp = BreakpointConstants.MD
    } else {
      newBp = BreakpointConstants.LG
    }
    if (this.curBp !== newBp) {
      this.curBp = newBp
      // 使用状态变量记录当前断点值
      AppStorage.setOrCreate(BreakpointConstants.BREAK_POINT_KEY, this.curBp)
    }
  }
...
onWindowStageCreate(windowStage: window.WindowStage): void {
  // Main window is created, set main page for this ability
  hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onWindowStageCreate');
  windowStage.getMainWindow()
    .then((windowObj) => {
      // 获取应用启动时的窗口尺寸
      this.updateBreakpoint(windowObj.getWindowProperties()
        .windowRect
        .width)
      // 注册回调函数,监听窗口尺寸变化
      windowObj.on('windowSizeChange', (windowSize) => {
        this.updateBreakpoint(windowSize.width)
      })
    });
  • 核心:在onWindowStageCreate中监听windowSizeChange窗口变化 windowStage.getMainWindow().then( windowObj.on('w..',s=>..s.width)

② 媒体查询

javascript 复制代码
const XS:mediaquery.MediaQueryListener = mediaquery.matchMediaSync('(0vp<=width<320vp)')
const SM:mediaquery.MediaQueryListener = mediaquery.matchMediaSync('(320vp<=width<600vp)')

aboutToAppear(){
XS.on('change',(res:mediaquery.MediaQueryResult)=>{
//打印res,执行逻辑
})
SM.on('change',(res:mediaquery.MediaQueryResult)=>{
//打印res,执行逻辑
})
}

aboutToDisappear(){
//移除监听 避免性能浪费
XS.off('change')
SM.off('change')
}

③ 栅格布局

GridRow({columns:{sm:4...}})列数{ForEach..GridCol({span:2})}占2列

④横竖屏

"orientation":"auto_rotation_unspecified" //受开关控制的自动旋转模式src/main/module.json5

二、功能级一多

  • 方法一:使用canIUse接口判断设备是否支持某系统能力
scss 复制代码
if(canIUse(系统能力)){
//正常调用
}
  • 方法二:通过import动态导入,配合try/catch
javascript 复制代码
import controller from '@kit.ConnectivityKit'
try{
controller.enableNfc()
}catch(busiError){
//打印
}

三、工程级一多

重点 : 若一个 HAR(2MB)被 3 个 HAP 引用,总冗余体积为 2MB × 3 = 6MB 。 若项目中存在 10 个此类 HAR,冗余体积可达 20MB ,占整体包体积的 10%-20%。所以使用 HSP(动态共享包)替代 HAR,将静态拷贝改为动态加载,冗余体积减少 80%-90%。(除非仓库需要HAR包)

四、LazyForEach

1. 首先我们测试一下LazyForEach和ForEach在表现上有哪些区别

    1. forEach 把数据全部都渲染出来,用户看不见的可视区域之外 也会渲染 数据量多 卡 内存占用大
    1. LazyForEach 懒加载 , 只会渲染 可视区域之内的组件,之外不会渲染
    1. 使用性能调试工具 profiler 内存监听 工具 数据76条的时候 内存占用 1.2G 卡
    1. LazyForEach 数据76条 内存占用 300M 流畅

2. 封装IDataSource接口实例

kotlin 复制代码
export  class  LazyForEachDataSource <T> implements  IDataSource {  
private  listeners : DataChangeListener [] = [];  
// 数据源   
private  dataArray : T[] = [];   

// 返回数据的长度   
public  totalCount (): number {  
return  this . dataArray . length ; }   

// 返回数组指定指定索引的数据   
public  getData ( index: number ) {  
return  this . dataArray [index]; }   

// 给数组增加一条数据   
public  pushData ( data : T): void {  
this . dataArray . push (data);  this . notifyDataAdd ( this . dataArray . length - 1 ); }   

// 批量设置数组  
public  setArray ( arr: T[] ) {  
this . dataArray = arr  //   通知UI更新   
this . notifyDataReload () }   

  
// 该方法为框架侧调用,为LazyForEach组件向其数据源处添加listener监听   
registerDataChangeListener ( listener : DataChangeListener ): void {  
if ( this . listeners . indexOf (listener) < 0 ) {  
console . info ( 'add listener' );  
this . listeners . push (listener); } }   


// 该方法为框架侧调用,为对应的LazyForEach组件在数据源处去除listener监听   
unregisterDataChangeListener ( listener : DataChangeListener ): void {  
const pos = this . listeners . indexOf (listener);  
if (pos >= 0 ) {  console . info ( 'remove listener' );  
this . listeners . splice (pos, 1 ); } }   


// 数据改变,  通知UI更新 所有数据   
notifyDataReload (): void {  
this . listeners . forEach ( listener => { listener. onDataReloaded (); }) }   

// 添加数据了 顺便帮你更新   
notifyDataAdd ( index : number ): void {  
this . listeners . forEach ( listener => { listener. onDataAdd (index);  
// 写法2:listener.onDatasetChange([{type: DataOperationType.ADD, index: index}]);  }) }   

// 通知LazyForEach组件在index对应索引处数据有变化,需要重建该子组件   
notifyDataChange ( index : number ): void {  
this . listeners . forEach ( listener => { listener. onDataChange (index);  
// 写法2:listener.onDatasetChange([{type: DataOperationType.CHANGE, index: index}]);  }) }   

// 通知LazyForEach组件需要在index对应索引处删除该子组件   
notifyDataDelete ( index : number ): void {  
this . listeners . forEach ( listener => { listener. onDataDelete (index);  
// 写法2:listener.onDatasetChange([{type: DataOperationType.DELETE, index: index}]);  }) }   

// 通知LazyForEach组件将from索引和to索引处的子组件进行交换   
notifyDataMove ( from : number , to : number ): void {  
this . listeners . forEach ( listener => { listener. onDataMove ( from , to);  
// 写法2:listener.onDatasetChange(   
// [{type: DataOperationType.EXCHANGE, index: {start: from, end: to}}]);  }) }   

notifyDatasetChange ( operations : DataOperation []): void {  this . listeners . forEach ( listener => { listener. onDatasetChange (operations); }) } } 
  • 然后直接使用瀑布流渲染
typescript 复制代码
RecommendData:LazyForEachDataSource<item> = new LazyForEachDataSource
...
//通过接口获得数据res
this.RecommendData.setArray(res)
...

WaterFlow(){
LazyForEach(this.RecommendData,(item:item,index:number)=>{
FlowItem(){
//放自定义组件
}},(item:item,index:number)=>item.id)
}
.cachedCount(6)

3. 实现原理:DataChangeListener数据变化监听器

    1. LazyForEach 接收数据源 不再是普通的数组, 而是一个 继承了IDataSource接口的实例
    1. 真正数据/数组 存放在 实例的成员上
    1. 除此之外还重要,还有很重要的成员 - 数据变化监听器 DataChangeListener
    1. 要操作数据的时候 直接操作真正的数据源 -> 主动调用 数据变化监听器的一些方法(add/delete/move

五、Account Kit华为一键登录

1. 四签名、添加公钥指纹、Client ID、scope权限申请

  • 这个需要了解的可以查看我之前的文章,有详细说明
  • scope权限申请,如下图所示

2. 实现核心流程图

    1. 配置证书及权限
    1. 调用华为登录的 api 拉起登录界面
    1. 获取登录成功之后的 code(码)
    1. 提交给应用的后端接口
    1. 后端会将 code 提交给【华为服务器】验真
    1. 成功返回用户信息
    1. 本地拿到用户信息保存即可

3. 封装华为登录,核心API:authentication

javascript 复制代码
import { authentication } from '@kit.AccountKit'


class HuaweiAuthPlugin {
  async requestAuth() {
    // 1. 创建一个Account Kit授权请求对象,可通过返回值设置请求参数。
    const huaweiIdProvider = new authentication.HuaweiIDProvider()
    const authCreateRequest = huaweiIdProvider.createAuthorizationWithHuaweiIDRequest()
    // 2. 添加请求参数
    authCreateRequest.scopes = ['phone']
    authCreateRequest.permissions = ['serviceauthcode']
    authCreateRequest.forceAuthorization = true
    // 3. 执行授权请求,获取认证码
    const authController = new authentication.AuthenticationController(getContext())
    const authResponse: authentication.AuthorizationWithHuaweiIDResponse =
      await authController.executeRequest(authCreateRequest)
    const serviceauthcode = authResponse.data?.authorizationCode
    return serviceauthcode
  }

  async cancelAuth() {
    try {
      // 1. 创建一个Account Kit授权请求对象,可通过返回值设置请求参数。
      const huaweiIdProvider = new authentication.HuaweiIDProvider()
      const authCancelRequest = huaweiIdProvider.createCancelAuthorizationRequest()
      // 2. 取消授权
      const authController = new authentication.AuthenticationController(getContext())
      await authController.executeRequest(authCancelRequest)
      return true
    } catch (e) {
      console.log('mk-logger', JSON.stringify(e))
      return false
    }
  }
}

export const huaweiAuthPlugin = new HuaweiAuthPlugin()

4.将code、clientID、clientSecret传给后端

  • 等服务器向华为服务器核实信息返回参数后登录
  • 这里的Auth是我封装的一个token管理工具,使用的是持久化存储
相关推荐
AI浩3 小时前
【Labelme数据操作】LabelMe标注批量复制工具 - 完整教程
运维·服务器·前端
涔溪3 小时前
CSS 网格布局(Grid Layout)核心概念、基础语法、常用属性、实战示例和进阶技巧全面讲解
前端·css
2401_878454534 小时前
浏览器工作原理
前端·javascript
西陵4 小时前
为什么说 AI 赋能前端开发,已经不是选择题,而是必然趋势?
前端·架构·ai编程
by__csdn5 小时前
Vue3 setup()函数终极攻略:从入门到精通
开发语言·前端·javascript·vue.js·性能优化·typescript·ecmascript
天天扭码5 小时前
前端如何实现RAG?一文带你速通,使用RAG实现长期记忆
前端·node.js·ai编程
Luna-player6 小时前
在前端中,<a> 标签的 href=“javascript:;“ 这个是什么意思
开发语言·前端·javascript
lionliu05196 小时前
js的扩展运算符的理解
前端·javascript·vue.js
小草cys6 小时前
项目7-七彩天气app任务7.4.2“关于”弹窗
开发语言·前端·javascript
奇舞精选6 小时前
GELab-Zero 技术解析:当豆包联手中兴,开源界如何守住端侧 AI 的“最后防线”?
前端·aigc