工程化

2020-05-14 14:20 更新

工程化是使用軟件工程的技術(shù)和方法對(duì)項(xiàng)目的開發(fā)、上線和維護(hù)進(jìn)行管理。CML 的工程化包含如下幾個(gè)大方面:

  • 1 模塊化,項(xiàng)目中的文件統(tǒng)一以模塊化的方式引入,包括.css,.js 以及定義的 interface 文件,這樣做有如下好處避免變量污染與命名沖突,提高代碼的復(fù)用率,提高代碼的可維護(hù)性。
  • 2 組件化,CML 定義了.cml 文件,以組件為單位,將一個(gè)組件需要的視圖、邏輯、樣式、配置采用單文件的形式進(jìn)行開發(fā),提高了代碼的可讀性。
  • 3 本地開發(fā)解決方案,CML 提供了大量的工程化方法,解決本地開發(fā)時(shí)的各種痛點(diǎn),包括提供 dev 服務(wù),Mock 數(shù)據(jù)等能力,達(dá)到不依賴后端實(shí)現(xiàn)開發(fā)的前后端分離。提供 熱更新,自動(dòng)刷新,調(diào)試窗口,線上資源代理等能力,提高本地開發(fā)時(shí)的效率。
  • 4 線上部署解決方案,CML 的構(gòu)建是完全配置化的,內(nèi)置線上線下兩種構(gòu)建模式,線上工程化相關(guān)功能包括線上靜態(tài)資源的路徑指定 基于文件內(nèi)容的文件指紋減少文件體積的代碼壓縮
  • 5 漸進(jìn)式使用,CML 提供了兩種漸進(jìn)式使用的方式,一種是將 CML 的組件導(dǎo)出成各端的原生組件,第二種方式是引用插件在 webpack 項(xiàng)目中使用 CML 組件與接口。

數(shù)據(jù) Mock

如何 Mock API 請(qǐng)求

如果需要 Mock 多個(gè)域名的 API 請(qǐng)參見 API 多域名 Mock。

1、 使用內(nèi)置網(wǎng)絡(luò)請(qǐng)求接口發(fā)起網(wǎng)絡(luò)請(qǐng)求。例如:

import cml from 'chameleon-api';
cml
  .get({
    url: '/api/getdriver',
  })
  .then(
    (res) => {
      cml.showToast({
        message: JSON.stringify(res),
        duration: 2000,
      });
    },
    (err) => {
      cml.showToast({
        message: JSON.stringify(err),
        duration: 2000,
      });
    },
  );

調(diào)用方法的參數(shù) url 中只需要寫 api 的路徑。那么本地 dev 開發(fā)模式如何 mock 這個(gè) api 請(qǐng)求以及 build 線上模式如何請(qǐng)求線上地址,就需要在配置文件中配置 apiPrefix。

2、配置 apiPrefix dev 開發(fā)模式和 build 模式配置的 apiPrefix 會(huì)拼接到網(wǎng)絡(luò)請(qǐng)求的 url 前,dev 模式不配置時(shí),默認(rèn)為當(dāng)前啟動(dòng) Web 服務(wù)的 ip+端口。上面的例子中如果本地 ip 為 198.168.1.1 啟動(dòng)端口為 8000。dev 模式發(fā)起的網(wǎng)絡(luò)請(qǐng)求為 198.168.1.1:8000/api/getdriver, build 模式發(fā)起的網(wǎng)絡(luò)請(qǐng)求為 http://api.chameleon.com/api/getdriver。

// 設(shè)置 API 請(qǐng)求前綴
const apiPrefix = 'http://api.chameleon.com';
cml.config.merge({
  wx: {
    dev: {},
    build: {
      apiPrefix,
    },
  },
});

3、 配置本地 Mock 數(shù)據(jù)

前兩步操作實(shí)現(xiàn)了網(wǎng)絡(luò)請(qǐng)求 dev 模式請(qǐng)求本地,build 模式請(qǐng)求線上,這一步就講解如何 mock 本地請(qǐng)求數(shù)據(jù)。

在/mock/api/文件夾下創(chuàng)建 Mock 數(shù)據(jù)的 js 文件。文件內(nèi)容格式如下:

module.exports = [
  {
    method: 'get',
    path: '/api/getdriver',
    controller: function(req, res, next) {
      console.log('/api/driver/getList');
      res.json({
        total: 100,
        driverList: [],
      });
    },
  },
];
  • method 指定請(qǐng)求方法,默認(rèn)值['get','post']
  • path 指定請(qǐng)求的路徑
  • controller 是 express 的中間件形式,在中間件中可以做任何操作最后調(diào)用 res 的方法返回結(jié)果。

啟動(dòng) dev 模式后,通過(guò) ip+端口+path 即可訪問(wèn)配置的 api 請(qǐng)求。結(jié)合上面講到的網(wǎng)絡(luò)請(qǐng)求方法,即可實(shí)現(xiàn)本地的 API 數(shù)據(jù) Mock。

擴(kuò)展 如何在本地 dev 模式請(qǐng)求線上數(shù)據(jù)?

可以在 mock 文件的 controller 中請(qǐng)求對(duì)應(yīng)的線上數(shù)據(jù)

如何 mock php 模板下發(fā)數(shù)據(jù)

/mock/template/文件夾下存放的 php 文件是下發(fā)的模板數(shù)據(jù),php 文件內(nèi)將下發(fā)的數(shù)據(jù)賦值給$CML 對(duì)象,例如:

<?php
  $chameleon = array(
    "errno" => 0,
    "errmsg" => "",
    "pageData" => array(
      "pageInfo" => array(
        "title" => "chameleon",
        "content" => "chameleon跨端"
      )
    )
  );
?>

在模板中通過(guò)變量pageData,errno,errmsg接收。

<script>
  var pageData = {json_encode($pageData)}
  var errno = {json_encode($errno)}
  var errmsg = {json_encode($errmsg)}
</script>

同時(shí)還模擬了與模板下發(fā)的 pageData 相同的 ajax 請(qǐng)求,只需在當(dāng)前訪問(wèn)頁(yè)面的 url 上添加 puredata=1 參數(shù)。

{
  errno: 0,
  errmsg: '',
  pageData: {
    pageInfo: {
      title: 'chameleon',
      content: 'chameleon跨端'
    }
  }
}

API 多域名 Mock

在數(shù)據(jù) Mock 一節(jié)講述了如何進(jìn)行 API 數(shù)據(jù)的 mock,但是只局限于所有 api 請(qǐng)求都是相同域名的情況,工作中可能出現(xiàn)一個(gè)項(xiàng)目請(qǐng)求多個(gè)域名的 api 接口,本節(jié)將講解如和進(jìn)行多域名的 mock。

版本要求

  • chameleon-tool >= 0.2.1
  • chameleon-api >= 0.3.1

chameleon.config.js 中配置多域名信息

domain 對(duì)象配置多域名的信息。 domain, Object 類型。 配置在 base 對(duì)象中,可以作為所有平臺(tái)的公共配置,dev 模式中配置的localhost會(huì)替換成當(dāng)前 dev 模式啟動(dòng)的 web 服務(wù) ip+端口。

例如:

cml.config.merge({
  base: {
    dev: {
      domain: {
        domain1: "localhost",
        domain2: "localhost"
      },
    },
    build: {
      domain: {
        domain1: "http://api.cml.com",
        domain2: "http://api2.cml.com"
      },
    }
  },
})

使用 chameleon-api 發(fā)網(wǎng)絡(luò)請(qǐng)求

chameleon-api的網(wǎng)絡(luò)請(qǐng)求get、post、request方法中添加 domain 參數(shù)。 chameleon.config.js中添加的domain對(duì)象配置,在項(xiàng)目中可以通過(guò)process.env.domain變量訪問(wèn)。

例如:

import cml from 'chameleon-api';
cml
  .get({
    domain: process.env.domain.domain1,
    url: '/api/getMessage',
  })
  .then(
    (res) => {
      cml.showToast({
        message: JSON.stringify(res),
        duration: 2000,
      });
    },
    (err) => {
      cml.showToast({
        message: JSON.stringify(err),
        duration: 2000,
      });
    },
  );

配置 Mock 數(shù)據(jù)

前兩步操作實(shí)現(xiàn)了網(wǎng)絡(luò)請(qǐng)求 dev 模式請(qǐng)求本地,build 模式請(qǐng)求線上,這一步就講解如何 mock 本地多域名的請(qǐng)求數(shù)據(jù)。

在/mock/api/文件夾下創(chuàng)建 Mock 數(shù)據(jù)的 js 文件。文件內(nèi)容格式如下:

module.exports = [
  {
    domainKey: 'domain1',
    request: [
      {
        method: ['get', 'post'],
        path: '/api/getMessage',
        controller: function(req, res, next) {
          res.json({
            total: 0,
            message: [
              {
                name: 'HelloCML domain1',
              },
            ],
          });
        },
      },
    ],
  },
  {
    domainKey: 'domain2',
    request: [
      {
        method: ['get', 'post'],
        path: '/api/getMessage',
        controller: function(req, res, next) {
          res.json({
            total: 0,
            message: [
              {
                name: 'domain2!',
              },
            ],
          });
        },
      },
    ],
  },
];
  • domainKey 指定 Mock 的域名,對(duì)應(yīng) chameleon.config.js 中 domain 對(duì)象的 key 值。
  • method 指定請(qǐng)求方法,默認(rèn)值['get','post']
  • path 指定請(qǐng)求的路徑
  • controller 是 express 的中間件形式,在中間件中可以做任何操作最后調(diào)用 res 的方法返回結(jié)果。

資源定位

靜態(tài)資源引用

模板中引用靜態(tài)資源,不能直接將資源的路徑寫在模板中,而是要通過(guò) js 中 require 該靜態(tài)資源得到變量,在模板中引用該變量。 該路徑會(huì)根據(jù)項(xiàng)目配置的 publicPath自動(dòng)替換成正確路徑。利用該功能可以實(shí)現(xiàn) 靜態(tài)資源 開發(fā)路徑和部署路徑之間的 分離,開發(fā)者只需要寫相對(duì)路徑,線上可以通過(guò)設(shè)置publicPath指定任意路徑。

<template>
  <!-- 
錯(cuò)誤形式
<image src="./images/logo.png" /> 
-->
  <!-- 正確形式 -->
  <image src="{{imgPath}}" />
</template>
<script>
class Index {
  data = {
    imgPath: require('./images/logo.png'),
  };
}
export default new Index();
</script>

圖片 base64

支持在引用圖片 url 后面添加inline參數(shù),以指定圖片的 base64 格式,例如:

<script>
class Index {
  data = {
    imgPath: require('./images/logo.png?__inline'),
  };
}
export default new Index();
</script>

代理模式開發(fā)

調(diào)試線上編譯處理過(guò)的非可讀性代碼,可以使用本功能

通過(guò)簡(jiǎn)單的 CML 配置將線上文件代理到線下的開發(fā)環(huán)境,這樣就可以通過(guò)修改線下的源碼 debug 線上頁(yè)面了,使用方法如下:

第一步

chameleon.config.js 中開啟代理模式:

{
    ...
    proxy: {
        enable: true,
    }
    ...
}

第二步

執(zhí)行以下命令

cml dev

第三步

根據(jù)調(diào)試面板打印的信息給手機(jī)安裝證書

image

第四步

根據(jù)上圖提示將手機(jī)代理到相應(yīng)的端口

完成以上步驟就可以進(jìn)行代理開發(fā)了。

默認(rèn)代理了 Weex 和 Web 端的 js 和 css 文件,如需代理更多文件,可以 添加 mapremote 配置,方法如下:

{
    ...
    proxy: {
        enable: true,
        mapremote: [{
            from: 'https://a.b.com/weex/aaa_(.+).js',
            to: 'http://localhost:8000/weex/aaa.js'
        },{
            from: 'https://a.b.com/weex/bbb_(.+).js',
            to: 'http://localhost:8000/weex/bbb.js'
        }]
    }
    ...
}

cmlUrl

一個(gè) cmlUrl 能在多端運(yùn)行,在普通瀏覽器/webview 運(yùn)行 Web 端,小程序運(yùn)行小程序端,Native 渲染(weex)則拉取對(duì)應(yīng)的 JS Bundle 并展現(xiàn),完整地址如下,使用場(chǎng)景包含:

  • a.跨應(yīng)用頁(yè)面之間跳轉(zhuǎn)使用open 接口
  • b.服務(wù)端下發(fā)給端(weex/瀏覽器/小程序)進(jìn)行跳轉(zhuǎn)

https://h5地址? cml_addr=jsbundle地址& path=路由path(通用字段)& envVersion=要打開的小程序版本(通用字段)& weixin_appid=123456& weixin_path=微信小程序路由path& weixin_envVersion=要打開的微信小程序版本& baidu_appid=123456& baidu_path=百度小程序路由path& baidu_envVersion=要打開的百度小程序版本& alipay_appid=123456& alipay_path=支付寶小程序路由path

參數(shù)作用說(shuō)明
h5地址H5端的地址或者用于提示bundle出錯(cuò)的h5地址如果你沒有h5地址,可以選擇將h5地址寫為jsbundle地址(后面的cml_addr=jsbundle地址依然需要)。
cml_addr描述weex/rn js bundle地址內(nèi)部非使用sdk開發(fā)者暫時(shí)使用cml_addr字段
path描述應(yīng)用里面的頁(yè)面路由, 即路由里面的 path 值若未填寫weixin_path, baidu_path, alipay_path時(shí), 統(tǒng)一使用該字段
envVersion要打開的小程序版本有效值 develop(開發(fā)版),trial(體驗(yàn)版),release(正式版) ,僅在當(dāng)前小程序?yàn)殚_發(fā)版或體驗(yàn)版時(shí)此參數(shù)有效(僅支持微信小程序支付寶小程序)
weixin_appid描述微信小程序的app id微信小程序跳轉(zhuǎn)需要 appid
weixin_path描述應(yīng)用里面的頁(yè)面路由(目標(biāo)微信小程序?yàn)榉?CML 項(xiàng)目時(shí)可用)
weixin_envVersion要打開的小程序版本有效值 develop(開發(fā)版),trial(體驗(yàn)版),release(正式版) ,僅在當(dāng)前小程序?yàn)殚_發(fā)版或體驗(yàn)版時(shí)此參數(shù)有效(該字段僅對(duì)微信小程序跳轉(zhuǎn)生效)
baidu_appid描述百度小程序的appKey百度小程序跳轉(zhuǎn)需要 appKey
baidu_path描述應(yīng)用里面的頁(yè)面路由(目標(biāo)百度小程序?yàn)榉?CML 項(xiàng)目時(shí)可用)
alipay_appid描述支付寶小程序的app id支付寶小程序跳轉(zhuǎn)需要 appid
alipay_path描述應(yīng)用里面的頁(yè)面路由(目標(biāo)支付寶小程序?yàn)榉?CML 項(xiàng)目時(shí)可用)
alipay_envVersion要打開的小程序版本有效值 develop(開發(fā)版),trial(體驗(yàn)版),release(正式版) ,僅在當(dāng)前小程序?yàn)殚_發(fā)版或體驗(yàn)版時(shí)此參數(shù)有效(該字段僅對(duì)支付寶小程序跳轉(zhuǎn)生效)

規(guī)范與校驗(yàn)

按照框架定義,有相應(yīng)的目錄規(guī)范,文件規(guī)范,文件內(nèi)容規(guī)范等,按照規(guī)范編寫代碼,可以最大程度的減少開發(fā)、調(diào)試時(shí)間。

另,框架提供了校驗(yàn)工具,讓開發(fā)者可以提前發(fā)現(xiàn)不符合規(guī)范的問(wèn)題,提高效率。

接口校驗(yàn)語(yǔ)法

接口是一系列方法的聲明,是一些方法特征的集合,一個(gè)接口只有方法的特征沒有方法的實(shí)現(xiàn),因此這些方法可以在不同的地方被不同的類實(shí)現(xiàn),而這些實(shí)現(xiàn)可以具有不同的行為(功能)進(jìn)行溝通。

校驗(yàn)配置

通過(guò)配置決定是否開啟接口的校驗(yàn)。 Object、Array、Nullable這三個(gè)類型默認(rèn)是不支持的,因?yàn)槲覀兘ㄗh更精確的校驗(yàn),可以通過(guò)配置文件開啟這三個(gè)類型。 具體參見多態(tài)校驗(yàn)控制的配置

類型說(shuō)明

注意:建議定義類型的時(shí)候取值為 Number String Boolean Null Undefined(Void) Object Array Function Date RegExp

目前 CML 接口定義支持簡(jiǎn)單類型和復(fù)合類型。

其中簡(jiǎn)單類型包括以下類型:

  • Number(number)
  • String(string)
  • Boolean(bool)
  • Undefined(void)
  • Null

復(fù)合類型包括以下類型:

  • Function
  • Object
  • Array
  • Date
  • RegExp

接口語(yǔ)法

接口的使用分兩個(gè)過(guò)程:

  1. 定義一個(gè)接口。
  2. 定義實(shí)現(xiàn)接口的類。
接口定義

范式

interface [接口名稱] {
    // 接口中的屬性
    [屬性名稱]: [類型],

    // 接口中的方法
   [方法名稱]([傳入?yún)?shù)1名稱]: [傳入?yún)?shù)1類型], [傳入?yún)?shù)2名稱]: [傳入?yún)?shù)2類型], ...): [返回類型]
}

舉例

// 一個(gè)名為interface1的接口
interface interface1 {
  // foo1: 傳入分別為string和number的兩個(gè)數(shù)據(jù),返回值類型為string值
  foo1(a: string, b: number): string;

  // foo2: 傳入分別為string和Callback(上文定義)的兩個(gè)數(shù)據(jù),返回值類型為bool值
  foo2(c: string, d: Callback): Boolean;
}
實(shí)現(xiàn)接口(定義類)

范式

class [類名稱] implaments [接口名稱] {

    // 實(shí)現(xiàn)接口中的屬性
    [屬性名稱]: [類型]

    // 實(shí)現(xiàn)接口中的方法
  [方法名稱]([傳入?yún)?shù)1名稱], [傳入?yún)?shù)2名稱], ...){
      return [返回值];
    }
}

舉例

// 實(shí)現(xiàn)一個(gè)名稱為Clazz,實(shí)現(xiàn)上文定義的interface1接口
class Clazz implaments interface1 {

    // 實(shí)現(xiàn)interface1定義的foo1方法,輸入值和輸出值要滿足定義
    foo1(a, b) {
        return 'hello ' + a + ' : ' + (b + 1);
    }

    // 實(shí)現(xiàn)interface1定義的foo2方法,輸入值和輸出值要滿足定義
    foo2(c, d) {
        return 'balabala...';
    }
}

復(fù)合類型的定義范式

type [類型名稱] = [類型定義]

不同的復(fù)合類型,類型定義也不相同,下面會(huì)對(duì)三種復(fù)合類型做詳細(xì)說(shuō)明。

Function 類型定義

范式

type [Function類型名稱] = ([傳入?yún)?shù)1名稱]: [傳入?yún)?shù)1類型], [傳入?yún)?shù)2名稱]: [傳入?yún)?shù)2類型], ...) => [返回類型]

舉例

// 定義一個(gè)傳參分別為number,string,bool類型的三個(gè)參數(shù),返回值為number的函數(shù)類型
type Callback = (a: number, b: string, c: bool) => number;
Object 類型定義

范式

type [Object類型名稱] = {
    [屬性名稱1]: [類型1],
    [屬性名稱2]: [類型2]
}

舉例

// 定義含有a,b,c三個(gè)屬性的復(fù)合類型
type Scheme = {
    a: string,
    b: bool,
    c: number
}
Array 類型定義

范式

type [Array類型名稱] = [
  [類型1]
]

舉例

// 定義名稱為arrayType1的數(shù)組類型,數(shù)組元素為number類型
type arrayType1 = [
    number
]
目前數(shù)組的詳細(xì)校驗(yàn)只能校驗(yàn)數(shù)組中的元素是同一種類型,即[1,2,3],而不能校驗(yàn) [1,2,'this is string'];
  • 如果想要校驗(yàn)?zāi)硞€(gè)入?yún)⒒蛘吆瘮?shù)的返回值是一個(gè)數(shù)組,但是數(shù)組中的值得類型不是同一類型
  • 或者只想簡(jiǎn)單的對(duì)某個(gè)對(duì)象進(jìn)行類型校驗(yàn),但是對(duì)象中具體的 key-value 值不想校驗(yàn); 那么在項(xiàng)目配置開啟['Object','Array']直接校驗(yàn)的前提下,可以如下寫**
interface EntryInterface {
  handleDate(arr: Array, o: Object): Array;
}
class Method implements EntryInterface {
  let arr = [1,2,3,'str'];
  let obj = {address:'China'}
  handleDate(arr,obj){
    return ['this is str',{name:"jhon"}];
  }
}

此時(shí)校驗(yàn)就只會(huì)校驗(yàn)入?yún)⒒蛘叻祷刂档脭?shù)據(jù)類型是否是 Array 或者 Object ,而不會(huì)深入校驗(yàn)數(shù)組或者對(duì)象中的元素;

復(fù)合類型中的相互嵌套

Function、Object、Array 三種復(fù)合類型可以互相嵌套:

// 定義一個(gè)傳參分別為number,string,bool類型的三個(gè)參數(shù),返回值為number的函數(shù)類型
type Callback = (a: number, b: string, c: bool) => number;

// 定義名稱為arrayType1的數(shù)組類型,數(shù)組元素為number類型
type arrayType1 = [
    number
]

// 定義名稱為Scheme的,含有Array類型和Function類型屬性的Object類型
type Scheme = {
    a: arrayType1,
    b: Callback,
}

// 定義名稱為Plan,含有Scheme類型和Callback的屬性的Object類型
type Plan = {
    a: string,
    b: Scheme,
    c: Callback
}

// 定義名稱為arrayType1類型,元素為Plan類型
type arrayType1 = [
    Plan
]
Date 類型的定義

如果函數(shù)參數(shù)或者返回值是 Date 數(shù)據(jù)類型,那么可以按照下面的方式進(jìn)行定義;

interface EntryInterface {
  handleDate(d: Date): Date;
}
class Method implements EntryInterface {
  handleDate(d) {
    return new Date();
  }
}

RegExp 類型的定義

如果函數(shù)參數(shù)或者返回值是 RegExp 數(shù)據(jù)類型,那么可以按照下面的方式進(jìn)行定義;

interface EntryInterface {
  handleDate(d: RegExp): RegExp;
}
class Method implements EntryInterface {
  handleDate(r) {
    return new RegExp();
  }
}

Maybe Types : 意味著該值可能是這種類型,但是也可能是 undefined 或者 null

注意如果要定義 Nullable(?Number)這樣的參數(shù),那么該參數(shù)的占位符是必須的

interface EntryInterface {
  acceptsMaybeNumber(a: ?Number, b: String, c: Boolean): Undefined;
}
class Method implements EntryInterface {
  acceptsMaybeNumber(a, b, c) {}
}
acceptsMaybeNumber(42, 'str', true); // Works!
acceptsMaybeNumber(undefined, 'str', true); // Works!
acceptsMaybeNumber(null, 'str', true); // Works!
acceptsMaybeNumber('42', 'str', true); // Error!

//**注意如果要定義Nullable(?Number)這樣的參數(shù),那么該參數(shù)的占位符是必須的,在校驗(yàn)入?yún)⒌臅r(shí)候,會(huì)按照interface中定義的順序,有序的校驗(yàn)傳入的參數(shù)是否和interface中定義的數(shù)據(jù)參數(shù)類型一直,(?Number)這種定義的校驗(yàn)其實(shí)只是說(shuō)明這個(gè)參數(shù)可以是 null  undefined number類型的數(shù)據(jù),但是是必須傳遞的**
acceptsMaybeNumber('str', true); // Error!

全局變量校驗(yàn)

CML 的代碼最終會(huì)運(yùn)行在多端框架中,每一個(gè)端都會(huì)有一些特有的全局變量,chameleon 內(nèi)部維護(hù)了一個(gè)各端全局變量的散列表如下表所示。

全局變量校驗(yàn)校驗(yàn)的是某一端運(yùn)行的代碼中存在其他端的全局變量并且不是當(dāng)前端的全局變量。例如非微信小程序端的 代碼中不能出現(xiàn)wx全局變量,非百度小程序的代碼中不能出現(xiàn)swan全局變量。

可以通過(guò)項(xiàng)目配置決定是否開啟全局變量校驗(yàn),還可以配置哪些文件是白名單文件進(jìn)行校驗(yàn)。

全局變量
微信小程序["wx"]
百度小程序["swan"]
weex["weex"]
支付寶小程序["my"]
web
["postMessage","blur","focus","close","frames","self","window","parent","opener","top","length","closed","location","document","origin","name","history","locationbar","menubar","personalbar","scrollbars","statusbar","toolbar","status","frameElement","navigator","customElements","external","screen","innerWidth","innerHeight","scrollX","pageXOffset","scrollY","pageYOffset","screenX","screenY","outerWidth","outerHeight","devicePixelRatio","clientInformation","screenLeft","screenTop","defaultStatus","defaultstatus","styleMedia","onanimationend","onanimationiteration","onanimationstart","onsearch","ontransitionend","onwebkitanimationend","onwebkitanimationiteration","onwebkitanimationstart","onwebkittransitionend","isSecureContext","onabort","onblur","oncancel","oncanplay","oncanplaythrough","onchange","onclick","onclose","oncontextmenu","oncuechange","ondblclick","ondrag","ondragend","ondragenter","ondragleave","ondragover","ondragstart","ondrop","ondurationchange","onemptied","onended","onerror","onfocus","oninput","oninvalid","onkeydown","onkeypress","onkeyup","onload","onloadeddata","onloadedmetadata","onloadstart","onmousedown","onmouseenter","onmouseleave","onmousemove","onmouseout","onmouseover","onmouseup","onmousewheel","onpause","onplay","onplaying","onprogress","onratechange","onreset","onresize","onscroll","onseeked","onseeking","onselect","onstalled","onsubmit","onsuspend","ontimeupdate","ontoggle","onvolumechange","onwaiting","onwheel","onauxclick","ongotpointercapture","onlostpointercapture","onpointerdown","onpointermove","onpointerup","onpointercancel","onpointerover","onpointerout","onpointerenter","onpointerleave","onafterprint","onbeforeprint","onbeforeunload","onhashchange","onlanguagechange","onmessage","onmessageerror","onoffline","ononline","onpagehide","onpageshow","onpopstate","onrejectionhandled","onstorage","onunhandledrejection","onunload","performance","stop","open","alert","confirm","prompt","print","requestAnimationFrame","cancelAnimationFrame","requestIdleCallback","cancelIdleCallback","captureEvents","releaseEvents","getComputedStyle","matchMedia","moveTo","moveBy","resizeTo","resizeBy","getSelection","find","webkitRequestAnimationFrame","webkitCancelAnimationFrame","fetch","btoa","atob","createImageBitmap","scroll","scrollTo","scrollBy","onappinstalled","onbeforeinstallprompt","crypto","ondevicemotion","ondeviceorientation","ondeviceorientationabsolute","indexedDB","webkitStorageInfo","sessionStorage","localStorage","chrome","visualViewport","speechSynthesis","webkitRequestFileSystem","webkitResolveLocalFileSystemURL","openDatabase","applicationCache","caches","whichAnimationEvent","animationendEvent","infinity","SETTING","AppView","ExtensionOptions","ExtensionView","WebView","iconPath","_app","_ZOOM_","Feed","md5","$","jQuery","Search","windmill","Lethargy","alertTimeOut","supportApps","lethargyX","lethargyY","iView","onModuleResLoaded","iEditDelete","infinityDrag","i","array","TEMPORARY","PERSISTENT","addEventListener","removeEventListener","dispatchEvent"]

代碼規(guī)范校驗(yàn)

核心文件校驗(yàn)

根據(jù)chameleon目錄結(jié)構(gòu),確定核心文件的位置,保證 CML 項(xiàng)目能夠正常運(yùn)行。

目錄結(jié)構(gòu)
├── chameleon.config.js                 // 項(xiàng)目的配置文件
├── dist                                // 打包產(chǎn)出目錄
├── mock                                // 模擬數(shù)據(jù)目錄
├── node_modules                        // npm包依賴
├── package.json
└── src                                 // 項(xiàng)目源代碼
    ├── app                             // app入口
    ├── components                      // 組件文件夾
    ├── pages                           // 頁(yè)面文件夾
    ├── router.config.json              // 路由配置文件
    └── store                           // 全局狀態(tài)管理
核心文件列表

會(huì)對(duì)以下核心文件進(jìn)行檢查:

chameleon.config.js
src/app/app.cml
src/router.config.json

CML 文件規(guī)范校驗(yàn)

CML 文件規(guī)范校驗(yàn)包括校驗(yàn)以下三個(gè)規(guī)范:

  • CML 文件命名規(guī)范
  • CML 文件內(nèi)容規(guī)范
  • CML Interface 內(nèi)容規(guī)范
CML 文件命名規(guī)范

以 cml 后綴結(jié)尾的文件分兩種情況:

多端實(shí)現(xiàn)完全一致組件命名格式

[component name].cml

組件所有邏輯實(shí)現(xiàn)在同一文件中

舉例:

demo.cml

多端實(shí)現(xiàn)不一致組件命名格式

[component name].[weex|wx|alipay|baidu|web].cml

組件文件名按照適配端命名,需要同一目錄下的 interface 文件組合使用

[component name].interface

舉例:

demo.interface
demo.weex.cml
demo.wx.cml
demo.alipay.cml
demo.baidu.cml
demo.web.cml
CML 文件內(nèi)容規(guī)范

cml 文件中可能包括以下幾個(gè)字段標(biāo)簽

  1. template(template 規(guī)范):標(biāo)簽中書寫組件的視圖層結(jié)構(gòu),chameleon 自定義了一套標(biāo)簽語(yǔ)言,結(jié)合基礎(chǔ)組件、事件系統(tǒng),可以構(gòu)建出頁(yè)面的結(jié)構(gòu)。
  2. style(CMSS 規(guī)范):標(biāo)簽中書寫組件的樣式, 描述視圖中的元素樣式。
  3. script(script 規(guī)范):標(biāo)簽中填充編寫組件邏輯層響應(yīng)頁(yè)面操作的代碼。
  4. json(json 規(guī)范):標(biāo)簽中書寫組件的配置信息。

舉例:

// demo.cml code
<template>
  <view id="banner"> </view>
</template>
<script>
class Index {}
export default new Index();
</script>
<style scoped>
#banner {
  background-color: #ff0000;
}
</style>
<script cml-type="json">
{
  "base":{
    "usingComponents": {
    }
  },
  "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
  },
  "web": {
  },
  "weex": {
  }
}
</script>
CML Interface 內(nèi)容規(guī)范

.interface 后綴文件用于定義多態(tài)組件的接口。

接口是一系列方法的聲明,是一些方法特征的集合,一個(gè)接口只有方法的特征沒有方法的實(shí)現(xiàn),因此這些方法可以在不同的地方被不同的類實(shí)現(xiàn),而這些實(shí)現(xiàn)可以具有不同的行為(功能)進(jìn)行溝通。

類型說(shuō)明

注意:建議定義類型的時(shí)候取值為 Number String Boolean Null Undefined(Void) Object Array Function Date RegExp

目前 CML 接口定義支持簡(jiǎn)單類型和復(fù)合類型。

其中簡(jiǎn)單類型包括以下類型:

  • Number(number)
  • String(string)
  • Boolean(bool)
  • Undefined(void)
  • Null

復(fù)合類型包括以下類型:

  • Function
  • Object
  • Array
  • Date
  • RegExp
  • Promise

接口語(yǔ)法

接口的使用分兩個(gè)過(guò)程:

  1. 定義一個(gè)接口。
  2. 定義實(shí)現(xiàn)接口的類。

接口定義

范式:

    interface [接口名稱] {
        // 接口中的屬性
        [屬性名稱]: [類型],
        // 接口中的方法
       [方法名稱]([傳入?yún)?shù)1名稱]: [傳入?yún)?shù)1類型], [傳入?yún)?shù)2名稱]: [傳入?yún)?shù)2類型], ...): [返回類型]
    }

舉例:

// 一個(gè)名為interface1的接口
interface interface1 {
  // foo1: 傳入分別為string和number的兩個(gè)數(shù)據(jù),返回值類型為string值
  foo1(a: string, b: number): string;
  // foo2: 傳入分別為string和Callback(上文定義)的兩個(gè)數(shù)據(jù),返回值類型為bool值
  foo2(c: string, d: Callback): Boolean;
}

實(shí)現(xiàn)接口(定義類)

范式:

    class [類名稱] implaments [接口名稱] {
        // 實(shí)現(xiàn)接口中的屬性
        [屬性名稱]: [類型]
        // 實(shí)現(xiàn)接口中的方法
      [方法名稱]([傳入?yún)?shù)1名稱], [傳入?yún)?shù)2名稱], ...){
          return [返回值];
        }
    }

舉例:

    // 實(shí)現(xiàn)一個(gè)名稱為Clazz,實(shí)現(xiàn)上文定義的interface1接口
    class Clazz implaments interface1 {
        // 實(shí)現(xiàn)interface1定義的foo1方法,輸入值和輸出值要滿足定義
        foo1(a, b) {
            return 'hello ' + a + ' : ' + (b + 1);
        }
        // 實(shí)現(xiàn)interface1定義的foo2方法,輸入值和輸出值要滿足定義
        foo2(c, d) {
            return 'balabala...';
        }
    }

復(fù)合類型的定義范式

type[類型名稱] = [類型定義];

不同的復(fù)合類型,類型定義也不相同,下面會(huì)對(duì)三種復(fù)合類型做詳細(xì)說(shuō)明。

Function 類型定義

范式:

    type [Function類型名稱] = ([傳入?yún)?shù)1名稱]: [傳入?yún)?shù)1類型], [傳入?yún)?shù)2名稱]: [傳入?yún)?shù)2類型], ...) => [返回類型]

舉例:

// 定義一個(gè)傳參分別為number,string,bool類型的三個(gè)參數(shù),返回值為number的函數(shù)類型
type Callback = (a: number, b: string, c: boolean) => number;

Object 類型定義

范式:

type[Object類型名稱] = {
  [屬性名稱1]: [類型1],
  [屬性名稱2]: [類型2],
};

舉例:

// 定義含有a,b,c三個(gè)屬性的復(fù)合類型
type Scheme = {
  a: string,
  b: boolean,
  c: number,
};

Array 類型定義

范式:

type[Array類型名稱] = [[類型1]];

舉例:

// 定義名稱為arrayType1的數(shù)組類型,數(shù)組元素為number類型
type arrayType1 = [number];

復(fù)合類型中的相互嵌套

Function、Object、Array 三種復(fù)合類型可以互相嵌套:

// 定義一個(gè)傳參分別為number,string,bool類型的三個(gè)參數(shù),返回值為number的函數(shù)類型
type Callback = (a: number, b: string, c: boolean) => number;
// 定義名稱為arrayType1的數(shù)組類型,數(shù)組元素為number類型
type arrayType1 = [number];
// 定義名稱為Scheme的,含有Array類型和Function類型屬性的Object類型
type Scheme = {
  a: arrayType1,
  b: Callback,
};
// 定義名稱為Plan,含有Scheme類型和Callback的屬性的Object類型
type Plan = {
  a: string,
  b: Scheme,
  c: Callback,
};
// 定義名稱為arrayType1類型,元素為Plan類型
type arrayType1 = [Plan];

Promise 類型的定義

對(duì)于 async 函數(shù),由于該函數(shù)調(diào)用之后的返回值是 Promise 對(duì)象,所以這樣的函數(shù)的返回值要聲明成 Promise;

interface EntryInterface {
  appEntry(): Promise;
  appEntry2(): Promise;
}

在 methods 中

class Method implements EntryInterface {
  async appEntry(num) {}
  appEntry2() {
    return new Promise((resolve, reject) => {
      setTimeout(resolve, 2000);
    });
  }
}

Date 類型的定義

如果函數(shù)參數(shù)或者返回值是 Date 數(shù)據(jù)類型,那么可以按照下面的方式進(jìn)行定義;

interface EntryInterface {
  handleDate(d: Date): Date;
}
class Method implements EntryInterface {
  handleDate(d) {
    return new Date();
  }
}

RegExp 類型的定義

如果函數(shù)參數(shù)或者返回值是 RegExp 數(shù)據(jù)類型,那么可以按照下面的方式進(jìn)行定義;

interface EntryInterface {
  handleDate(d: RegExp): RegExp;
}
class Method implements EntryInterface {
  handleDate(r) {
    return new RegExp();
  }
}

模板規(guī)范校驗(yàn)

該文檔匯集模板校驗(yàn)支持所有檢查點(diǎn),附錄有模板格式規(guī)范

模板語(yǔ)言校驗(yàn)

模板可以指定模板語(yǔ)言,指定方式為在 template 標(biāo)簽上指定 lang 屬性, 其合法值為 "cml" 和 "vue"。

校驗(yàn)點(diǎn):

  • template 可以忽略 lang 屬性,此時(shí)默認(rèn)值為 cml
  • template lang 屬性如果指定,則必須為 "cml" 或者 "vue"
報(bào)錯(cuò)信息:'the tag template lang attribute: "<%= lang %>" is not valid'.
模板 template 標(biāo)簽校驗(yàn)

校驗(yàn)點(diǎn):每個(gè)模板只能 且必須有一對(duì) template 根標(biāo)簽。

報(bào)錯(cuò)信息:"Each template can only have one group of template tags."
模板內(nèi) tags 校驗(yàn)

每個(gè)模板都有一個(gè)模板語(yǔ)言和一個(gè)平臺(tái)類型,其中模板語(yǔ)言由 template 的 lang 屬性指定,平臺(tái)類型由模板文件的文件名解析出來(lái)。 對(duì)于多態(tài)組件平臺(tái)類型可以直接從文件名解析出來(lái), 比如 index.web.cml, index.weex.cml, index.wx.cml, index.alipay.cml, index.baidu.cml, 對(duì)應(yīng)的平臺(tái)類型分別為 web, weex, wx, alipay, baidu。 對(duì)于單文件組件,由于其模板要跨三端,故模板中只能使用 CML 原生支持的內(nèi)建標(biāo)簽。

校驗(yàn)點(diǎn):

  • 單文件組件只能使用 CML 內(nèi)建標(biāo)簽,使用非內(nèi)建標(biāo)簽校驗(yàn)不通過(guò)。CML 內(nèi)建標(biāo)簽有: ['template', 'view', 'text', 'block', 'scroller', 'list', 'cell', 'image', 'switch', 'video', 'input', 'button', 'radio', 'checkbox', 'page', 'router-view', 'slot']
  • src/app/app.cml 項(xiàng)目啟動(dòng)文件中可以使用app組件,且只有store和router-config屬性
  • 多態(tài)組件可以使用 CML 內(nèi)建標(biāo)簽, 加上各平臺(tái)類型所支持的原生標(biāo)簽,使用其他標(biāo)簽驗(yàn)證不同過(guò)。在使用平臺(tái)類型支持的原生標(biāo)簽時(shí),必須使用 'origin-' 為前綴。比如: 在 wx 平臺(tái)下使用 swiper 標(biāo)簽,那么在模板里的寫法是 'origin-swiper'. 各個(gè)平臺(tái)類型支持的原生標(biāo)簽列舉如下:web 平臺(tái)原生支持標(biāo)簽: ['a', 'abbr', 'acronym', 'address', 'applet', 'area', 'article', 'aside', 'audio', 'b', 'base', 'basefont', 'bdi', 'bdo', 'big', 'blockquote', 'body', 'br', 'button', 'canvas', 'caption', 'center', 'cite', 'code', 'col', 'colgroup', 'command', 'datalist', 'dd', 'del', 'details', 'dir', 'div', 'dfn', 'dialog', 'dl', 'dt', 'em', 'embed', 'fieldset', 'figcaption', 'figure', 'font', 'footer', 'form', 'frame', 'frameset', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hr', 'html', 'i', 'iframe', 'img', 'input', 'ins', 'kbd', 'keygen', 'label', 'legend', 'li', 'link', 'map', 'mark', 'menu', 'menuitem', 'meta', 'meter', 'nav', 'noframes', 'noscript', 'object', 'ol', 'optgroup', 'option', 'output', 'p', 'param', 'pre', 'progress', 'q', 'rp', 'rt', 'ruby', 's', 'samp', 'script', 'section', 'select', 'small', 'source', 'span', 'strike', 'strong', 'style', 'slot', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'textarea', 'tfoot', 'th', 'thead', 'time', 'title', 'tr', 'track', 'tt', 'u', 'ul', 'var', 'video', 'wbr']weex 平臺(tái)原生支持標(biāo)簽: ['a', 'div', 'image', 'indicator', 'input', 'list', 'cell', 'recycle-list', 'loading', 'refresh', 'scroller', 'slider', 'textarea', 'text', 'richtext', 'video', 'waterfall', 'web']wx 平臺(tái)原生支持標(biāo)簽: ['template', 'view', 'block', 'scroll-view', 'swiper', 'movable-view', 'movable-area', 'cover-view', 'cover-image', 'icon', 'text', 'rich-text', 'progress', 'lable', 'input', 'form', 'checkbox', 'picker', 'picker-view', 'radio', 'switch', 'slider', 'textarea', 'navigator', 'functional-page-navigator', 'camera', 'live-player', 'live-pusher', 'map', 'open-data', 'web-view', 'ad', 'official-account', 'slot']alipay 平臺(tái)原生支持標(biāo)簽: ['view', 'swiper', 'scroll-view', 'cover-view', 'movable-view', 'text', 'icon', 'progress', 'rich-text', 'button', 'form', 'label', 'input', 'textarea', 'radio', 'checkbox', 'switch', 'slider', 'picker-view', 'picker', 'navigator', 'image', 'canvas', 'map', 'webview']baidu 平臺(tái)原生支持標(biāo)簽: ['view', 'scroll-view', 'swiper', 'movable-area', 'cover-view', 'cover-image', 'icon', 'text', 'rich-text', 'progress', 'animation-view', 'button', 'checkbox', 'form', 'input', 'label', 'picker', 'radio', 'slider', 'switch', 'textarea', 'navigator', 'audio', 'image', 'video', 'camera', 'ar-camera', 'live-player', 'map', 'canvas', 'open-data', 'web-view']
報(bào)錯(cuò)信息:'tag: "<%= tag %>" is either not allowed in this template or not referenced as a component'
模板指令校驗(yàn)

除引入的 平臺(tái)原生組件對(duì)應(yīng)的標(biāo)簽和'origin-'為前綴的原生標(biāo)簽,每個(gè)模板只能夠使用 template lang 指定 模板語(yǔ)言對(duì)應(yīng)的指令。Chameleon 現(xiàn)只提供兩種模板語(yǔ)言 'vue' 和 'cml'。其對(duì)應(yīng)的指令列舉如下:

  • lang='cml' 支持的指令:['c-if', 'c-else', 'c-else-if', 'c-for', 'c-for-index', 'c-for-item', 'c-model', 'c-text', 'c-show', 'c-bind', 'c-catch', 'c-key']
  • lang='vue' 支持的指令:['v-if', 'v-else', 'v-else-if', 'v-for', 'v-on', 'v-bind', 'v-html', 'v-show', 'v-model', 'v-pre', 'v-once', 'slot-scope', 'is', '@', ':']

校驗(yàn)點(diǎn):

  • 單文件模板只能使用模板語(yǔ)言對(duì)應(yīng)的指令,使用模板語(yǔ)言指令之外的指令校驗(yàn)不通過(guò)
  • 多態(tài)組件各平臺(tái)文件中以 'origin-' 為前綴的平臺(tái)類型原生標(biāo)簽上必須使平臺(tái)原生指令,不能使用 Chameleon 內(nèi)置指令,使用此外其他指令校驗(yàn)不通過(guò)。各平臺(tái)支持原生指令列舉如下:web 平臺(tái)支持指令: ['v-if', 'v-else', 'v-else-if', 'v-for', 'v-on', 'v-bind', 'v-html', 'v-show', 'v-model', 'v-pre', 'v-once', 'slot-scope', 'is', '@', ':']weex 平臺(tái)支持指令同 vue.js 框架: ['v-if', 'v-else', 'v-else-if', 'v-for', 'v-on', 'v-bind', 'v-html', 'v-show', 'v-model', 'v-pre', 'v-once', 'slot-scope', 'is', '@', ':']wx 平臺(tái)支持指令:['wx:if', 'wx:elif', 'wx:else', 'wx:for', 'wx:for-item', 'wx:for-index', 'wx:key', 'bindtap', 'catchtap']
  • 多態(tài)組件通過(guò) usingComponents 配置引入的 第三方 平臺(tái)原生組件,組件對(duì)應(yīng)的標(biāo)簽上只能夠使用平臺(tái)原生指令,不能使用 Chameleon 內(nèi)置指令,使用此外其他指令校驗(yàn)不通過(guò)。
報(bào)錯(cuò)信息:'directive "<%= attribute %>" is not allowed to be used in this template, as the template language is set to "<%= lang %>"'
報(bào)錯(cuò)信息:'tag "<%= name %>" is prefixed with "origin-" directive, so it's not allowed to use a CML built-in directive:"<%= directive %>"'
報(bào)錯(cuò)信息:'tag "<%= name %>" is a third party imported component, so it's not allowed to use a CML built-in directive:"<%= directive %>"'
組件屬性和事件名稱校驗(yàn)

在使用組件的時(shí),會(huì)對(duì)使用過(guò)程中屬性名和綁定的事件名稱進(jìn)行校驗(yàn)。組件屬性校驗(yàn)分為內(nèi)建組件與自定義組件兩部分。

校驗(yàn)點(diǎn):

  • 內(nèi)建組件:使用的屬性名和綁定事件必須在組件內(nèi)有定義否則校驗(yàn)不通過(guò) - CML 內(nèi)建組件有:['template', 'view', 'text', 'block', 'scroller', 'list', 'cell', 'image', 'switch', 'video', 'input', 'textarea', 'richtext', 'button', 'radio', 'checkbox', 'page', 'router-view', 'slot', 'aside', 'col', 'container', 'foot', 'head', 'main', 'row']報(bào)錯(cuò)信息:'component "<%= name %>" doesn't have a defined property named "<%= prop %>"'
報(bào)錯(cuò)信息:'component "<%= name %>" doesn't have a defined event named "<%= prop %>"'
  • 自定義組件:模板校驗(yàn)時(shí)和根據(jù) usingComponents 配置解析對(duì)應(yīng)組件,使用組件時(shí)屬性名和事件名必須在組件內(nèi)有定義否則校驗(yàn)不通過(guò)。
報(bào)錯(cuò)信息: "The property "propName" is not a property of component "compName" which path is: path/to/component"
報(bào)錯(cuò)信息: "The event "eventName" is not defined in component "compName" which path is: path/to/component"
內(nèi)置組件嵌套規(guī)則校驗(yàn)

在使用 CML 內(nèi)置組件時(shí),內(nèi)置組件之間需要遵循一定的嵌套關(guān)系。

校驗(yàn)點(diǎn):

  • text 組件text 組件只能包含 text 組件作為子節(jié)點(diǎn)
  • scroller 組件scroller 組件不能包含 textarea 或者 video 組件
  • listlist 組件不能包含 textarea 或者 video 組件
  • videovideo 組件如果包含子組件,那么只能是 text 組件
報(bào)錯(cuò)信息 'tag "<%= parent %>" can not have any child elements, therefor tag "<%= forbiddenTag %>" is not allowed as it's children'
報(bào)錯(cuò)信息 'tag "<%= parent %>" can only have "<%= elements %>" as it's child elements, therefor tag "<%= forbiddenTag %>" is not allowed as it's children'
報(bào)錯(cuò)信息 'tag "<%= parent %>" can not have "<%= forbiddenTag %>" as it's child elements, and element in this list: "<%= elements %>" is forbidden附:模板格式規(guī)范

模板書寫規(guī)范

chameleon 模板書寫規(guī)范尊從 HTML5 基本規(guī)范。

模板目錄規(guī)范

CML 支持三端(三種 Native 環(huán)境),每個(gè)組件在每個(gè)環(huán)境對(duì)應(yīng)有一個(gè)模板。模板命名格式 組件名稱+端名稱.cml 比如:c-title 組件

├── components
│   ├── c-title
│   │   ├── c-title.web.cml
│   │   ├── c-title.weex.cml
│   │   └── c-title.wx.cml

其中: c-title.web.cml 為 Web 端模板,c-title.weex.cml 為 iOS、Android 端,c-title.wx.cml 為微信小程序端。

本節(jié)模板規(guī)范就是指對(duì)這三個(gè)模板文件的編寫規(guī)范。

模板語(yǔ)言指定

每個(gè)端的模板都可以并且必須選擇兩種語(yǔ)法規(guī)范中的一個(gè),cml 語(yǔ)法規(guī)范 或者 類 vue 語(yǔ)法規(guī)范。指定語(yǔ)法規(guī)范的方式為在根節(jié)點(diǎn) template 標(biāo)簽上給屬性 lang 指定 "cml" 或者 "vue"。

列如指定模板為 cml 語(yǔ)法規(guī)范

<template lang="cml"></template>
注意:每個(gè)模板只能夠有一個(gè)根節(jié)點(diǎn)并且必須為 template 標(biāo)簽,template 便簽每個(gè)模板只能有一個(gè)。

模板標(biāo)簽使用規(guī)范

每個(gè)模板內(nèi)可以使用的標(biāo)簽由三部分組成:

  1. CML 的內(nèi)置組件對(duì)應(yīng)的標(biāo)簽chamelon 支持的標(biāo)簽有: template、view、text、block、scroller、list、cell、image、switch、video、input、textarea、richtext、button、radio、checkbox、page、router-view、slot、aside、col、container、foot、head、main、row
  2. 多態(tài)組件中在平臺(tái)文件里, 以 'origin-' 為 前綴的平臺(tái)原生組件對(duì)應(yīng)的 標(biāo)簽。
  3. 模板文件中通過(guò) usingComponents 引入的組件對(duì)應(yīng)的 標(biāo)簽。

舉例

仍以 c-title 組件為例,假設(shè)各個(gè)模板都有自定義組件配置

<script cml-type="json">
{
  "base": {
    "usingComponents": {
      "tickets": "/components/ticket/index"
    }
  }
}
</script>
  • c-title.web.cml可以使用 CML 支持的 view、text、block 等基本標(biāo)簽,帶 'origin-' 前綴的 web 原生標(biāo)簽 origin-div、origin-p、origin-span 等,以及自定義組件 tickets。
  • c-title.weex.cml可以使用 CML 支持的 view、text、block 等基本標(biāo)簽,Weex 支持的標(biāo)簽,以及自定義組件 tickets。如果以 Vue 作為 Weex 使用的前端框架,那么 Weex 支持的標(biāo)簽基本和 vue 框架支持的標(biāo)簽基本一致,其中有部分不支持的標(biāo)簽比如:transition 標(biāo)簽,具體請(qǐng)參見weex 文檔
  • c-title.wx.cml可以使用 CML 支持的 view、text、block 等基本標(biāo)簽,帶 'origin-' 前綴的 wx 原生標(biāo)簽比如 origin-swiper、origin-movable-area、origin-cover-view、origin-web-view 等,以及自定義組件 tickets。

模板指令使用規(guī)范

除引用平臺(tái)原生組件 對(duì)應(yīng)的標(biāo)簽外,每個(gè)模板必須使用模板 語(yǔ)言(由 template 標(biāo)簽 的 lang 屬性指定) 所對(duì)應(yīng)的指令集。

  • 模板語(yǔ)言為 cml 時(shí)支持的指令有:c-if、c-else、c-else-if、c-for、c-for-index、c-for-item、c-model、c-text、c-show、c-bind、c-catch
  • 模板語(yǔ)言為類 vue 時(shí)支持的指令有:v-if、v-else、v-else-if、v-for、v-on、v-bind、v-html、v-show、v-model、v-pre、v-once、slot-scope、is、@、:
類 vue 語(yǔ)法支持上述列表中的指令,其他 vue.js 的指令如 v-cloak 是不支持的。

舉例

若模板語(yǔ)言為 "cml" 即 template 標(biāo)簽 lang 屬性為 "cml",native 環(huán)境為微信小程序。還是以 c-title 組件為例,那么此時(shí)對(duì)應(yīng)的是 c-title.wx.cml 模板。 c-title.wx.cml:

<template lang="cml">
    <view c-if="{{showMessage}}">{{messageText}}</view>
    <picker-view></picker-view>
</template>

那么模板里可以使用 CML 支持的指令:

c-if、c-else、c-else-if、c-for、c-for-index、c-for-item、c-model、c-text、c-show、c-bind、c-catch

引用平臺(tái)原生組件

Chameleon 提供兩種方式引入平臺(tái)原生 組件和平臺(tái)第三方原生組件:

  • 通過(guò)給平臺(tái) 原生內(nèi)置組件添加 'origin-' 前綴引用原生組件
  • 通過(guò) usingComponents 引入平臺(tái)原生第三方組件
引用的原生組件上只能夠使用平臺(tái)支持的原生指令,不能使用 CML 內(nèi)置指令。 改限制只限于組件本身,對(duì)其子組件沒有影響。

舉例

若模板語(yǔ)言為 "cml" 即 template 標(biāo)簽 lang 屬性為 "cml",native 環(huán)境為微信小程序。還是以 c-title 組件為例,那么此時(shí)對(duì)應(yīng)的是 c-title.wx.cml 模板。 c-title.wx.cml:

<template lang="cml">
    <view>{{messageText}}</view>
    <origin-picker-view></origin-picker-view>
</template>

那么模板里可以使用 CML 支持的指令:

c-if、c-else、c-else-if、c-for、c-for-index、c-for-item、c-model、c-text、c-show、c-bind、c-catch

origin-picker-view 組件可以使用微信小程序原生支持的指令:

wx:if、wx:elif、wx:else、wx:for、wx:for-item、wx:for-index、wx:key、bindtap、catchtap

腳本規(guī)范校驗(yàn)

  • 編寫一端代碼邏輯時(shí),如果使用其他端的全局變量,會(huì)校驗(yàn)失敗。
  • 使用未定義的 event 名稱時(shí),會(huì)報(bào)錯(cuò)
  • 使用未定義的 prop 名稱時(shí),會(huì)報(bào)錯(cuò)

組件邏輯層響應(yīng)頁(yè)面操作的代碼,需要導(dǎo)出以下規(guī)范的對(duì)象。

{
  // 數(shù)據(jù)
  data: {
    dataKey1: dataValue1,
    dataKey2: dataValue2
  },
  // 屬性
  props: {
    propKey1: propValue1,
    propKey2: propValue2
  },
  // 計(jì)算屬性
  computed: {
    computedKey1: () => {

    },
    computedKey2: () => {

    }
  },
  // 監(jiān)聽屬性
  watch: {
    watchKey1: () => {

    },
    watchKey2: () => {

    }
  },
  // 實(shí)例初始化之后,數(shù)據(jù)和方法掛在到實(shí)例之前
  beforeCreate: () => {

  },
  // 數(shù)據(jù)及方法掛載完成
  created: () => {

  },
  // 開始掛載已經(jīng)編譯完成的html,到對(duì)應(yīng)的dom節(jié)點(diǎn)時(shí)
  beforeMount: () => {

  },
  // 模板或者h(yuǎn)tml編譯完成,且渲染到dom中完成
  mounted: () => {

  },
  // 實(shí)例銷毀之前
  beforeDestroy: () => {

  },
  // 實(shí)例銷毀后
  destroyed: () => {

  }
}
生命周期
鉤子執(zhí)行時(shí)機(jī)詳細(xì)
beforeCreate實(shí)例初始化之后,數(shù)據(jù)和方法掛在到實(shí)例之前在該鉤子函數(shù)中會(huì)傳入當(dāng)前頁(yè)面 query 參數(shù)
created數(shù)據(jù)及方法掛載完成
beforeMount開始掛載已經(jīng)編譯完成的 html,到對(duì)應(yīng)的 dom 節(jié)點(diǎn)時(shí)
mounted模板或者 html 編譯完成,且渲染到 dom 中完成
beforeDestroy實(shí)例銷毀之前
destroyed實(shí)例銷毀后
全局變量校驗(yàn)

編寫一端代碼邏輯時(shí),如果使用其他端的全局變量,會(huì)校驗(yàn)失敗。

按照端類型區(qū)分可用的全局變量:

weex

weex , global

wx

wx, global

alipay

my, global

baidu

swan, global

web

postMessage, blur, focus, close, frames, self, window, parent, opener, top, length, closed, location, document, origin, name, history, locationbar, menubar, personalbar, scrollbars, statusbar, toolbar, status, frameElement, navigator, customElements, external, screen, innerWidth, innerHeight, scrollX, pageXOffset, scrollY, pageYOffset, screenX, screenY, outerWidth, outerHeight, devicePixelRatio, clientInformation, screenLeft, screenTop, defaultStatus, defaultstatus, styleMedia, onanimationend, onanimationiteration, onanimationstart, onsearch, ontransitionend, onwebkitanimationend, onwebkitanimationiteration, onwebkitanimationstart, onwebkittransitionend, isSecureContext, onabort, onblur, oncancel, oncanplay, oncanplaythrough, onchange, onclick, onclose, oncontextmenu, oncuechange, ondblclick, ondrag, ondragend, ondragenter, ondragleave, ondragover, ondragstart, ondrop, ondurationchange, onemptied, onended, onerror, onfocus, oninput, oninvalid, onkeydown, onkeypress, onkeyup, onload, onloadeddata, onloadedmetadata, onloadstart, onmousedown, onmouseenter, onmouseleave, onmousemove, onmouseout, onmouseover, onmouseup, onmousewheel, onpause, onplay, onplaying, onprogress, onratechange, onreset, onresize, onscroll, onseeked, onseeking, onselect, onstalled, onsubmit, onsuspend, ontimeupdate, ontoggle, onvolumechange, onwaiting, onwheel, onauxclick, ongotpointercapture, onlostpointercapture, onpointerdown, onpointermove, onpointerup, onpointercancel, onpointerover, onpointerout, onpointerenter, onpointerleave, onafterprint, onbeforeprint, onbeforeunload, onhashchange, onlanguagechange, onmessage, onmessageerror, onoffline, ononline, onpagehide, onpageshow, onpopstate, onrejectionhandled, onstorage, onunhandledrejection, onunload, performance, stop, open, alert, confirm, prompt, print, requestAnimationFrame, cancelAnimationFrame, requestIdleCallback, cancelIdleCallback, captureEvents, releaseEvents, getComputedStyle, matchMedia, moveTo, moveBy, resizeTo, resizeBy, getSelection, find, webkitRequestAnimationFrame, webkitCancelAnimationFrame, fetch, btoa, atob, createImageBitmap, scroll, scrollTo, scrollBy, onappinstalled, onbeforeinstallprompt, crypto, ondevicemotion, ondeviceorientation, ondeviceorientationabsolute, indexedDB, webkitStorageInfo, sessionStorage, localStorage, chrome, visualViewport, speechSynthesis, webkitRequestFileSystem, webkitResolveLocalFileSystemURL, openDatabase, applicationCache, caches, whichAnimationEvent, animationendEvent, infinity, SETTING, AppView, ExtensionOptions, ExtensionView, WebView, iconPath, _app, _ZOOM_, Feed, md5, $, jQuery, Search, windmill, Lethargy, alertTimeOut, supportApps, lethargyX, lethargyY, iView, onModuleResLoaded, iEditDelete, infinityDrag, i, array, TEMPORARY, PERSISTENT, addEventListener, removeEventListener, dispatchEvent

#樣式規(guī)范校驗(yàn)

  • 滿足 css 規(guī)則
  • 不支持級(jí)聯(lián)
#CMSS 規(guī)則

CMSS 規(guī)則由兩個(gè)主要的部分構(gòu)成:選擇器,以及一條或多條聲明。

selector { declaration1; declaration2; ... declarationN } 每條聲明由一個(gè)屬性和一個(gè)值組成。

#聲明

屬性(property)是你希望設(shè)置的樣式屬性(style attribute)。每個(gè)屬性有一個(gè)值。屬性和值被冒號(hào)分開,一條聲明以分號(hào)結(jié)尾。

selector {
  property1: value1;
  property2: value2;
}

selector 應(yīng)為一條獨(dú)立的 id 名稱或者 class 名稱,不支持級(jí)聯(lián)

// 正確
.selector {
  property1: value1;
  property2: value2;
}
#selector {
  property1: value1;
  property2: value2;
}

// 錯(cuò)誤
#selector1 .selector {
  property1: value1;
  property2: value2;
}
#多端之間的差異對(duì)比
CSS屬性H5小程序weex
布局allallflexbox
盒模型allall只支持display:border-box
float浮動(dòng)???
display:inline-block|none???
ID選擇器???
類選擇器???
屬性選擇器???
級(jí)聯(lián)選擇器、派生選擇器(后代、子元素、相鄰兄弟)???
選擇器分組???
偽類(:active|:focus)???
偽類(:hover|:link|:visited|:first-child|:lang)???
偽元素(:first-letter|:first-line|:before|:after)???
百分比定值???
line-height:1???
尺寸px|rem|em|vw|vhpx|rpxpx
!important???

**注意:**框架會(huì)根據(jù)上表所述的多端之間的差異做校驗(yàn)。

#配置規(guī)范校驗(yàn)

CML 文件配置規(guī)范的校驗(yàn),包括語(yǔ)法格式,組件的引用等。

  • json 格式是否合法
  • usingComponents 只放置在 base 字段下
#說(shuō)明

wx、alipay、baidu、web、weex 分別對(duì)應(yīng)各端構(gòu)建時(shí)所應(yīng)用的配置信息,base 的配置會(huì)應(yīng)用到每一端的配置,內(nèi)部做的 result = merge(base,target)。

wx 字段配置的內(nèi)容最終會(huì)生成到微信小程序所需要的 json 文件。

wx.component 字段,如果在微信中該 cml 文件是組件必須聲明該字段。 usingComponents 字段是目前最重要的配置,三端都會(huì)使用,微信小程序規(guī)定頁(yè)面的 json 文件中配置使用到的組件。web 和 Weex 端的構(gòu)建時(shí)也是根據(jù)該字段去找到相應(yīng)的組件進(jìn)行自動(dòng)化的注冊(cè)。所以用到組件必須進(jìn)行配置 usingComponents 中組件的引用地址支持引用 src 和 node_modules 下的組件,src 下的,可以寫相對(duì)路徑,也可以寫相對(duì)于 src 的絕對(duì)路徑,例如/components/**, node_modules 下的組件,不需要寫 node_modules,直接從 npm 的包名稱開始寫例如 cml-test-ui/navi/navi。 路徑寫到.cml 文件所在層級(jí),不寫后綴。 例子:

{
  "base": {
    "usingComponents": {
      "c-scroller": "chameleon-ui-builtin/components/scroller/scroller",
      "c-checkbox": "/components/c-checkbox/c-checkbox"
    }
  },
  "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
  },
}

##### CML JSON 規(guī)范

包括以下三條:
1. 滿足標(biāo)準(zhǔn)的 JSON 格式規(guī)范,滿足 JSON.parse 的解析
  - 數(shù)據(jù)在名稱/值對(duì)中
  - 數(shù)據(jù)由逗號(hào)分隔
  - 花括號(hào)保存對(duì)象
  - 方括號(hào)保存數(shù)組

2. CML json為對(duì)象,必須包括base字段,根據(jù)適配的平臺(tái)可配置wx|alipay|baidu|web|weex字段,作為單一端上的特殊配置

```javascript
// 適配微信小程序和weex
{
  "base": {

  },
  "wx": {

  },
  "alipay": {

  },
  "baidu": {

  },
  "weex": {

  }
}
  1. usingComponents 使用規(guī)范:

多端實(shí)現(xiàn)完全一致組件:

usingComponents 字段必須放置在 base 下,不應(yīng)出現(xiàn) wx|alipay|baidu|web|weex 字段中

多端實(shí)現(xiàn)不一致組件:

usingComponents 字段可以放置在 base 下,也可以在出現(xiàn)當(dāng)前端對(duì)應(yīng)的 wx|alipay|baidu|web|weex 字段中

注:多端實(shí)現(xiàn)完全一致組件 和 多端實(shí)現(xiàn)不一致組件 的說(shuō)明可參見這


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

掃描二維碼

下載編程獅App

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

編程獅公眾號(hào)