HarmonyOS ArkUI基础学习02

以下所有代码涉及的源码地址: https://gitee.com/jiangqianghua/harmony-test
点我获取更多it学习资源

1. Index.ets 文件介绍

@Entry // 入口文件
@Component  // 组件
struct Index {
  // @State 让普遍变量有状态
  @State message: string = 'Hello World111111'

  // build 特点
  // 1. 根节点只有一个, 根节点必须是容器 2 不能声明本地变量  3 不允许console.info,4 不允许调用没有用@Builder装饰的方法  
  // 5. 不允许switch,使用if代替, 6 不允许表达式,比如三目运算
  build() {
    Row() {
      Column() {
        Text(this.message)
          .fontSize(50)
          .fontWeight(FontWeight.Bold)
          .backgroundColor(Color.Red)
          .border({width: 5, color: Color.Black, style: BorderStyle.Dotted})
          .margin(10)
          .padding(10)
        .borderRadius(20)
        if (false) Divider()
        Button('click me')
          // .onClick(() => {this.message = 'click me'})
          // .onClick(function(){
          //   this.message = 'click me'
          // }.bind(this))
          // .onClick(this.changeMessage)
          .onClick(this.changeMessage2.bind(this))
      }
      .width('100%')
    }
    .height('100%')
  }

  changeMessage = () => {
    this.message = 'click me'
  }

  changeMessage2(){
    this.message = 'click me'
  }
}

2. 自定义构建函数

@Entry
@Component
struct Page2 {
  @State message: string = 'Hello World'

  build() {
    Row() {
      Column() {
        this.TextLabel()
        TextLabel2()
      }
      .width('100%')
    }
    .height('100%')
  }

  // 内部构建组件函数
  @Builder
  TextLabel() {
    Text(this.message)
      .fontSize(30)
      .backgroundColor(Color.Red)
      .onClick(() => { this.message = 'click me'})
  }
}

// 外部构建组件函数, 也可以使用this,但是不建议,使用传参方式
@Builder
function TextLabel2() {
  Text(this.message)
    .fontSize(30)
    .backgroundColor(Color.Red)
    .onClick(() => { this.message = 'click me'})
}

3. 便宜的全局组件传参数方法和组件回调

@Entry
@Component
struct Page3 {
  @State message: string = 'Hello World'

  build() {
    Row() {
      Column() {
        // 这种值传递,状态发生变化,组件不会被修改
        TextLabel3(this.message)
        // 需要引用传递,被$$1接受,状态发生变化,组件会改变,单组件内无法修改该值
        // cb 是回调
        TextLabel4({message: this.message, cb: (value: string) => {
          console.log(value)
        }})
        Button('click').onClick(() => this.message = 'click')
      }
      .width('100%')
    }
    .height('100%')
  }
}
// 值传递
@Builder
function TextLabel3(message: string) {
  Text(message)
    .fontSize(50)
    .fontWeight(FontWeight.Bold)
}

// 引用传递
// $$1 可以任意取
@Builder
function TextLabel4($$1:{message: string, cb:(value: string)=>void}) {
  Text($$1.message)
    .fontSize(50)
    .fontWeight(FontWeight.Bold)
    .onClick(() => {
      $$1.cb('text click')
    })
}

4. @Styles 便宜的定义通用属性, 不支持传参

// 提取样式
@Entry
@Component
struct Page4 {
  @State message: string = 'Hello World'

  build() {
    Row() {
      Column() {
        Text(this.message)
          .fontSize(20)
          .fontWeight(FontWeight.Bold)
          .commonStyle()
      }
      .width('100%')
    }
    .height('100%')
  }
}

// 全局定义样式, 只支持通用属性和事件, 比如 fontColor, 且不支持传参, 可以定义在组件内,也能定义在全局
@Styles
function commonStyle(){
  .width(200)
  .height(100)
  .margin(10)
  .padding(10)
  .backgroundColor(Color.Red)
  .border({width: 5, color: Color.Blue, style: BorderStyle.Solid})
  .borderRadius('20.00vp')
}

5. @Extend扩展组件样式

和@Styles区别

  1. @Extend仅支持全全局
  2. @Extend支持封装组件私有属性和私有事件和预定义相同的组件的@Extend的方法
  3. @Extend装饰的方法支持传参
  4. @Extend参数可以为状态变量,当状态变量发生变化,ui正常被刷新渲染
// 扩展组件样式
@Entry
@Component
struct Page5 {
  @State message: string = 'Hello World'
  @State color: Color = Color.Pink

  build() {
    Row() {
      Column() {
        Text(this.message)
          .MyText1(this.color, (value) => {
            console.log(value)
            this.color = Color.Green
          } )
      }
      .width('100%')
    }
    .height('100%')
  }
}

// 可以传参数以及回调函数, 参数是有状态的,会跟着外部变化而变化
@Extend(Text)
function MyText1(color: Color, cb: (value: string) => void ){
  .fontSize(20)
  .fontColor(color)
  .width(200)
  .height(100)
  .margin(10)
  .padding(10)
  .backgroundColor(Color.Red)
  .border({width: 5, color: Color.Blue, style: BorderStyle.Solid})
  .borderRadius('20.00vp')
  .onClick(() => {
    cb('click')
  })
}

5. 状态样式


@Entry
@Component
struct Page6 {
  @State message: string = 'Hello World'

  build() {
    Row() {
      Column() {
        Button('click')
          .width(100)
          .height(100)
          .onClick(() => {})
          // 组件内写法
          .stateStyles({
            normal: {
              .backgroundColor(Color.Green)
            },
            pressed: {
              .backgroundColor(Color.Red)
            },
            disabled: {
              .backgroundColor(Color.Grey)
            },
            // focused: {
            //   .backgroundColor(Color.Blue)
            // }
          })
        MyButton2()
      }
      .width('100%')
    }
    .height('100%')
  }
}
// 外部定义并调用状态样式
@Styles
function normalStyle(){
  .backgroundColor(Color.Green)
}

@Styles
function pressedStyle(){
  .backgroundColor(Color.Red)
}

@Styles
function disabledStyle(){
  .backgroundColor(Color.Grey)
}
@Builder
function MyButton2() {
  Button('mybtn')
    .width(100)
    .height(100)
    .stateStyles({
      normal: normalStyle,
      pressed: pressedStyle,
      disabled: disabledStyle
    })
}

6. forEach + if else

// forEach
// forEach
@Entry
@Component
struct Pager7 {
  @State message: string = 'Hello World'

  @State list: string[] = ['商品1', '商品2']

  @State list2: Object[] = [{
    id:1, title: "商品1"
  },{
    id:2, title: "商品2"
  },{
    id:3, title: "商品3"
  }]
  build() {
    Row() {
      Column() {
        // // forEach 会默认加上key值, 默认是 index + JSON.stringify(item)
        // ForEach(this.list,(item: string) => {
        //   Text(item).fontSize(30)
        // })
        // ForEach(this.list,(item: string, index: number) => {
        //   Text(item).fontSize(30)
        //   // 人为返回一个key, 不推荐, list改变,如果发现index是一样的,不会做改变, 可以使用商品id
        // }, (item: string, index: number) => index.toString())

        ForEach(this.list2, (item: Object) => {
          Text(item['title']).fontSize(30)
        })
        Button('click').onClick(() => {
          // 该方式无法修改成功, 不会引发ui渲染, 使用@ObjectLink解决
          // this.list2[0]['title'] = '商品1-修改'
          // 另外一个方式
          this.list2.splice(0, 1, {
            id: 1,
            title: '商品1-修改'
          })
        })

        // 使用List组件
        if (this.list2.length) {
          List() {
            ForEach(this.list2, (item: Object) => {
              ListItem() {
                Text(item['title']).fontSize(30)
              }
            })
          }.height(100).divider({
            strokeWidth: 1,
            startMargin: 10,
            endMargin: 10,
            color: Color.Grey
          })
        } else {
          Text('空空如也')
        }

      }
      .width('100%')
    }
    .height('100%')
  }
}

7. 自定义组件

@Entry
@Component
struct Page9 {
  @State message: string = 'Hello World'

  build() {
    Row() {
      Column() {
        MyComponent({title: this.message, delClick: () => {
          console.log('delClick call')
        }})
        Button('click').onClick(() => {
          // 修改后,内部ui不会渲染
          this.message = 'click'
        })
      }
      .width('100%')
    }
    .height('100%')
  }
}

@Component
struct MyComponent{
  // 自动接受
  private title: string ;

  public delClick = (event: ClickEvent) => {
  }
  build(){
    Row(){
      Text(this.title)
      Button('delClick').onClick((event: ClickEvent) => this.delClick(event))
    }
  }
}

8. @State组件内状态

  1. 当类型是boolean, string, number, 可以观察到变化
  2. 类型是class 或则Object时候,可以观察到自身赋值的变化,和属性赋值的变化,但是嵌套属性赋值变化观察不到
  3. 类型是array时, 可以观察到数组本省的赋值和添加,删除,更新数组的变化
ForEach(this.list.filter(item=>item.includes(this.value)), (item) => {
  Text(item).fontSize(30)
})

9. 父子单项同步@Prop

  1. @Prop变量允许本地修改,但是修改后不会同步给父
  2. 当父组件更新数据源,与之关联的@Prop也会自动个更新,父组件会优先覆盖子组件修改的值
  3. 允许变量 string, number, Boolean,enum类型, 如果要传对象,请使用@Link方式
@Entry
@Component
struct Page9 {
  @State message: string = 'Hello World'

  build() {
    Row() {
      Column() {
        MyComponent({title: this.message, delClick: () => {
          console.log('delClick call')
        }})
        Button('click').onClick(() => {
          // 修改后,内部ui不会渲染
          this.message = 'click'
        })
      }
      .width('100%')
    }
    .height('100%')
  }
}

@Component
struct MyComponent{
  // 自动接受
  @Prop title: string ;

  public delClick = (event: ClickEvent) => {
  }
  build(){
    Row(){
      Text(this.title)
      Button('delClick').onClick((event: ClickEvent) => this.delClick(event))
    }
  }
}

10. 父子双向同步@Link

  1. 装饰的变量有 Object, class, string, numberm boolean, enum类型,不支持any, 不支持简单类型和复杂的类型的联合类型,不允许undefined和null,
  2. 禁止本地初始化
@Entry
@Component
struct Page_Link {
  @State message: string = 'Hello World'

  build() {
    Row() {
      Column() {
        Text(this.message).fontColor(Color.Red)
        // $message 表示传this.message的引用
        MyComponent2({title: $message})
        Button('click').onClick(() => {
          // 修改后,内部ui不会渲染
          this.message = 'click'
        })
      }
      .width('100%')
    }
    .height('100%')
  }
}

@Component
struct MyComponent2{
  // 自动接受
  @Link title: string ;

  build(){
    Row(){
      Text(this.title)
      // 也会改变父类的值
      Button('change').onClick((event: ClickEvent) => this.title = 'change')
    }
  }
}
// @link 修饰对象形式
@Entry
@Component
struct Page_ObjectLink {
  @State message: string = 'Hello World'

  @State person: Person = new Person('jiang')
  build() {
    Row() {
      Column() {
        ShowPerson({ person: $person, cb: () => {
          console.log(this.person.name)
        }})
      }
      .width('100%')
    }
    .height('100%')
  }
}

class Person {
  name: string

  constructor(name: string) {
    this.name = name
  }
}

@Component
struct ShowPerson{
  @Link person: Person;
  private cb: () => void;
  build(){
    Row(){
      Text(this.person.name)
      Button('change').onClick(() => {
        this.person.name = 'change'
        this.cb()
      })
    }
  }
}

10. 监听嵌套类对象属性变@ObjectLink

  1. @ObjectLink 可以监听嵌套类对象的属性变化
  2. @ObjectLink 必须和@Observed一起使用
  3. @Observed 必须 new去创建对象
@Entry
@Component
struct Page_ObjectLink {
  @State message: string = 'Hello World'

  // 通过@Observed 必须通过new形式生成对象,否则无法监听
  @State list: Array<Person> = [new Person('jiang'), new Person('hua')]
  build() {
    Row() {
      Column() {
        ForEach(this.list, (item, index) => {
          ShowPerson({person: item, cb: () => {

          }})
        })
      }
      .width('100%')
    }
    .height('100%')
  }
}

@Observed
class Person {
  name: string

  constructor(name: string) {
    this.name = name
  }
}

@Component
struct ShowPerson{
  @ObjectLink person: Person;
  private cb: () => void;
  build(){
    Row(){
      Text(this.person.name)
      Button('change').onClick(() => {
        this.person.name = this.person.name + ' change'
        this.cb()
      })
    }
  }
}

11. 后代组件双向同步@Provide

@Entry
@Component
struct Page_provide {
  @Provide('msgkey') message: string = 'Hello World'
  build() {
    Row() {
      Column() {
        Text('root')
        Parent1()
      }
      .width('100%')
    }
    .height('100%')
  }
}

@Component
struct Parent1{
  @Consume('msgkey') msg: string;
  build(){
    Column(){
      Text('parent-' + this.msg)
      Child1()
    }
  }
}

@Component
struct Child1{
  @Consume('msgkey') msg1: string;
  build(){
    Row(){
      Text('child-' + this.msg1)
      // 改动,会触发全部所有地方更新
      Button('click').onClick(() => this.msg1 = 'change')
    }
  }
}

12. 状态变更更改通知 @Watch

  1. 建议@State @Prop @Link装饰再@Watch之前
  2. 不允许监听常规变量,可以监听@State, @Prop, @Link @ObjectLink, @Provide, @Consume, @StorageProp 以及@StorageProp
  3. 解决监听不到list的对象里面属性改变的问题 https://www.bilibili.com/video/BV1CC4y1K7Yk/?p=23&spm_id_from=pageDriver&vd_source=6ffada3620cd92b1ca1436b6ac6b18dd
@Entry
@Component
struct Page_watch {
  @State type: number = 1

  build() {
    Row() {
      Column() {
        Row(){
          Button('衣服').onClick(() => this.type = 1)
          Button('鞋子').onClick(() => this.type = 2)
        }
        ShowShop({ type: this.type })
      }
      .width('100%')
    }
    .height('100%')
  }
}

@Component
struct ShowShop{
  @Prop @Watch('typeChange') type: number
  build(){
    Column(){
      Text('当前展示的是' + this.type)
    }
  }

  typeChange(){
    // 可以监听type变化,然后加载数据
    console.log(this.type.toString())
  }
}

13. LocalStorage 页面共享状态

  1. 提供了两种方式
    1. @LocalStorageProp,和LocalStorage中给定的属性建立单项同步关系
    let storage = new LocalStorage({
  name: 'jiang'
})
@Entry(storage)
@Component
struct Page_Cinema {
  @State message: string = 'Hello World'
  // 在当前页面修改会触发当前ui同步
  @LocalStorageProp('name') myname: string = '';
  build() {
    Row() {
      Column() {
        Text(this.myname)
          .fontSize(50)
          .fontWeight(FontWeight.Bold).onClick(() => {
          // 以下两种写法,展现形式一样,但是处理过程不一样
          // 只会改变当前的值,child不会跟着边,也不会改变 storage的值
          //   this.myname = 'change'
          // 会改变和storage相关的所有值,包含当前的值和child值
            storage.set('name', 'change1')
            console.log(storage.get('name'))
          })
        Child2()
      }
      .width('100%')
    }
    .height('100%')
  }
}

@Component
struct Child2{
  @LocalStorageProp('name') myname1: string = ''
  build(){
    Column(){
      Text(this.myname1).onClick(() => {
        // 只会改变当前页面的
        this.myname1 = 'change2'
      })
    }
  }
}
  1. @LocalStorageLink,再@Component中创建与Localstorage中给定的属性建立双向同步关系
let storage = new LocalStorage({
  name: 'jiang'
})
@Entry(storage)
@Component
struct Page_Cinema {
  @State message: string = 'Hello World'
  // 在当前页面修改会触发当前ui同步
  @LocalStorageLink('name') myname: string = '';
  build() {
    Row() {
      Column() {
        Text(this.myname)
          .fontSize(50)
          .fontWeight(FontWeight.Bold).onClick(() => {
          // 以下两种写法等效
            this.myname = 'change'
          //   storage.set('name', 'change1')
            console.log(storage.get('name'))
          })
        Child2()
      }
      .width('100%')
    }
    .height('100%')
  }
}

@Component
struct Child2{
  @LocalStorageLink('name') myname1: string = ''
  build(){
    Column(){
      Text(this.myname1).onClick(() => {
        // 也会改变storage的值
        this.myname1 = 'change2'
      })
    }
  }
}

14. LocalStorage 页面跳转共享状态

EntryAbility.ts

import UIAbility from '@ohos.app.ability.UIAbility';
import hilog from '@ohos.hilog';
import window from '@ohos.window';

export default class EntryAbility extends UIAbility {
  storage = new LocalStorage({name: '北京'})
  ....

  onWindowStageCreate(windowStage: window.WindowStage) {
    ...
    windowStage.loadContent('pages/Page_Cinema', this.storage, (err, data) => {
     ...
    });
  }
}

Page_Cinema.etx

import router from '@ohos.router';
let storage = LocalStorage.GetShared()
@Entry(storage)
@Component
struct Page_Cinema {
  @State message: string = 'Hello World'
  // 在当前页面修改会触发当前ui同步
  @LocalStorageLink('name') myname: string = '';
  build() {
    Row() {
      Column() {
        Text(this.myname)
          .fontSize(50)
          .fontWeight(FontWeight.Bold).onClick(() => {
          // 以下两种写法等效
            this.myname = 'change'
          //   storage.set('name', 'change1')
            console.log(storage.get('name'))
          })
        Child2()
        Button('跳转到city').onClick(() => {
          router.pushUrl({
            url: "pages/Page_City"
          })
        })
      }
      .width('100%')
    }
    .height('100%')
  }
}

@Component
struct Child2{
  @LocalStorageLink('name') myname1: string = ''
  build(){
    Column(){
      Text(this.myname1).onClick(() => {
        // 也会改变storage的值
        this.myname1 = 'change2'
      })
    }
  }
}

Page_City.etc

import router from '@ohos.router'
let storage = LocalStorage.GetShared()
@Entry(storage)
@Component
struct Page_City {
  @State message: string = 'City'
  @LocalStorageLink('name') cityName: string = ''
  build() {
    Row() {
      Column() {
        Text(this.message)
          .fontSize(50)
          .fontWeight(FontWeight.Bold)
        Button('返回').onClick(() => {
          this.cityName = '南昌'
          router.back()
        })
      }
      .width('100%')
    }
    .height('100%')
  }
}

15. AppStorage 应用全局的UI状态存储, 不是存储在内存中

// 创建全局storage,在任何地方都可以创建
AppStorage.SetOrCreate('cityName', '北京')
// 使用的页面使用
@StorageProp('cityName') cityName: string = '';
@StorageLink('cityName') cityName: string = '';
// ui显示
Text(this.cityName)
// 修改
this.cityName = '上海'
AppStorage.Set('cityName', '上海')

15. 持久的ui存储状态 PersistentStorage

  1. 只能在ui页面中使用,否则无法持久化数据
  2. PersistentStorage 是在AppStorage再次封装,所以PersistentStorage能获取到AppStorage数据
  3. 只能存储number, string, boolean, enum等简单类型
  4. 变量最好小于2kb
 // 存储数据
 PersistentStorage.PersistProp('cityName', '北京')
 // 使用方式和AppStoreage一致
 // 使用的页面使用
  @StorageProp('cityName') cityName: string = '';
  @StorageLink('cityName') cityName: string = '';
  // ui显示
  Text(this.cityName)
  // 修改
  this.cityName = '上海'

16. 插槽 @BuilderParam

@Entry
@Component
struct Page_BuilderParam {
  @State message: string = 'Hello World'

  @Builder
  left(){
    Row(){
      Button('left')
    }
  }

  @Builder
  right(){
    Row(){
      Button('right')
    }
  }
  build() {
    Row() {
      Column() {
        R_NavBar({left: this.left, right: this.right})
        // 尾随闭包写法,该方式适合只有一个BuilderParam参数
        MyCom1(){
          Text('aaaa')
        }
      }
      .width('100%')
    }
    .height('100%')
  }
}

@Component
struct R_NavBar{
  @BuilderParam left:()=>void;
  @BuilderParam right:()=>void;
  build(){
    Row(){
      this.left()
      Text('title')
      this.right()
    }.justifyContent(FlexAlign.SpaceBetween).width('100%')
  }
}

@Component
struct MyCom1{
  @BuilderParam comp:() => void;
  build(){
    Row(){
      this.comp()
    }
  }
}

17. 组件声明周期

  1. aboutToAppear , 组件即将出现调用,在执行build之前调用,该函数一般要请求数据等
  2. onPageShow, 仅仅在Entry组件生效,每次显示触发一次
  3. onPageHiden, 仅仅在Entry组件生效,每次隐藏触发一次,包括路由过程,应用进入后台等
  4. onBackPress,仅仅在Entry组件生效,用户点击返回按钮触发
  5. aboutToDisappear, 组件销毁千执行,不允许在该函数修改状态变量, 一般处理销毁定时器,取消http请求等

18. 页面路由

  1. 两种跳转方式
    1. router.pushUrl({url: ‘pages/Page_Cinema’}, router.RouterMode.Single), 目标页会压入当前栈
    2. router.replaceUrl({url: ‘pages/Page_Cinema’}, router.RouterMode.Single ), 目标也会替换当前页,当前页会释放
  2. 两种跳转模式
    1. Standard, 默认模式,每次都会新建一个页面,压入栈
    2. Single, 单页面模式,如果页面已经存在,会把该页面移动到栈顶,替换掉当前页(页面-1),如果不存在,按照标准默认
  3. 使用场景
    1. pushUrl + Standard 模式 主页 -> 详情页
    2. replaceUrl + Standard模式, 登录页 -> 个人中心
    3. pushUrl + Single 设置页 -> 主题切换页
    4. replaceUrl + Single 搜索结果列表页 -> 搜索结果详情页
  4. 传参
// A页面
router.pushUrl({
  url: "pages/B",
  params: {
    name: 'jiang'
  }
})
// B页面接受
aboutToAppear(){
    const params = router.getParams();
    console.log('aboutToAppear', JSON.stringify(params))
}

// B页面返回
try {
  router.showAlertBeforeBackPage({
    message: '你确定要离开吗?'
  })
} catch (e) {
  
}
router.back({
  url: 'pages/Page_Cinema',
  params: {
    id: '1'
  }
})
// A页面接受
onPageShow(){
  let params = router.getParams();
  console.log(JSON.stringify(params))
}

19. 应用入口

  1. 应用生命周期
    1. onCreate,应用创建,该函数可以读取电量,系统配置信息等
    2. onWindowStageCreate, 主window创建,可以加载组件
    3. onForeground, 进入前台
    4. onBackground, 进入后台, 可以关闭一些不需要得时间,比如定位
    5. onWindowStageDestroy, 主window销毁,可以释放资源
  2. 多Ability跳转
    参考 https://www.bilibili.com/video/BV1CC4y1K7Yk?p=30&spm_id_from=pageDriver&vd_source=6ffada3620cd92b1ca1436b6ac6b18dd
  // A Ability
  private context = getContext(this) as common.UIAbilityContext;
  let want = {
    deviceId: '',
    bundleName: getContext(this.context).applicationInfo.name,
    abilityName: 'BAbility', // module.json5 配置好的名字
    parameters: {
      info: 'hello'
    }
    this.context.startAbility(want);
  }
  // BAbility.ets
   onCreate(want, launchParam) {
    console.log(want?.parameters?.info)
  }

20. http 请求


import http from '@ohos.net.http'
@Entry
@Component
struct Page_http {
  @State message: string = 'Hello World'
  private httpRequest = http.createHttp();
  build() {
    Row() {
      Column() {
        Button('http').onClick(() => {
          this.httpRequest.request("https:///www.baidu.com",
            {
              method: http.RequestMethod.GET, //默认是Get,
              connectTimeout: 60000,
              readTimeout: 60000,
              header: {
                // 'Content-Type': 'application/json',
                // 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
              }
            }, (err, data) => {
              if (!err) {
                console.log(JSON.stringify(data.result))
              }
            });
          // 可以中断
          this.httpRequest.destroy()
        })
      }
      .width('100%')
    }
    .height('100%')
  }
}

21. Preferences 存储数据

import dataPreferences from '@ohos.data.preferences'
...

// 入口用  this.context,   页面用 getContext(this)
    dataPreferences.getPreferences(this.context, 'mystore', (err, preferences) => {
      if (err) {
        console.error(`Failed to get Preferences, Code ${err.code}, message:${err.message}`)
        return
      }
      console.info('success in getting preferences.')
      preferences.put('username', 'xiaojiang', (err) => {
        if (err){
          console.error(`Failed to put the value of 'username',Code ${err.code}, message:${err.message}`)
          return;
        }
        console.info(`Successed in putting the value of 'username'`)
        preferences.flush((err) => {
          if (err) {
            return;
          }
          preferences.get('username', 'default', (err, val) => {
            if (err){
              return;
            }
            console.log(`Success in getting value of 'username', val: ${val}`)
          })
        })
      })
    })

你可能感兴趣的:(IT资源,harmonyos,学习,华为,前端)