HarmonyOS 應(yīng)用開發(fā)之自定義組件凍結(jié)功能

自定義組件處于非激活狀態(tài)時,狀態(tài)變量將不響應(yīng)更新呀伙,即@Watch不會調(diào)用梆暮,狀態(tài)變量關(guān)聯(lián)的節(jié)點不會刷新。通過freezeWhenInactive屬性來決定是否使用凍結(jié)功能,不傳參數(shù)時默認不使用。支持的場景有:頁面路由更卒,TabContent少梁,LazyforEach洛口,Navigation。

說明:

從API version 11開始凯沪,支持自定義組件凍結(jié)功能第焰。

當前支持的場景

頁面路由

  • 當頁面A調(diào)用router.pushUrl接口跳轉(zhuǎn)到頁面B時,頁面A為隱藏不可見狀態(tài)妨马,此時如果更新頁面A中的狀態(tài)變量挺举,不會觸發(fā)頁面A刷新。

  • 當應(yīng)用退到后臺運行時無法被凍結(jié)烘跺。

頁面A:

import router from '@ohos.router';

@Entry
@Component({ freezeWhenInactive: true })
struct FirstTest {
  @StorageLink('PropA') @Watch("first") storageLink: number = 47;

  first() {
    console.info("first page " + `${this.storageLink}`)
  }

  build() {
    Column() {
      Text(`From fist Page ${this.storageLink}`).fontSize(50)
      Button('first page storageLink + 1').fontSize(30)
        .onClick(() => {
          this.storageLink += 1
        })
      Button('go to next page').fontSize(30)
        .onClick(() => {
          router.pushUrl({ url: 'pages/second' })
        })
    }
  }
}

頁面B:

import router from '@ohos.router';

@Entry
@Component({ freezeWhenInactive: true })
struct SecondTest {
  @StorageLink('PropA') @Watch("second") storageLink2: number = 1;

  second() {
    console.info("second page: " + `${this.storageLink2}`)
  }

  build() {
    Column() {

      Text(`second Page ${this.storageLink2}`).fontSize(50)
      Button('Change Divider.strokeWidth')
        .onClick(() => {
          router.back()
        })

      Button('second page storageLink2 + 2').fontSize(30)
        .onClick(() => {
          this.storageLink2 += 2
        })

    }
  }
}

在上面的示例中:

1.點擊頁面A中的Button “first page storLink + 1”湘纵,storLink狀態(tài)變量改變,@Watch中注冊的方法first會被調(diào)用滤淳。

2.通過router.pushUrl({url: 'pages/second'})梧喷,跳轉(zhuǎn)到頁面B,頁面A隱藏脖咐,狀態(tài)由active變?yōu)閕nactive铺敌。

3.點擊頁面B中的Button “this.storLink2 += 2”,只回調(diào)頁面B@Watch中注冊的方法second屁擅,因為頁面A的狀態(tài)變量此時已被凍結(jié)偿凭。

4.點擊“back”,頁面B被銷毀派歌,頁面A的狀態(tài)由inactive變?yōu)閍ctive弯囊,重新刷新在inactive時被凍結(jié)的狀態(tài)變量,頁面A@Watch中注冊的方法first被再次調(diào)用硝皂。

TabContent

  • 對Tabs中當前不可見的TabContent進行凍結(jié),不會觸發(fā)組件的更新作谭。

  • 需要注意的是:在首次渲染的時候稽物,Tab只會創(chuàng)建當前正在顯示的TabContent,當切換全部的TabContent后折欠,TabContent才會被全部創(chuàng)建贝或。

@Entry
@Component
struct TabContentTest {
  @State @Watch("onMessageUpdated") message: number = 0;

  onMessageUpdated() {
    console.info(`TabContent message callback func ${this.message}`)
  }

  build() {
    Row() {
      Column() {
        Button('change message').onClick(() => {
          this.message++
        })

        Tabs() {
          TabContent() {
            FreezeChild({ message: this.message })
          }.tabBar('one')

          TabContent() {
            FreezeChild({ message: this.message })
          }.tabBar('two')
        }
      }
      .width('100%')
    }
    .height('100%')
  }
}

@Component({ freezeWhenInactive: true })
struct FreezeChild {
  @Link @Watch("onMessageUpdated") message: number
  private index: number = 0

  onMessageUpdated() {
    console.info(`FreezeChild message callback func ${this.message}, index: ${this.index}`)
  }

  build() {
    Text("message" + `${this.message}, index: ${this.index}`)
      .fontSize(50)
      .fontWeight(FontWeight.Bold)
  }
}

在上面的示例中:

1.點擊“change message”更改message的值,當前正在顯示的TabContent組件中的@Watch中注冊的方法onMessageUpdated被觸發(fā)锐秦。

2.點擊“two”切換到另外的TabContent咪奖,TabContent狀態(tài)由inactive變?yōu)閍ctive,對應(yīng)的@Watch中注冊的方法onMessageUpdated被觸發(fā)酱床。

3.再次點擊“change message”更改message的值羊赵,僅當前顯示的TabContent子組件中的@Watch中注冊的方法onMessageUpdated被觸發(fā)。

LazyforEach

  • 對LazyforEach中緩存的自定義組件進行凍結(jié),不會觸發(fā)組件的更新昧捷。
// Basic implementation of IDataSource to handle data listener
class BasicDataSource implements IDataSource {
  private listeners: DataChangeListener[] = [];
  private originDataArray: string[] = [];

  public totalCount(): number {
    return 0;
  }

  public getData(index: number): string {
    return this.originDataArray[index];
  }

  // 該方法為框架側(cè)調(diào)用闲昭,為LazyForEach組件向其數(shù)據(jù)源處添加listener監(jiān)聽
  registerDataChangeListener(listener: DataChangeListener): void {
    if (this.listeners.indexOf(listener) < 0) {
      console.info('add listener');
      this.listeners.push(listener);
    }
  }

  // 該方法為框架側(cè)調(diào)用,為對應(yīng)的LazyForEach組件在數(shù)據(jù)源處去除listener監(jiān)聽
  unregisterDataChangeListener(listener: DataChangeListener): void {
    const pos = this.listeners.indexOf(listener);
    if (pos >= 0) {
      console.info('remove listener');
      this.listeners.splice(pos, 1);
    }
  }

  // 通知LazyForEach組件需要重載所有子組件
  notifyDataReload(): void {
    this.listeners.forEach(listener => {
      listener.onDataReloaded();
    })
  }

  // 通知LazyForEach組件需要在index對應(yīng)索引處添加子組件
  notifyDataAdd(index: number): void {
    this.listeners.forEach(listener => {
      listener.onDataAdd(index);
    })
  }

  // 通知LazyForEach組件在index對應(yīng)索引處數(shù)據(jù)有變化靡挥,需要重建該子組件
  notifyDataChange(index: number): void {
    this.listeners.forEach(listener => {
      listener.onDataChange(index);
    })
  }

  // 通知LazyForEach組件需要在index對應(yīng)索引處刪除該子組件
  notifyDataDelete(index: number): void {
    this.listeners.forEach(listener => {
      listener.onDataDelete(index);
    })
  }
}

class MyDataSource extends BasicDataSource {
  private dataArray: string[] = [];

  public totalCount(): number {
    return this.dataArray.length;
  }

  public getData(index: number): string {
    return this.dataArray[index];
  }

  public addData(index: number, data: string): void {
    this.dataArray.splice(index, 0, data);
    this.notifyDataAdd(index);
  }

  public pushData(data: string): void {
    this.dataArray.push(data);
    this.notifyDataAdd(this.dataArray.length - 1);
  }
}

@Entry
@Component
struct LforEachTest {
  private data: MyDataSource = new MyDataSource();
  @State @Watch("onMessageUpdated") message: number = 0;

  onMessageUpdated() {
    console.info(`LazyforEach message callback func ${this.message}`)
  }

  aboutToAppear() {
    for (let i = 0; i <= 20; i++) {
      this.data.pushData(`Hello ${i}`)
    }
  }

  build() {
    Column() {
      Button('change message').onClick(() => {
        this.message++
      })
      List({ space: 3 }) {
        LazyForEach(this.data, (item: string) => {
          ListItem() {
            FreezeChild({ message: this.message, index: item })
          }
        }, (item: string) => item)
      }.cachedCount(5).height(500)
    }

  }
}

@Component({ freezeWhenInactive: true })
struct FreezeChild {
  @Link @Watch("onMessageUpdated") message: number;
  private index: string = "";

  aboutToAppear() {
    console.info(`FreezeChild aboutToAppear index: ${this.index}`)
  }

  onMessageUpdated() {
    console.info(`FreezeChild message callback func ${this.message}, index: ${this.index}`)
  }

  build() {
    Text("message" + `${this.message}, index: ${this.index}`)
      .width('90%')
      .height(160)
      .backgroundColor(0xAFEEEE)
      .textAlign(TextAlign.Center)
      .fontSize(30)
      .fontWeight(FontWeight.Bold)
  }
}

在上面的示例中:

1.點擊“change message”更改message的值序矩,當前正在顯示的ListItem中的子組件@Watch中注冊的方法onMessageUpdated被觸發(fā)。緩存節(jié)點@Watch中注冊的方法不會被觸發(fā)跋破。(如果不加組件凍結(jié)簸淀,當前正在顯示的ListItem和cachcount緩存節(jié)點@Watch中注冊的方法onMessageUpdated都會觸發(fā)watch回調(diào)。)

2.List區(qū)域外的ListItem滑動到List區(qū)域內(nèi)毒返,狀態(tài)由inactive變?yōu)閍ctive租幕,對應(yīng)的@Watch中注冊的方法onMessageUpdated被觸發(fā)。

3.再次點擊“change message”更改message的值饿悬,僅有當前顯示的ListItem中的子組件@Watch中注冊的方法onMessageUpdated被觸發(fā)令蛉。

Navigation

  • 對當前不可見的頁面進行凍結(jié),不會觸發(fā)組件的更新狡恬,當返回該頁面時珠叔,觸發(fā)@Watch回調(diào)進行刷新。
@Entry
@Component
struct MyNavigationTestStack {
  @Provide('pageInfo') pageInfo: NavPathStack = new NavPathStack();
  @State @Watch("info") message: number = 0;
  @State logNumber: number = 0;

  info() {
    console.info(`freeze-test MyNavigation message callback ${this.message}`);
  }

  @Builder
  PageMap(name: string) {
    if (name === 'pageOne') {
      pageOneStack({ message: this.message, logNumber: this.logNumber })
    } else if (name === 'pageTwo') {
      pageTwoStack({ message: this.message, logNumber: this.logNumber })
    } else if (name === 'pageThree') {
      pageThreeStack({ message: this.message, logNumber: this.logNumber })
    }
  }

  build() {
    Column() {
      Button('change message')
        .onClick(() => {
          this.message++;
        })
      Navigation(this.pageInfo) {
        Column() {
          Button('Next Page', { stateEffect: true, type: ButtonType.Capsule })
            .width('80%')
            .height(40)
            .margin(20)
            .onClick(() => {
              this.pageInfo.pushPath({ name: 'pageOne' }); //將name指定的NavDestination頁面信息入棧
            })
        }
      }.title('NavIndex')
      .navDestination(this.PageMap)
      .mode(NavigationMode.Stack)
    }
  }
}

@Component
struct pageOneStack {
  @Consume('pageInfo') pageInfo: NavPathStack;
  @State index: number = 1;
  @Link message: number;
  @Link logNumber: number;

  build() {
    NavDestination() {
      Column() {
        NavigationContentMsgStack({ message: this.message, index: this.index, logNumber: this.logNumber })
        Text("cur stack size:" + `${this.pageInfo.size()}`)
          .fontSize(30)
          .fontWeight(FontWeight.Bold)
        Button('Next Page', { stateEffect: true, type: ButtonType.Capsule })
          .width('80%')
          .height(40)
          .margin(20)
          .onClick(() => {
            this.pageInfo.pushPathByName('pageTwo', null);
          })
        Button('Back Page', { stateEffect: true, type: ButtonType.Capsule })
          .width('80%')
          .height(40)
          .margin(20)
          .onClick(() => {
            this.pageInfo.pop();
          })
      }.width('100%').height('100%')
    }.title('pageOne')
    .onBackPressed(() => {
      this.pageInfo.pop();
      return true;
    })
  }
}

@Component
struct pageTwoStack {
  @Consume('pageInfo') pageInfo: NavPathStack;
  @State index: number = 2;
  @Link message: number;
  @Link logNumber: number;

  build() {
    NavDestination() {
      Column() {
        NavigationContentMsgStack({ message: this.message, index: this.index, logNumber: this.logNumber })
        Text("cur stack size:" + `${this.pageInfo.size()}`)
          .fontSize(30)
          .fontWeight(FontWeight.Bold)
        Button('Next Page', { stateEffect: true, type: ButtonType.Capsule })
          .width('80%')
          .height(40)
          .margin(20)
          .onClick(() => {
            this.pageInfo.pushPathByName('pageThree', null);
          })
        Button('Back Page', { stateEffect: true, type: ButtonType.Capsule })
          .width('80%')
          .height(40)
          .margin(20)
          .onClick(() => {
            this.pageInfo.pop();
          })
      }.width('100%').height('100%')
    }.title('pageTwo')
    .onBackPressed(() => {
      this.pageInfo.pop();
      return true;
    })
  }
}

@Component
struct pageThreeStack {
  @Consume('pageInfo') pageInfo: NavPathStack;
  @State index: number = 3;
  @Link message: number;
  @Link logNumber: number;

  build() {
    NavDestination() {
      Column() {
        NavigationContentMsgStack({ message: this.message, index: this.index, logNumber: this.logNumber })
        Text("cur stack size:" + `${this.pageInfo.size()}`)
          .fontSize(30)
          .fontWeight(FontWeight.Bold)
        Button('Next Page', { stateEffect: true, type: ButtonType.Capsule })
          .width('80%')
          .height(40)
          .margin(20)
          .onClick(() => {
            this.pageInfo.pushPathByName('pageOne', null);
          })
        Button('Back Page', { stateEffect: true, type: ButtonType.Capsule })
          .width('80%')
          .height(40)
          .margin(20)
          .onClick(() => {
            this.pageInfo.pop();
          })
      }.width('100%').height('100%')
    }.title('pageThree')
    .onBackPressed(() => {
      this.pageInfo.pop();
      return true;
    })
  }
}

@Component({ freezeWhenInactive: true })
struct NavigationContentMsgStack {
  @Link @Watch("info") message: number;
  @Link index: number;
  @Link logNumber: number;

  info() {
    console.info(`freeze-test NavigationContent message callback ${this.message}`);
    console.info(`freeze-test ---- called by content ${this.index}`);
    this.logNumber++;
  }

  build() {
    Column() {
      Text("msg:" + `${this.message}`)
        .fontSize(30)
        .fontWeight(FontWeight.Bold)
      Text("log number:" + `${this.logNumber}`)
        .fontSize(30)
        .fontWeight(FontWeight.Bold)
    }
  }
}

在上面的示例中:

1.點擊“change message”更改message的值弟劲,當前正在顯示的MyNavigationTestStack組件中的@Watch中注冊的方法info被觸發(fā)祷安。

2.點擊“Next Page”切換到PageOne,創(chuàng)建pageOneStack節(jié)點兔乞。

3.再次點擊“change message”更改message的值汇鞭,僅pageOneStack中的NavigationContentMsgStack子組件中的@Watch中注冊的方法info被觸發(fā)。

4.再次點擊“Next Page”切換到PageTwo庸追,創(chuàng)建pageTwoStack節(jié)點霍骄。

5.再次點擊“change message”更改message的值,僅pageTwoStack中的NavigationContentMsgStack子組件中的@Watch中注冊的方法info被觸發(fā)淡溯。

6.再次點擊“Next Page”切換到PageThree读整,創(chuàng)建pageThreeStack節(jié)點。

7.再次點擊“change message”更改message的值咱娶,僅pageThreeStack中的NavigationContentMsgStack子組件中的@Watch中注冊的方法info被觸發(fā)米间。

8.點擊“Back Page”回到PageTwo,此時膘侮,僅pageTwoStack中的NavigationContentMsgStack子組件中的@Watch中注冊的方法info被觸發(fā)屈糊。

9.再次點擊“Back Page”回到PageOne,此時琼了,僅pageOneStack中的NavigationContentMsgStack子組件中的@Watch中注冊的方法info被觸發(fā)逻锐。

10.再次點擊“Back Page”回到初始頁,此時,無任何觸發(fā)谦去。

寫在最后

  • 如果你覺得這篇內(nèi)容對你還蠻有幫助慷丽,我想邀請你幫我三個小忙:
  • 點贊,轉(zhuǎn)發(fā)鳄哭,有你們的 『點贊和評論』要糊,才是我創(chuàng)造的動力。
  • 關(guān)注小編妆丘,同時可以期待后續(xù)文章ing??锄俄,不定期分享原創(chuàng)知識。
  • 想要獲取更多完整鴻蒙最新學(xué)習知識點勺拣,請移步前往小編:https://gitee.com/MNxiaona/733GH/blob/master/jianshu
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末奶赠,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子药有,更是在濱河造成了極大的恐慌毅戈,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,539評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件愤惰,死亡現(xiàn)場離奇詭異苇经,居然都是意外死亡,警方通過查閱死者的電腦和手機宦言,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評論 3 396
  • 文/潘曉璐 我一進店門扇单,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人奠旺,你說我怎么就攤上這事蜘澜。” “怎么了响疚?”我有些...
    開封第一講書人閱讀 165,871評論 0 356
  • 文/不壞的土叔 我叫張陵鄙信,是天一觀的道長。 經(jīng)常有香客問我忿晕,道長装诡,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,963評論 1 295
  • 正文 為了忘掉前任杏糙,我火速辦了婚禮慎王,結(jié)果婚禮上蚓土,老公的妹妹穿的比我還像新娘宏侍。我一直安慰自己,他們只是感情好蜀漆,可當我...
    茶點故事閱讀 67,984評論 6 393
  • 文/花漫 我一把揭開白布谅河。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪绷耍。 梳的紋絲不亂的頭發(fā)上吐限,一...
    開封第一講書人閱讀 51,763評論 1 307
  • 那天,我揣著相機與錄音褂始,去河邊找鬼诸典。 笑死,一個胖子當著我的面吹牛崎苗,可吹牛的內(nèi)容都是我干的狐粱。 我是一名探鬼主播,決...
    沈念sama閱讀 40,468評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼胆数,長吁一口氣:“原來是場噩夢啊……” “哼肌蜻!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起必尼,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤蒋搜,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后判莉,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體豆挽,經(jīng)...
    沈念sama閱讀 45,850評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,002評論 3 338
  • 正文 我和宋清朗相戀三年骂租,在試婚紗的時候發(fā)現(xiàn)自己被綠了祷杈。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,144評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡渗饮,死狀恐怖但汞,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情互站,我是刑警寧澤私蕾,帶...
    沈念sama閱讀 35,823評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站胡桃,受9級特大地震影響踩叭,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜翠胰,卻給世界環(huán)境...
    茶點故事閱讀 41,483評論 3 331
  • 文/蒙蒙 一容贝、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧之景,春花似錦斤富、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽焕参。三九已至,卻和暖如春油额,著一層夾襖步出監(jiān)牢的瞬間叠纷,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評論 1 272
  • 我被黑心中介騙來泰國打工潦嘶, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留涩嚣,地道東北人。 一個月前我還...
    沈念sama閱讀 48,415評論 3 373
  • 正文 我出身青樓掂僵,卻偏偏與公主長得像缓艳,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子看峻,可洞房花燭夜當晚...
    茶點故事閱讀 45,092評論 2 355

推薦閱讀更多精彩內(nèi)容