自定義組件

2020-05-14 14:20 更新

CML 支持簡潔的組件化編程。

開發(fā)者可以將頁面內(nèi)的功能模塊抽象成自定義組件,以便在不同的頁面中重復(fù)使用;也可以將復(fù)雜的頁面拆分成多個低耦合的模塊,有助于代碼維護(hù)。

創(chuàng)建自定義組件

類似頁面,自定義組件由 <template> <script> <style> <script cml-type="json"> 4 部分組成。

要編寫一個自定義組件,可以在 json 中進(jìn)行自定義組件聲明(將 component 字段設(shè)為 true,這是可選操作,因?yàn)?CML 會在引用這個文件時自動識別自定義組件):

{
  "component": true
}

接下來,在 <template> 中編寫組件模板,在 <style> 中加入組件樣式,它們的寫法與頁面的寫法類似。具體細(xì)節(jié)和注意事項(xiàng)參見組件模板和樣式。

代碼示例:

<!-- 這是自定義組件的內(nèi)部CML結(jié)構(gòu) -->
<view class="inner">
  {{innerText}}
</view>
<slot></slot>
/* 這里的樣式只應(yīng)用于這個自定義組件 */
.inner {
  color: red;
}

注意:在組件 CMSS 中不應(yīng)使用 ID 選擇器、屬性選擇器和標(biāo)簽名選擇器。

組件的屬性值和內(nèi)部數(shù)據(jù)將被用于組件 CML 的渲染,其中,屬性值是可由組件外部傳入的。

代碼示例:

<script>
class CustomCom {
  props = {
    title: String,
    innerText: {
      type: String,
    },
    content: {
      type: Object,
      default: {},
    },
    list: {
      type: Array,
      default: [],
    },
  };

  data = {};
  computed = {};
  watch = {};
  methods = {};
  beforeCreate() {}
  created() {}
  beforeMount() {}
  mounted() {}
  beforeDestroy() {}
  destroyed() {}
}
export default new CustomCom();
</script>

使用自定義組件

使用已注冊的自定義組件前,首先要在頁面的 <script cml-type="json"> 中進(jìn)行引用聲明。

此時需要提供每個自定義組件的標(biāo)簽名和對應(yīng)的自定義組件文件路徑(相對路徑或者絕對路徑):

{
  "usingComponents": {
    "component-tag-name": "path/to/the/custom/component"
  }
}

這樣,在頁面的 CML 中就可以像使用基礎(chǔ)組件一樣使用自定義組件。節(jié)點(diǎn)名即自定義組件的標(biāo)簽名,節(jié)點(diǎn)屬性即傳遞給組件的屬性值。

代碼示例:

<view>
  <!-- 以下是對一個自定義組件的引用 -->
  <component-tag-name title="xxx" inner-text="Some text"></component-tag-name>
</view>

自定義組件的 CML 節(jié)點(diǎn)結(jié)構(gòu)在與數(shù)據(jù)結(jié)合之后,將被插入到引用位置內(nèi)。

細(xì)節(jié)注意事項(xiàng)

一些需要注意的細(xì)節(jié):

  • 因?yàn)?CML 節(jié)點(diǎn)標(biāo)簽名只能是小寫字母、中劃線和下劃線的組合,所以自定義組件的標(biāo)簽名也只能包含這些字符。
  • 自定義組件也是可以引用自定義組件的,引用方法類似于頁面引用自定義組件的方式(使用 usingComponents 字段)。
  • 自定義組件和頁面所在項(xiàng)目根目錄名不能以“wx-”為前綴,否則會報錯。

**注意,**是否在頁面文件中使用 usingComponents 會使得頁面的 this 對象的原型稍有差異,包括:

  • 使用 usingComponents 時會多一些方法,如 selectComponent 。
  • 如果頁面比較復(fù)雜,新增或刪除 usingComponents 定義段時建議重新測試一下。

組件模板和樣式

類似于頁面,自定義組件擁有自己的 <template> 模板和 <style> 樣式。

首先我們通過 cml init project 生成一個 CML 項(xiàng)目,然后按照以下操作指引,看下如何使用 CML 的組件

cml init component
選擇普通組件
輸入 component-tag-namecomponent-tag-name

在 src/pages/index/index.cml中引入這個組件

"usingComponents": {
  "component-tag-name":"/components/component-tag-name/component-tag-name"
}

組件模板

組件模板的寫法與頁面模板相同。組件模板與組件數(shù)據(jù)結(jié)合后生成的節(jié)點(diǎn)樹,將被插入到組件的引用位置上。

在組件模板中可以提供一個 <slot> 節(jié)點(diǎn),用于承載組件引用時提供的子節(jié)點(diǎn)。

代碼示例:

<!-- component-tag-name.cml -->
<!-- 組件模板 -->
<view class="wrapper">
  <view>這里是組件的內(nèi)部節(jié)點(diǎn)</view>
  <slot></slot>
</view>
<!-- index.cml -->
<!-- 引用組件的頁面模板 -->
<view>
  <component-tag-name>
    <!-- 這部分內(nèi)容將被放置在組件 <slot> 的位置上 -->
    <view>這里是插入到組件slot中的內(nèi)容</view>
  </component-tag-name>
</view>

模板數(shù)據(jù)綁定

可以使用數(shù)據(jù)綁定,這樣就可以向子組件的屬性傳遞動態(tài)數(shù)據(jù)。

代碼示例:

<!-- 引用組件的頁面模板 -->
<view>
  <component-tag-name prop-a="{{dataFieldA}}" prop-b="{{dataFieldB}}">
    <!-- 這部分內(nèi)容將被放置在組件 <slot> 的位置上 -->
    <view>這里是插入到組件slot中的內(nèi)容</view>
  </component-tag-name>
</view>

在以上例子中,組件的屬性 propA 和 propB 將收到頁面?zhèn)鬟f的數(shù)據(jù)。頁面可以通過 this.dataFieldA、this.dataFieldB 來改變綁定的數(shù)據(jù)字段。

注意:這樣的數(shù)據(jù)綁定只能傳遞 JSON 兼容數(shù)據(jù)。

組件 <template> 的 slot

在組件的 <template> 中可以包含 slot 節(jié)點(diǎn),用于承載組件使用者提供的 CML 結(jié)構(gòu)。

支持通過具名插槽的方式在模板中使用多個 slot

<!-- component-tag-name.cml -->
<template>
  <view>
    <view>組件模板的slot</view>
    <slot name="before"></slot>
    <slot></slot>
    <slot name="after"></slot>
  </view>
</template>

然后在父頁面 src/pages/index/index.cml中

<template>
  <component-tag-name>
    <view>this is from index</view>
    <view slot="before">this is before from index</view>
    <view slot="after">this is after from index</view>
    <view>this is from index aaaa</view>
  </component-tag-name>
</template>

組件樣式

組件對應(yīng) style 標(biāo)簽內(nèi)的樣式,只對當(dāng)前節(jié)點(diǎn)生效。編寫組件樣式時,需要注意以下幾點(diǎn):

  • 組件和引用組件的頁面不能使用 id 選擇器(#a)、屬性選擇器([a])和標(biāo)簽名選擇器,請改用class選擇器。
  • 組件和引用組件的頁面中使用后代選擇器(.a .b)在一些極端情況下會有非預(yù)期的表現(xiàn),如遇,請避免使用。
  • 子元素選擇器(.a > .b)只能用于 view 組件與其子節(jié)點(diǎn)之間,用于其他組件可能導(dǎo)致非預(yù)期的情況。
  • 繼承樣式,如 font 、 color ,會從組件外繼承到組件內(nèi)。
  • 元素選擇器不支持
#a {
} /* 在組件中不能使用 */
[a] {
} /* 在組件中不能使用 */
button {
} /* 在組件中不能使用 */
.a > .b {
} /* 除非 .a 是 view 組件節(jié)點(diǎn),否則不一定會生效 */
view {
} /* 不支持元素選擇器 */

樣式多態(tài)

CML 擴(kuò)展了多態(tài)樣式,用于針對對于不同端有不同的樣式需求的情況參考

組件 VM

在 .cml 文件 <script> 代碼塊 export default 的對象實(shí)例,可用于定義組件,指定組件的屬性、數(shù)據(jù)、方法等。

cml init component
選擇普通組件
輸入 component-tag-name

定義如下:

字段名類型說明
propsObject聲明當(dāng)前組件可接收數(shù)據(jù)屬性 props = { type, default } type 為數(shù)據(jù)類型,default 為數(shù)據(jù)默認(rèn)值
dataObjectCML 模板可直接使用的響應(yīng)數(shù)據(jù),是連接視圖層的樞紐
methodsObject處理業(yè)務(wù)邏輯與交互邏輯的方法
watchObject偵聽屬性,監(jiān)聽數(shù)據(jù)的變化,觸發(fā)相應(yīng)操作
computedObjectCML 模板可直接使用的計算屬性數(shù)據(jù),也是連接視圖層的樞紐
beforeCreateFunction例初始化之后,數(shù)據(jù)和方法掛在到實(shí)例之前 一個頁面只會返回一次
createdFunction數(shù)據(jù)及方法掛載完成
beforeMountFunction開始掛載已經(jīng)編譯完成的 cml 到對應(yīng)的節(jié)點(diǎn)時
mountedFunctioncml 模板編譯完成,且渲染到 dom 中完成
beforeDestroyFunction實(shí)例銷毀之前
destroyedFunction實(shí)例銷毀后

組件間的通信

組件間的通信方式有以下幾種:

父組件 -> 子組件: props 傳遞

代碼示例

<!-- index.cml -->
<template>
  <view>
    <component-tag-name parent-prop="{{parent}}"> </component-tag-name>
  </view>
</template>

<script>
class Index {
  data = {
    parent: { msg: 'this is parent message' },
  };
}

export default new Index();
</script>
<!-- component-tag-name.cml -->
<template>
  <view>
    <view>{{ parentProp.msg }}</view>
  </view>
</template>

<script>
class ComponentTagName {
  props = {
    parentProp: {
      type: Object,
      default: {},
    },
  };
}

export default new ComponentTagName();
</script>

子組件 -> 父組件:事件通訊

代碼示例

<!-- index.cml -->
<template>
  <view>
    <component-tag-name c-bind:parentevent="handleParentEvent"> </component-tag-name>
  </view>
</template>
<script>
class Index {
  methods = {
    handleParentEvent(...args) {
      console.log(...args);
    },
  };
}
export default new Index();
</script>
<!-- component-tag-name.cml -->
<template>
  <view>
    <view c-bind:tap="handleClick"></view>
  </view>
</template>
<script>
class ComponentTagName {
  methods = {
    handleClick() {
      this.$cmlEmit('parentevent', {
        value: 'this is from child',
      });
    },
  };
}
export default new ComponentTagName();
</script>


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號