鴻蒙JS UI 組件通信總結(jié)及任意組件通信
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)
組件間的通信主要作用是能在組件間傳遞數(shù)據(jù)或者執(zhí)行相關(guān)的業(yè)務(wù)邏輯,對于鴻蒙應(yīng)用組件,下面將對幾種組件間的通信方式進行代碼實現(xiàn),其中包括實現(xiàn)自定義實現(xiàn)任意組件通信。
首先我們準備好幾個組件 parent組件、current組件、child1組件、child2組件,其中parent與current是父子組件關(guān)系、curren與child1/child2是父子組件關(guān)系、child1與child2是兄弟組件關(guān)系、parent與child1/child2是跨層級組件的關(guān)系。
common.css
- .title {
- font-size: 20px;
- text-align: left;
- padding-bottom: 5px;
- line-height: 20px;
- }
- .div-button {
- flex-direction: row;
- }
- .div-button .button {
- margin: 5px;
- }
1.parent組件
parent.hml
- <element src="../current/current.hml" name="current" ></element>
- <div class="container">
- <text class="title">parent-num : {{num}}</text>
- <text class="title">parent-info-str : {{info.str}}</text>
- <current id="current" title="parent" ></current>
- </div>
parent.js
- export default {
- data: {
- num: 0,
- info:{
- str: 'parentStr'
- }
- }
- }
parent.css
- @import '../../../common/css/common.css';
- .container {
- display: flex;
- flex: 1;
- flex-direction: column;
- width: 100%;
- padding: 10px;
- background-color: aqua;
- }
2.current組件
current.hml
- <element src="../child1/child.hml" name="child1" ></element>
- <element src="../child2/child.hml" name="child2" ></element>
- <div class="container">
- <text class="title">current-title : {{title}}</text>
- <text class="title">current-num : {{num}}</text>
- <text class="title">current-info-str : {{info.str}}</text>
- <div class="div-button">
- <button class="button" type="capsule" value="改變parent" onclick="changeParent"></button>
- <button class="button" type="capsule" value="改變child1" onclick="changeChild1" ></button>
- <button class="button" type="capsule" value="改變child2" onclick="changeChild2" ></button>
- </div>
- <child1 id="child1" ></child1>
- <child2 id="child2" ></child2>
- </div>
current.js
- export default {
- props:{
- title:{
- default: 'current-title',
- }
- },
- data: {
- num: 0,
- info:{
- str: 'currentStr'
- }
- },
- // 改變parent組件的num 和 info
- changeParent(){
- console.log('**start**********************');
- console.log('current change parent');
- let parent = this.$parent();
- let num = parent.num + 1;
- parent.num = num;
- let info = {
- str: 'current change parent'
- };
- // current.info = info;
- parent.info.str = 'current change parent';
- console.log('num :');
- console.log(num);
- console.log('info :');
- console.log(JSON.stringify(info));
- console.log('**end************************');
- },
- // 改變child1組件的num 和 info
- changeChild1(){
- console.log('**start**********************');
- console.log('current change child1');
- let child1 = this.$child('child1');
- let num = child1.num + 1;
- child1.num = num;
- let info = {
- str: 'current change child1'
- };
- child1.info = info;
- console.log('num :');
- console.log(num);
- console.log('info :');
- console.log(JSON.stringify(info));
- console.log('**end************************');
- },
- // 改變child2組件的num 和 info
- changeChild2(){
- console.log('**start**********************');
- console.log('current change child2');
- let child2 = this.$child('child2');
- let num = child2.num + 1;
- child2.num = num;
- let info = {
- str: 'current change child2'
- };
- child2.info = info;
- console.log('num :');
- console.log(num);
- console.log('info :');
- console.log(JSON.stringify(info));
- console.log('**end************************');
- }
- }
current.css
- @import '../../../common/css/common.css';
- .container {
- display: flex;
- flex: 1;
- flex-direction: column;
- width: 100%;
- padding: 10px;
- background-color: aquamarine;
- border: 1px solid #333333;
- }
3.child1組件
child1.hml
- <div class="container" style="line-height: 40px;">
- <text class="title">child1-num : {{num}}</text>
- <text class="title">child1-info-str : {{info.str}}</text>
- </div>
child1.js
- export default {
- data: {
- num: 0,
- info:{
- str: 'child1Str'
- }
- }
- }
child1.css
- @import '../../../common/css/common.css';
- .container {
- display: flex;
- flex-direction: column;
- width: 100%;
- padding: 10px;
- background-color: bisque;
- border: 1px solid #333333;
- min-height: 200px;
- }
4.child2組件
child2.hml
- <div class="container">
- <text class="title">child2-num : {{num}}</text>
- <text class="title">child2-info-str : {{info.str}}</text>
- </div>
child2.js
- export default {
- data: {
- num: 0,
- info:{
- str: 'child2Str'
- }
- }
- }
child2.css
- @import '../../../common/css/common.css';
- .container {
- display: flex;
- flex-direction: column;
- width: 100%;
- padding: 10px;
- background-color: beige;
- border: 1px solid #333333;
- margin-top: 10px;
- min-height: 200px;
- }
最終預(yù)覽得到結(jié)果如下:

父子關(guān)系通信
parent與current是父子組件關(guān)系,在current.js中props的title=''current-title",在parent.hml中使用current組件時傳入title="parent"時,current組件顯示parent。


curren與child1/child2是父子組件關(guān)系,在current組件中有三個按鈕 改變parent、改變child1、改變child2

在curren組件中,點擊改變parent按鈕會觸發(fā)changeParent方法,點擊改變child1按鈕會觸發(fā)changeChild1方法,點擊改變child1按鈕會觸發(fā)changeChild1方法,這些方法會改變對應(yīng)組件中的數(shù)據(jù),如下:
點擊改變parent

點擊改變child1

點擊改變child2

在changeParent方法中使用this.$parent()方法來獲取了parent組件的ViewModel,從而可以訪問和改變parent組件的數(shù)據(jù)
在changeChild1和changeChild2中使用了this.$child(id)方法來獲取child1/child2組件的ViewModel,從而可以訪問和改變child1/child2組件的數(shù)據(jù)
除了使用this.$parent方法外還可以使用自定義事件的方式來進行子組件調(diào)用父組件方法來向父組件傳值,parent組件在引用current組件時綁定事件,在current組件中調(diào)用this.$emit方法來觸發(fā)綁定的自定義事件。
parent.hml

parent.js

current.js

總結(jié)下來,父子組件間的通信方式大概有幾種方法:
props:用于父組件向子組件傳遞數(shù)據(jù)
$emit:用于自組件向父組件傳遞數(shù)據(jù)或調(diào)用父組件方法
$parent:用于獲取父組件ViewModel從而可以修改父組件數(shù)據(jù)和調(diào)用父組件方法
$child:用于獲取子組件ViewModel從而修改子組件數(shù)據(jù)和調(diào)用子組件方法
兄弟關(guān)系與跨層級組件間通信
根據(jù)上面父子組件的通信方式,對于兄弟組件和跨層級組件之間,可以組合使用上面的幾種方法組合的方式可以實現(xiàn)非父子組件間的通信,但是如果組件間嵌套比較復(fù)雜,嵌套層級比較多的情況下,使用以上方法組合的方式顯然不太方便,在這里嘗試實現(xiàn)一種類似vue bus全局通信的方式來進行任意組件間的通信。
在JS API中app.js文件中定義的數(shù)據(jù)可以通過this.app.app.def獲取,根據(jù)這個特性和使用觀察者模式來設(shè)計一可以全局訂閱響應(yīng)的通信模式。

首先我們定義evnetBus.js文件,在里面定義相關(guān)的訂閱、發(fā)布和取消訂閱的方法:
eventBus.js
- const Bus = {
- // 發(fā)布的事件集合
- $events:{},
- /**
- * 發(fā)布事件方法
- * type: string 字符串
- * fun: function 綁定的方法
- * */
- $on(type,fun){
- },
- /**
- * 觸發(fā)事件方法
- * type: string 發(fā)布事件的字符串
- * args: 傳參
- * */
- $emit(type,...args){
- },
- /**
- * 注銷事件方法
- * type: string 字符串
- * fun: string|function 發(fā)布事件時返回的值或者發(fā)布的原function
- * */
- $off(type,fun){
- }
- }
- export default Bus;
在里面我們定義了$events對象用于綁定事件的存放,$on方法用于綁定事件,$emit方法用于觸發(fā)事件,$off方法用于注銷事件,完整的eventBus.js如下
eventBus.js
- const Bus = {
- // 發(fā)布的事件集合
- $events:{ },
- /**
- * 發(fā)布事件方法
- * type: string 字符串
- * fun: function 綁定的方法
- * */
- $on(type,fun){
- let res = "";
- if(type && typeof type == "string" && fun && typeof fun =="function"){
- let events = this.$events[type];
- if(events){
- let index = events.findIndex(null);
- if(index > -1){
- res = `${String(index)}${type}`;
- events[index] = fun;
- }else{
- events.push(fun);
- }
- }else{
- this.$events[type] = [fun];
- res = `0${type}`;
- }
- }
- return res;
- },
- /**
- * 觸發(fā)事件方法
- * type: string 發(fā)布事件的字符串
- * args: 傳參
- * */
- $emit(type,...args){
- if(type && typeof type == "string"){
- let events = this.$events[type];
- if(events){
- events.forEach(fun => {
- if(fun && typeof fun =="function"){
- fun(...args);
- }
- });
- }
- }
- },
- /**
- * 注銷事件方法
- * type: string 字符串
- * fun: string|function 發(fā)布事件時返回的值或者發(fā)布的原function
- * */
- $off(type,fun){
- if(type && typeof type == "string" && fun){
- let events = this.$events[type];
- if(events){
- if(typeof fun == "string"){
- let indexStr = fun.replace(type,'');
- let index = parseInt(indexStr);
- events[index] = null;
- }
- if(typeof fun == "function"){
- events.forEach(item => {
- if(item == fun){
- item = null;
- }
- });
- }
- }
- }
- }
- }
- export default Bus;
使用方法如下:
首先在app.js中引入eventBus:

在parent組件的onInit方法中綁定事件,可以通過綁定的事件來改變parent組件的值:

然后我們修改child1組件,添加按鈕來觸發(fā)bus事件:

在觸發(fā)方法的時候我們傳入了自定義參數(shù)數(shù)據(jù)"123456789"

在點擊按鈕后將會觸發(fā)我們在parent組件中綁定的方法從而改變parent組件數(shù)據(jù),同時也接收到了child組件傳過來的數(shù)據(jù)"123456789"

在此就實現(xiàn)了跨層級組件間的通信,此方法可以對任意組件間的事件觸發(fā)和傳值都有效,限于篇幅不再展示,最后我們還要在組件銷毀的時候注銷我們綁定的事件。
我們定義一個注銷事件的按鈕并綁定注銷事件:

在我們$on綁定事件的時候可以得到一個返回值,這個值也可以用于事件的注銷,在這里就不做演示了,有興趣的可以下去嘗試一下。
以上就是使用觀察者模式實現(xiàn)了一個簡易版的事件總線機制可以在任意組件間通信,基本功能可以實現(xiàn),還有一些缺點需要提示:
1.首先這個在使用的時候必須依賴鴻蒙JS API this.$app.$def 這個方法,每次使用都要用這個來獲取eventBus對象。
2.在注冊綁定事件后一定要記得在當前組件銷毀的時候注銷綁定的事件,否則可能會造成內(nèi)存泄露。
3.事件注銷有兩種方式,需要稍微理解注銷事件的原理才能熟練使用。
文章相關(guān)附件可以點擊下面的原文鏈接前往下載
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)