CML 語(yǔ)法

2020-05-14 14:19 更新

CML(Chameleon Markup Language)用于描述頁(yè)面的結(jié)構(gòu),我們知道 HTML 是有一套標(biāo)準(zhǔn)的語(yǔ)義化標(biāo)簽,例如 文本是 <span>  按鈕是 <button>。CML 同樣具有一套標(biāo)準(zhǔn)的標(biāo)簽,我們將標(biāo)簽定義為組件,CML 為用戶提供了一系列基礎(chǔ)組件。同時(shí) CML 中還支持模板語(yǔ)法,例如條件渲染、列表渲染,數(shù)據(jù)綁定等等。

基礎(chǔ)組件

框架為開發(fā)者提供了一系列基礎(chǔ)組件,開發(fā)者可以通過組合這些基礎(chǔ)組件進(jìn)行快速開發(fā)。詳細(xì)介紹請(qǐng)參考組件文檔。

什么是組件:

  • 組件是視圖層的基本組成單元。
  • 組件自帶一些功能與微信風(fēng)格一致的樣式。
  • 一個(gè)組件通常包括 開始標(biāo)簽 和 結(jié)束標(biāo)簽,屬性 用來(lái)修飾這個(gè)組件,內(nèi)容 在兩個(gè)標(biāo)簽之內(nèi)。
<tagname property="value">Content goes here ...</tagname>
注意:所有組件屬性都是小寫,以連字符-連接。

屬性類型

類型描述注解
String字符串`"string"`
Number數(shù)字`1, 1.5`
Boolean布爾值`true,false`
Array數(shù)組`[1, 'string']`
Object對(duì)象`{key: value}`
EventHandler事件處理函數(shù)名`handlerName`是組件中定義的事件處理函數(shù)名

公共屬性

所有組件都有以下屬性

屬性名類型描述注解
idString組件唯一標(biāo)示保證整個(gè)頁(yè)面唯一
classString組件樣式類名在cmss中定義的樣式類
styleString組件內(nèi)聯(lián)樣式可動(dòng)態(tài)設(shè)置內(nèi)聯(lián)樣式
c-bindEventHandler組件事件

特殊屬性

CML 提供了內(nèi)置組件及擴(kuò)展組件,根據(jù)組件特殊性幾乎每個(gè)組件都有自己的特殊屬性,詳細(xì)屬性請(qǐng)查看組件文檔。

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

模板中綁定的數(shù)據(jù)來(lái)均來(lái)自于 data、computed 屬性。

簡(jiǎn)單綁定

數(shù)據(jù)綁定使用 Mustache 語(yǔ)法(雙大括號(hào)), {{}}之內(nèi)的可以是一些變量或者簡(jiǎn)單的表達(dá)式。

內(nèi)容

<view><text>{{ message }}</text></view>

組件屬性

<view id="item-{{id}}"> </view>

運(yùn)算

<view hidden="{{flag ? true : false}}"> <text>Hidden </text> </view>
<view><text>{{a + b}} + {{c}} + d </text></view>
<view c-if="{{length > 5}}"> </view>
class Index {
  data = {
    a: 1,
    b: 2,
    c: 3,
  };
}
export default new Index();

view 中的內(nèi)容為 3 + 3 + d

c-model

應(yīng)用于表單元素

<template>
  <page title="chameleon">
       <view><text>message:{{message}}</text></view>
       <input c-model="{{message}}"></input>
  </page>
</template>
<script>
class Comp {
  data = {
     message:'default-value'
  }
  watch = {
    message(){
      console.log('modelTest change');
    }
  }
}
export default new Comp();
</script>
<script cml-type="json">
{
  "base": {}
}
</script>

c-model 元素上不支持再綁定 input 事件,如果對(duì)于輸入值變化之后想執(zhí)行一些操作,可以通過 watch 對(duì)應(yīng)的值來(lái)進(jìn)行;

應(yīng)用于父子組件之間

父組件

<template>
  <page title="chameleon">
    <scroller height="{{-1}}">
      <view><text>c-model的在組件上的使用</text></view>
      <comp c-model="{{modelValueTest2}}"></comp>
      <view
        ><text>組件使其改變{{ modelValueTest2 }}</text></view
      >
    </scroller>
  </page>
</template>

<script>
class Index {
  data = {
    currentComp: 'comp1',
    modelValueTest2: 'sss',
  };
  methods = {
    handleClick() {
      this.currentComp = this.currentComp === 'comp1' ? 'comp1' : 'comp2';
    },
  };
}

export default new Index();
</script>
<style>
.scroller-wrap {
  display: flex;
  flex-direction: column;
  align-items: center;
}
</style>
<script cml-type="json">
{
  "base": {
    "usingComponents": {
      "comp1":"/components/comp1",
      "comp2":"/components/comp2"
    }
  },
  "wx": {
    "navigationBarTitleText": "index",
    "backgroundTextStyle": "dark",
    "backgroundColor": "#E2E2E2"
  },
  "alipay": {
    "defaultTitle": "index",
    "pullRefresh": false,
    "allowsBounceVertical": "YES",
    "titleBarColor": "#ffffff"
  },
  "baidu": {
    "navigationBarBackgroundColor": "#ffffff",
    "navigationBarTextStyle": "white",
    "navigationBarTitleText": "index",
    "backgroundColor": "#ffffff",
    "backgroundTextStyle": "dark",
    "enablePullDownRefresh": false,
    "onReachBottomDistance": 50
  }
}
</script>

子組件

<template>
  <view>
    <input type="text" value="{{value}}" c-bind:input="handleInput" />
  </view>
</template>

<script>
class Comp {
  props = {
    value: {
      type: String,
      default: 'default-value',
    },
  };
  methods = {
    handleInput(e) {
      console.log('input', e);
      this.$cmlEmit('input', {
        value: e.detail.value,
      });
    },
  };
}
export default new Comp();
</script>
<script cml-type="json">
{
  "base": {}
}
</script>

Bug & Tips

注意 c-model 的值只能是 data 或者 computed 中的 key 值,不支持 modelValue.xxx 等需要二次計(jì)算的值;

條件渲染

c-if

在框架中,使用

c-if="{{ condition }}"

來(lái)判斷是否需要渲染該代碼塊:

<view c-if="{{condition}}">True</view>

也可以用 c-else-if 和 c-else 來(lái)添加一個(gè) else 塊:

<view c-if="{{length > 5}}"> <text>1 </text></view>
<view c-else-if="{{length > 2}}"> <text>2 </text></view>
<view c-else> <text>3 </text></view>

block c-if

因?yàn)?c-if 是一個(gè)控制屬性,需要將它添加到一個(gè)標(biāo)簽上。如果要一次性判斷多個(gè)組件標(biāo)簽,可以使用一個(gè) \<block\> 標(biāo)簽將多個(gè)組件包裝起來(lái),并在上邊使用 c-if 控制屬性。

<block c-if="{{true}}">
  <view> <text>view1 </text></view>
  <view> <text>view2 </text></view>
</block>

注意: \<block\> 并不是一個(gè)組件,它僅僅是一個(gè)包裝元素,不會(huì)在頁(yè)面中做任何渲染,只接受控制屬性。

列表渲染

c-for

在組件上使用 c-for 控制屬性綁定一個(gè)數(shù)組,即可使用數(shù)組中各項(xiàng)的數(shù)據(jù)重復(fù)渲染該組件。 默認(rèn)數(shù)組的當(dāng)前項(xiàng)的下標(biāo)變量名默認(rèn)為 index,數(shù)組當(dāng)前項(xiàng)的變量名默認(rèn)為 item

<view c-for="{{array}}">
  <text>{{index}}: {{item.message}}</text>
</view>

使用 c-for-item 可以指定數(shù)組當(dāng)前元素的變量名, 使用 c-for-index 可以指定數(shù)組當(dāng)前下標(biāo)的變量名:

<view c-for="{{array}}" c-for-index="idx" c-for-item="itemName">
  <text> {{idx}}: {{itemName.message}}</text>
</view>

c-for 也可以嵌套,下邊是一個(gè)九九乘法表

<view c-for="{{[1, 2, 3, 4, 5, 6, 7, 8, 9]}}" c-for-item="i">
  <view c-for="{{[1, 2, 3, 4, 5, 6, 7, 8, 9]}}" c-for-item="j">
    <view v-if="{{i <= j}}">
      <text> {{i}} * {{j}} = {{i * j}}</text>
    </view>
  </view>
</view>

block c-for

類似 block c-if,也可以將 c-for 用在 \<block\> 標(biāo)簽上,以渲染一個(gè)包含多節(jié)點(diǎn)的結(jié)構(gòu)塊。例如:

<block c-for="{{[1, 2, 3]}}">
  <view> <text>{{index}}: </text></view>
  <view> <text>{{item}}</text> </view>
</block>

c-key

如果列表中項(xiàng)目的位置會(huì)動(dòng)態(tài)改變或者有新的項(xiàng)目添加到列表中,并且希望列表中的項(xiàng)目保持自己的特征和狀態(tài)(如 <input/> 中的輸入內(nèi)容,<switch/>的選中狀態(tài)),需要使用 c-key 來(lái)指定列表中項(xiàng)目的唯一的標(biāo)識(shí)符。~~~~

c-key 的值以兩種形式提供

1.字符串,代表在 for 循環(huán)的 array 中 item 的某個(gè) property,該 property 的值需要是列表中唯一的字符串或數(shù)字,且不能動(dòng)態(tài)改變。

2.保留關(guān)鍵字 *this 代表在 for 循環(huán)中的 item 本身,這種表示需要 item 本身是一個(gè)唯一的字符串或者數(shù)字,如: 當(dāng)數(shù)據(jù)改變觸發(fā)渲染層重新渲染的時(shí)候,會(huì)校正帶有 key 的組件,框架會(huì)確保他們被重新排序,而不是重新創(chuàng)建,以確保使組件保持自身的狀態(tài),并且提高列表渲染時(shí)的效率。

事件

Chameleon 支持一些基礎(chǔ)的事件,保障各端效果一致運(yùn)行。如果你想要使用某個(gè)端特定的事件,請(qǐng)從業(yè)務(wù)出發(fā)使用多態(tài)組件或者多態(tài)接口差異化實(shí)現(xiàn)功能。

什么是事件

  • 事件是視圖層到邏輯層的通訊方式。
  • 事件可以將用戶的行為反饋到邏輯層進(jìn)行處理。
  • 事件可以綁定在組件上,當(dāng)達(dá)到觸發(fā)事件,就會(huì)執(zhí)行邏輯層中對(duì)應(yīng)的事件處理函數(shù)。

事件綁定

當(dāng)用戶點(diǎn)擊該組件的時(shí)候會(huì)在該組件邏輯對(duì)象的methods中尋找相應(yīng)的處理函數(shù)

<template>
  <view id="tapTest" data-hi="WeChat" c-bind:tap="tapName">
    <text>Click me!</text>
  </view>
</template>
<script>
class Index {
  methods = {
    tapName(e) {
      // 打印事件對(duì)象
      console.log('事件對(duì)象:', e);
    },
  };
}
export default new Index();
</script>

事件類型

chameleon 所有元素都支持基礎(chǔ)事件類型如下:

類型觸發(fā)條件
tap手指觸摸后馬上離開
touchstart手指觸摸動(dòng)作開始
touchmove手指觸摸后移動(dòng)
touchend手指觸摸動(dòng)作結(jié)束

事件對(duì)象

當(dāng)觸發(fā)事件時(shí),邏輯層綁定該事件的處理函數(shù)會(huì)收到一個(gè)事件對(duì)象。它有以下屬性:

名稱類型說(shuō)明
typeString事件類型
timeStampNumber頁(yè)面打開到觸發(fā)事件所經(jīng)過的毫秒數(shù)
targetObject觸發(fā)事件的目標(biāo)元素 且 target = { id, dataset }
currentTargetObject綁定事件的目標(biāo)元素 且 currentTarget = { id, dataset }
touchesArray觸摸事件中的屬性,當(dāng)前停留在屏幕中的觸摸點(diǎn)信息的數(shù)組 且 touches = [{ identifier, pageX, pageY, clientX, clientY }]
changedTouchesArray觸摸事件中的屬性,當(dāng)前變化的觸摸點(diǎn)信息的數(shù)組 且 changedTouches = [{ identifier, pageX, pageY, clientX, clientY }]
detailObject自定義事件所攜帶的數(shù)據(jù)。 通過`$cmlEmit`方法觸發(fā)自定義事件,可以傳遞自定義數(shù)據(jù)即detail。具體下面`自定義事件`。
_originEventObjectCML 對(duì)各平臺(tái)的事件對(duì)象進(jìn)行統(tǒng)一,會(huì)把原始的事件對(duì)象放到_originEvent屬性中,當(dāng)需要特殊處理的可以進(jìn)行訪問。

#target && currentTarget 事件屬性

屬性類型說(shuō)明
idString事件源組件的id
datasetObject事件源組件上由`data-`開頭的自定義屬性組成的集合
dataset

在組件中可以定義數(shù)據(jù),這些數(shù)據(jù)將會(huì)通過事件傳遞給 SERVICE。 書寫方式: 以 data-開頭,多個(gè)單詞由連字符-鏈接,不能有大寫(大寫會(huì)自動(dòng)轉(zhuǎn)成小寫)如 data-element-type,最終在 event.currentTarget.dataset 中會(huì)將連字符轉(zhuǎn)成駝峰 elementType。

示例:

<view data-alpha-beta="1" data-alphaBeta="2" c-bind:tap="bindViewTap"> DataSet Test </view>
<script>
class Index {
  methods = {
    bindViewTap: function(event) {
      event.currentTarget.dataset.alphaBeta === 1; // - 會(huì)轉(zhuǎn)為駝峰寫法
      event.currentTarget.dataset.alphabeta === 2; // 大寫會(huì)轉(zhuǎn)為小寫
    },
  };
}
export default new Index();
</script>

touches && changedTouches 事件屬性

數(shù)組中的對(duì)象具有如下屬性:

屬性類型說(shuō)明
identifierNumber觸摸點(diǎn)的標(biāo)識(shí)符
pageX, pageYNumber距離文檔左上角的距離,文檔的左上角為原點(diǎn) ,橫向?yàn)閄軸,縱向?yàn)閅軸
clientX, clientYNumber距離頁(yè)面可顯示區(qū)域(屏幕除去導(dǎo)航條)左上角距離,橫向?yàn)閄軸,縱向?yàn)閅軸

注意:返回值的單位為 px;可以通過chameleon-api中的 px2cpx進(jìn)行單位的轉(zhuǎn)化;

自定義事件

自定義事件用于父子組件之間的通信,父組件給子組件綁定自定義事件,子組件內(nèi)部觸發(fā)該事件。綁定事件的方法是以bind+事件名稱="事件處理函數(shù)的形式給組件添加屬性,規(guī)定事件名稱不能存在大寫字母觸發(fā)事件的方法是調(diào)用this.$cmlEmit(事件名稱,detail對(duì)象)。

注意:自定義事件名稱不支持click、scroll

例如: 子組件 child

<template>
 <view c-bind:tap="triggerCustomEvent"><text>觸發(fā)自定義事件</text></view>
</template>
<script>
class Index {
  data: {}
  method: {
    triggerCustomEvent(e) {
      this.$cmlEmit('customevent', {
        company: 'didi',
        age: 18
      })
    }
  }
}
export default new Index();
<script>

父組件

<template>
  <child c-bind:customevent="customEventHandler">
  </child>
</template>
<script>
class Index {
  data = {}
  method = {
    customEventHandler(e) {
      console.log(e)
    }
  }
}
export default new Index();
<script>

當(dāng)點(diǎn)擊child組件的按鈕時(shí),父組件中的 customEventHandler 方法中打印的 e 對(duì)象如下:

{
  type: "customevent",
  detail: {
    company: "didi",
    age: 18
  }
}

支持的語(yǔ)法

事件綁定支持以下幾種形式(在內(nèi)聯(lián)語(yǔ)句中,$event代表事件對(duì)象)

<!-- 寫法(1) -->
<view c-bind:tap="handleElementTap"><text>觸發(fā)元素點(diǎn)擊事件</text></view>
<!-- 寫法(2) -->
<view
  c-bind:tap="handleElementTap(1,2,3, 'message'+msg ,  $event)"><text>觸發(fā)元素點(diǎn)擊事件(1,2,3)</text></view>
<!-- 寫法(3) -->
<view c-bind:tap="handleElementTap()"><text>觸發(fā)元素點(diǎn)擊事件()</text></view>

**針對(duì)以上寫法返回的事件對(duì)象如下: **

寫法(1)調(diào)用事件函數(shù)輸出如下

'handleElementTap'[e];

寫法(2)調(diào)用事件函數(shù)輸出如下

'handleElementTap'[(1, 2, 3, 'messagetestEvent', e)];

寫法(3)調(diào)用事件函數(shù)輸出如下

'handleElementTap'  []

事件冒泡

chameleon-tool@0.2.0 + 的版本 支持了事件冒泡和阻止事件冒泡

注意:對(duì)于阻止事件冒泡,在內(nèi)聯(lián)事件傳參的情況下,需要傳遞 $event參數(shù);

<!-- 不會(huì)阻止冒泡 -->
<view c-catch:click="handleElementTap(1,2)"><text>觸發(fā)元素點(diǎn)擊事件</text></view>
<!-- 會(huì)阻止冒泡 -->
<view c-catch:click="handleElementTap(1,2,$event)"><text>觸發(fā)元素點(diǎn)擊事件</text></view>
<template>
  <view class="root">
    <view class="pad">
      cml語(yǔ)法事件冒泡測(cè)試
    </view>
    <view c-bind:click="rootClick">
      <text style="font-size: 40px;">{{ rootText }}</text>
      <view class="outer" c-catch:click="parentClick">
        <view>
          <text style="font-size: 40px;">{{ parentText }}</text>
        </view>
        <text class="inner" c-bind:click="click">{{ innerText }}</text>
      </view>
    </view>
  </view>
</template>

<script>
class Index {
  methods = {
    click: function(e) {
      this.innerText = 'inner bubble';
      console.log('this.innerText', this.innerText);
    },
    parentClick: function(e) {
      this.parentText = 'parent bubble';
      console.log('this.parentClick', this.parentClick);
    },
    rootClick: function(e) {
      this.rootText = 'root bubble';
      console.log('this.rootClick', this.rootClick);
    },
  };
}

export default new Index();
</script>

其他事件說(shuō)明

事件綁定的寫法同組件的屬性,以 key、value 的形式。 key 以 c-bind,然后跟上事件的類型,如 c-bind:tap、c-bind:touchstart。 value 是一個(gè)字符串,需要在對(duì)應(yīng)的邏輯對(duì)象中聲明的methods中聲明該方法。

Bug & Tips

不支持的語(yǔ)法 注意,事件綁定不支持直接傳入一個(gè)表達(dá)式,和綁定多個(gè)內(nèi)聯(lián)執(zhí)行函數(shù)比如

<div c-bind:tap="count++"></div>
<div c-bind:tap="handleTap1(); handleTap2()"></div>

動(dòng)態(tài)組件

component 接受兩個(gè)屬性

屬性名說(shuō)明
is接受一個(gè)計(jì)算屬性作為動(dòng)態(tài)渲染的標(biāo)簽名
shrinkcomponents接受 usingComponents 中的key值組成的字符串作為動(dòng)態(tài)組件選擇的范圍

注意,為了提高微信端的渲染效率,強(qiáng)烈建議加上 shrinkcomponents = "comp1,comp2,...",縮小動(dòng)態(tài)渲染的查找范圍,減少不必要的渲染開銷

<template>
  <view class="page-container">
    <view c-bind:tap="handleElementClick"><text>組件改變</text></view>
    <component is="{{currentComp}}" shrinkcomponents="comp,comp1"></component>
  </view>
</template>

<script>
class Index {
  data = {
    dataComp: 'comp',
  };
  computed = {
    currentComp() {
      return this.dataComp === 'comp' ? 'comp1' : 'comp';
    },
  };
  methods = {
    handleElementClick(a, b) {
      console.log('handleElementClick', arguments, a, b);
      this.dataComp = this.dataComp === 'comp' ? 'comp1' : 'comp';
    },
  };
}

export default new Index();
</script>

<script cml-type="json">
{
    "base": {
        "usingComponents": {
          "comp":"./comp",
          "comp1":"./comp1",
          "comp2":"./comp2",
          "comp3":"./comp3",
        }
    },
    "wx": {
        "navigationBarTitleText": "index",
        "backgroundTextStyle": "dark",
        "backgroundColor": "#E2E2E2"
    },
    "alipay": {
      "defaultTitle": "index",
      "pullRefresh": false,
      "allowsBounceVertical": "YES",
      "titleBarColor": "#ffffff"
    },
    "baidu": {
      "navigationBarBackgroundColor": "#ffffff",
      "navigationBarTextStyle": "white",
      "navigationBarTitleText": "index",
      "backgroundColor": "#ffffff",
      "backgroundTextStyle": "dark",
      "enablePullDownRefresh": false,
      "onReachBottomDistance": 50
    }
}
</script>

component 動(dòng)態(tài)組件上同樣支持綁定事件,傳遞屬性;

比如

<component
  is="{{currentComp}}"
  type="upcaseEvent"
  c-bind:upcaseEvent="handleUpcaseEvent"
  id="{{id}}"
></component>

Bug & Tips

注意 : 小程序端是通過條件判斷來(lái)模擬 component is 的效果的,所以不要在 component 標(biāo)簽上在在寫 c-if c-else c-else-if 等條件判斷

指令

c-if

根據(jù)表達(dá)式的真假值條件渲染元素

<div c-if="{{true}}">根據(jù)c-if的真假結(jié)果決定是否渲染</div>

c-else

  • 不需要表達(dá)式;
  • 限制:前一個(gè)兄弟元素必須有 c-if 或者 c-else-if

用法

<div c-if="{{1 > 0.5}}">
  Now you see me
</div>
<div c-else>
  Now you don't
</div>

c-else-if

  • 限制:前一個(gè)兄弟元素必須有 c-if 或者 c-else-if
<div c-if="{{type === 'A'}}">
  A
</div>
<div c-else-if="{{type === 'B'}}">
  B
</div>
<div c-else-if="{{type === 'C'}}">
  C
</div>
<div c-else>
  Not A/B/C
</div>

c-for

<view c-for="{{array}}" c-for-index="idx" c-for-item="itemName">
 <text> {{idx}}: {{itemName.message}}</text>
</view>

c-model

父組件

<view><text>c-model的使用</text></view>
<input type="text" c-model="{{modelValueTest}}" />
<text>{{modelValueTest}}</text>

<comp c-model="{{modelValueTest2}}"></comp>
<view><text>組件使其改變{{modelValueTest2}}</text></view>

子組件

<template>
  <view>
    <input type="text" :value="value" c-bind:input="handleInput" />
  </view>
</template>

<script>

methods = {
    handleInput(e){
      console.log('input',e);
      this.$cmlEmit('input', {
        value:  Date.now()
      })
    }
  }
}
</script>

** $cmlEmit 的事件名必須是 'input', 傳入的參數(shù)需要有一個(gè)更新的 value 作為 key, 其屬性值作為新值進(jìn)行更新;**

c-text

<view c-text="{{message}}"></view>

不支持組件的 c-text

c-show

<view c-show="{{elementShow}}">
    <text>測(cè)試元素c-show</text>
  </view>
<view><text>組件v-show</text></view>
<comp c-show="{{elementShow}}"></comp>
  • 使用 c-show 的元素不支持 同時(shí)有 style 屬性
  • elementShow 是來(lái)自 data 或者 computed 中的 key 值,或者 true/false

c-animation

傳入的值必須由createAnimation返回

<template>
  <text c-animation="{{animationData}}" c-bind:click="click">hello world</text>
</template>
<script>
import cml from 'cml目錄';

const animation = cml.createAnimation();

class Index {
  data = {
    animationData: {},
  };
  methods = {
    click: function() {
      this.animationData = animation
        .opacity(0.1)
        .step({})
        .export();
    },
  };
}
export default new Index();
</script>


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

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)