一文介紹amis百度低代碼框架(百度低代碼開(kāi)發(fā)平臺(tái))
最近公司的后臺(tái)管理系統(tǒng)接入了amis低代碼平臺(tái),就想寫(xiě)下對(duì)于這個(gè)低代碼框架的使用心得,以及如何通過(guò)這個(gè)框架完成日常需求中的功能。
背景是項(xiàng)目經(jīng)理希望后續(xù)在前端開(kāi)發(fā)pd數(shù)比較緊張的情況下,后端也能夠介入前端頁(yè)面配置,也就有了低代碼框架選型及后續(xù)的推進(jìn)事項(xiàng)。(后端同學(xué):好好好這么玩是吧)
低代碼LowCode
低代碼LowCode,wikipedia上是這么介紹它的,低代碼開(kāi)發(fā)平臺(tái)LCDP(Low-code development platform),它本身是一個(gè)軟件,為開(kāi)發(fā)者提供了創(chuàng)建應(yīng)用程序的開(kāi)發(fā)環(huán)境。和傳統(tǒng)代碼的IDE(集成開(kāi)發(fā)環(huán)境 integrated development environment)不同的是,低代碼開(kāi)發(fā)平臺(tái)提供了更強(qiáng)可維護(hù)性且易用的可視化IDE。
低代碼是一種開(kāi)發(fā)模式,開(kāi)發(fā)者通過(guò)可視化的圖形界面、預(yù)構(gòu)建模塊以及自動(dòng)化工具,來(lái)操作圖形拖拽、參數(shù)配置等就可以生成頁(yè)面完成開(kāi)發(fā)工作,而非傳統(tǒng)的手動(dòng)編寫(xiě)代碼的形式。
主要用途是希望能夠加快應(yīng)用程序的開(kāi)發(fā)速度,降低開(kāi)發(fā)的學(xué)習(xí)成本和難度、讓非技術(shù)人員在熟悉開(kāi)發(fā)方式后也能上手參與到應(yīng)用程序的開(kāi)發(fā)中。
amis介紹
amis 是一個(gè)低代碼前端框架,主要功能就是使用JSON配置來(lái)生成頁(yè)面。
在官網(wǎng)的介紹案例中,實(shí)現(xiàn)一個(gè)具備以下功能的頁(yè)面:
- 可以對(duì)數(shù)據(jù)做篩選
- 有按鈕可以刷新數(shù)據(jù)
- 編輯單行數(shù)據(jù)
- 批量修改和刪除
- 按某列排序
- 可以隱藏某些列
- 可以調(diào)整列順序
- 自動(dòng)生成頂部查詢區(qū)域
- 可調(diào)整列寬度
- 開(kāi)啟整頁(yè)內(nèi)容拖拽排序
- 表格有分頁(yè)(頁(yè)數(shù)還能同步到地址欄,不過(guò)這個(gè)例子中關(guān)了)
- 有數(shù)據(jù)匯總
- 支持導(dǎo)出 Excel
- 等其他功能(其他可以查看文檔)
這類(lèi)我們?cè)谌粘i_(kāi)發(fā)需求時(shí)經(jīng)常寫(xiě)的CURD表格,在amis上只用配置157行JSON配置就能生成,代碼量上比直接編寫(xiě)代碼的模式下少了非常多。
平常我們很多基于業(yè)務(wù)功能封裝的表格,亦或是基于hooks的寫(xiě)法編寫(xiě)類(lèi)似功能表格,157行應(yīng)該是沒(méi)法完成的(狗頭.jpg)。
為什么選擇amis?
TL在低代碼框架選型宣講會(huì)上提到了選擇amis最重要的原因,框架穩(wěn)定且經(jīng)歷了很長(zhǎng)時(shí)間的實(shí)戰(zhàn)考驗(yàn)(百度內(nèi)部最老的amis頁(yè)面是6年多以前創(chuàng)建的)。
優(yōu)點(diǎn)
- 框架穩(wěn)定經(jīng)歷過(guò)長(zhǎng)時(shí)間的實(shí)戰(zhàn)考驗(yàn)(百度6年多的時(shí)間了創(chuàng)建了5萬(wàn)多頁(yè)面,最復(fù)雜的頁(yè)面超過(guò)了1萬(wàn)行的JSON配置);
- 提供豐富的組件,且目前還在持續(xù)更新;
- 支持低代碼模式 自定義組件的混合模式;
- 不受前端技術(shù)更新的影響(Angular/Vue/React等);
- 不需要會(huì)前端技術(shù)棧,掌握文檔中JSON生成頁(yè)面的規(guī)則即可;
在實(shí)際開(kāi)發(fā)過(guò)程中,也能比較直觀的感受到低代碼對(duì)于開(kāi)發(fā)效率的提升,尤其是在搭建頁(yè)面的效率上,配合上amis的可視化編輯器,一些CURD功能的表格及表單彈窗之類(lèi)的頁(yè)面,基本能夠很快的搭建完成。
缺點(diǎn)
- 大量定制化UI的情況不適宜使用,也是目前低代碼存在的通病,尤其是toC的需求。
- 復(fù)雜/特殊的交互場(chǎng)景不適合使用。
之前介入的一個(gè)公司核心后臺(tái)系統(tǒng)項(xiàng)目,涉及訂單模塊,頁(yè)面交互復(fù)雜不說(shuō),UI組件上還需要加比較多的業(yè)務(wù)邏輯限制,以及組件間的聯(lián)動(dòng),如果是那個(gè)項(xiàng)目接入低代碼,后續(xù)的開(kāi)發(fā)可能也會(huì)比較麻煩。
重要概念
官網(wǎng)文檔里的概念、類(lèi)型、高級(jí)內(nèi)容有時(shí)間的話一定要全部看完,具體內(nèi)容大家可以仔細(xì)查閱文檔,下面針對(duì)通過(guò)amis開(kāi)發(fā)必須具備的知識(shí)點(diǎn)做一個(gè)簡(jiǎn)介。
JSON渲染邏輯
amis 的渲染過(guò)程是將 JSON 轉(zhuǎn)成對(duì)應(yīng)的 React 組件。先通過(guò) JSON 的 type 找到對(duì)應(yīng)的 Component,然后把其他屬性作為 props 傳遞過(guò)去完成渲染。
拿一個(gè)表單頁(yè)面來(lái)說(shuō),如果用 React 組件開(kāi)發(fā)一般長(zhǎng)這樣。
jsx復(fù)制代碼<Page title="頁(yè)面標(biāo)題" subTitle="副標(biāo)題"> <Form title="用戶登錄"> <InputText name="username" label="用戶名" /> </Form></Page>
把以上配置方式換成amis JSON,則是:
json復(fù)制代碼{ "type": "page", "title": "頁(yè)面標(biāo)題", "subTitle": "副標(biāo)題", "body": { "type": "form", "title": "用戶登錄", "body": [ { "type": "Input-text", "name": "username", "label": "用戶名" } ] }
amis中兩種使用方法,JS SDK(可以使用在任何頁(yè)面中,適合對(duì)前端或React不了解的開(kāi)發(fā)者,它不依賴npm及webpack,可以像Vue/jQuery那樣外鏈代碼就能使用)和React。
數(shù)據(jù)域與數(shù)據(jù)鏈
json復(fù)制代碼// amis配置內(nèi)容{ "type": "page", "initApi": "/amis/api/mock2/page/initData", "body": "date is ${date}"}// amis/api/mock2/page/initData接口響應(yīng)數(shù)據(jù){ "status": 0, "msg": "", "data": { "title": "Test Page Component", "date": "2017-10-13" }}
渲染后頁(yè)面的展示結(jié)果如下:
上面的json配置主要做了這些事:
- 首先給 Page 組件配置initApi屬性,該屬性會(huì)在組件初始化時(shí),請(qǐng)求所該屬性所配置的接口。
- 接口請(qǐng)求成功后,Page 會(huì)把接口返回響應(yīng)數(shù)據(jù)data存到當(dāng)前的數(shù)據(jù)域中。
- Page 在渲染 body 所配置的文本時(shí),會(huì)解析文本內(nèi)容,當(dāng)解析到模板變量${text}時(shí),amis 會(huì)把嘗試在當(dāng)前組件的數(shù)據(jù)域中獲取text變量值,并替換掉${text},最后渲染解析后的文本。
數(shù)據(jù)域
上面在說(shuō)到初始化接口渲染動(dòng)態(tài)文本時(shí),提到了作用域,這也是amis中最重要的概念之一。
json復(fù)制代碼{ "type": "page", "body": "hello ${text}"}
在沒(méi)有配置數(shù)據(jù)域之前,這里肯定是會(huì)只展示hello。
配置上作用域后就會(huì)展示為hello world。
json復(fù)制代碼{ "data": { "text": "World!" }, "type": "page", "body": "Hello ${text}"}
data屬性是數(shù)據(jù)域的一種形式,當(dāng)我們沒(méi)有顯示配置數(shù)據(jù)域時(shí),就等同于:
json復(fù)制代碼{ "data": {}, "type": "page", "body": "Hello ${text}"}
數(shù)據(jù)鏈
數(shù)據(jù)鏈也是amis中的重要概念。它的特性是當(dāng)前組件在遇到獲取變量的場(chǎng)景時(shí)(例如模板渲染、展示表單數(shù)據(jù)、渲染列表等等)。
- 首先會(huì)先嘗試在當(dāng)前組件的數(shù)據(jù)域中尋找變量,當(dāng)成功找到變量時(shí),通過(guò)數(shù)據(jù)映射完成渲染,停止尋找過(guò)程;
- 當(dāng)在當(dāng)前數(shù)據(jù)域中沒(méi)有找到變量時(shí),則向上尋找,在父組件的數(shù)據(jù)域中,重復(fù)步驟1和2;
- 一直尋找,直到頂級(jí)節(jié)點(diǎn),也就是page節(jié)點(diǎn),尋找過(guò)程結(jié)束。
- 但是如果 url 中有參數(shù),還會(huì)繼續(xù)向上查找這層,所以很多時(shí)候配置中可以直接 ${id} 取地址欄參數(shù)。
和JS中的作用域鏈類(lèi)似
接下來(lái)可以看下數(shù)據(jù)鏈的使用案例:
json復(fù)制代碼{ "type": "page", "data": { "name": "zhangsan", "age": 20 }, "body": [ { "type": "tpl", "tpl": "my name is ${name}" }, { "type": "service", "data": { "name": "lisi" }, "body": { "type": "tpl", "tpl": "my name is ${name}, I'm ${age} years old" } } ]}
markdown復(fù)制代碼- page - tpl - service - tpl
數(shù)據(jù)鏈工作過(guò)程:
- page組件下的tpl組件,在渲染my name is ${name}時(shí),首先會(huì)在page的數(shù)據(jù)域中,嘗試尋找name變量,在當(dāng)前數(shù)據(jù)域中,name變量為zhangsan,因此尋找變量結(jié)束,通過(guò)數(shù)據(jù)映射渲染,輸出:my name is zhangsan,渲染結(jié)束。
- service組件開(kāi)始渲染,service組件內(nèi)子組件tpl,它配置的模板字符串是:my name is ${name}, I'm ${age} years old,它會(huì)在service的數(shù)據(jù)域中,嘗試尋找name和age變量。
- 由代碼可以看出,service數(shù)據(jù)域中name變量為lisi,因此停止該變量的尋找,接下來(lái)尋找age變量。
- 很明顯在service數(shù)據(jù)域中尋找age變量會(huì)失敗,因此向上查找,嘗試在page數(shù)據(jù)域中尋找age變量,找到為20,尋找變量結(jié)束,通過(guò)數(shù)據(jù)映射渲染,輸出:my name is lisi, I'm 20 years old,渲染結(jié)束。
使用數(shù)據(jù)域的注意點(diǎn)
官方文檔中特地提到,具備作用域的組件只有以下幾種:
markdown復(fù)制代碼- App- Page- Cards- Chart- CRUD- CRUD2- Dialog- Drawer- List- Page- PaginationWrapper- Service- Wizard- Combo- InputArray- Table- Table2
有個(gè)特殊情況是 CRUD 中 filter,實(shí)際上是個(gè) form,所以 CRUD 中有兩層數(shù)據(jù)域,第一層是 CRUD 本身,同時(shí)查詢條件表單中也有一層數(shù)據(jù)域。 比如以下案例中,獲取數(shù)據(jù)域里的數(shù)據(jù)是無(wú)效的:
json復(fù)制代碼{ "type": "page", "data": { "name": "zhangsan" }, "body": [ { "type": "tpl", "tpl": "my name is ${name}" }, { "type": "container", "data": { "name": "lisi" }, "body": { "type": "tpl", "tpl": "my name is ${name}" } } ]}
正確做法是需要通過(guò)service包裹一層:
json復(fù)制代碼{ "type": "page", "data": { "name": "zhangsan" }, "body": [ { "type": "tpl", "tpl": "my name is ${name}" }, { "type": "service", "data": { "name": "lisi" }, "body": { "type": "container", "body": { "type": "tpl", "tpl": "my name is ${name}" } } } ]}
這里個(gè)人也存在一個(gè)小疑問(wèn),官方文檔上提到page是具備數(shù)據(jù)鏈,但是案例上在page上設(shè)置了數(shù)據(jù)域,按官方的說(shuō)法應(yīng)該是生效,但實(shí)際上需要通過(guò)service組件包裹一層才能夠生效,有了解的朋友可以評(píng)論區(qū)里聊一下哦
數(shù)據(jù)映射
數(shù)據(jù)映射支持用戶通過(guò)${xxx}或$xxx獲取當(dāng)前數(shù)據(jù)鏈中某個(gè)變量的值,實(shí)現(xiàn)靈活的數(shù)據(jù)配置功能,主要用于模板字符串、 自定義 api 請(qǐng)求數(shù)據(jù)體格式等場(chǎng)景。
模板字符串的使用
json復(fù)制代碼{ "type": "page", "data": { "name": "rick", "friend": { "name": "xiaoming", "age": "18" } }, "body": [ { "type": "tpl", "tpl": "my name is $name / ${name}, my friend ${friend.name}" } ]}
默認(rèn) amis 在解析模板字符串時(shí),遇到$字符會(huì)嘗試去解析該變量并替換該模板變量,如果你想輸出純文本"${xxx}"或"$xxx",那么需要在$前加轉(zhuǎn)義字符"",即"${xxx}"
自定義api請(qǐng)求的數(shù)據(jù)格式
在日常開(kāi)發(fā)中表單提交是很常見(jiàn)的開(kāi)發(fā)需求,通過(guò)以下簡(jiǎn)單的JSON配置就能生成頁(yè)面。
json復(fù)制代碼{ "type": "page", "body": { "type": "form", "api": { "method": "post", "url": "/amis/api/mock2/form/saveForm", // 配置`api`的`data`屬性,使用數(shù)據(jù)映射實(shí)現(xiàn)對(duì)數(shù)據(jù)格式的自定義 "data": { "userName": "${name}", "userEmail": "${email}" } }, "body": [ { "type": "input-text", "name": "name", "label": "姓名:" }, { "name": "email", "type": "input-text", "label": "郵箱:" } ] }}
Q:為什么這里不需要顯示定義數(shù)據(jù)域,也能夠回顯數(shù)據(jù)? A: 上文提到了如果不顯示定義數(shù)據(jù)域,其實(shí)data屬性還是會(huì)隱式存在的(忘記的同學(xué)可以查看上面數(shù)據(jù)域那一小節(jié)),amis中有個(gè)小細(xì)節(jié),當(dāng)form組件存在時(shí),會(huì)將data數(shù)據(jù)和當(dāng)前form組件的數(shù)據(jù)域進(jìn)行合并。
此時(shí)可以理解數(shù)據(jù)域里有兩個(gè)字段
json復(fù)制代碼{ "data": { "name": "", "email": "" }}
過(guò)濾器
過(guò)濾器這個(gè)功能非常實(shí)用,相當(dāng)于是將JS內(nèi)置的一些方法通過(guò)過(guò)濾器的模式進(jìn)行使用。舉個(gè)我在項(xiàng)目使用的一個(gè)例子。
json復(fù)制代碼{ "type": "page", "data": { "info": { "name": "rick", "company": "baidu" } }, "body": { "type": "tpl", "tpl": "my info is ${info|json}" }}// |json 過(guò)濾器的作用于等同于 JSON.stringify 方法js復(fù)制代碼// 展示結(jié)果my info is { "name": "rick", "company": "baidu" }
這里在實(shí)際應(yīng)用過(guò)程發(fā)現(xiàn),可以直接新建對(duì)象使用過(guò)濾器。
json復(fù)制代碼{ "type": "page", "data": { "a": "1" }, "body": { "type": "tpl", "tpl": "my info is ${{a: a, b: 2}|json}" }}js復(fù)制代碼// 結(jié)果my info is { "a": "1", "b": 2 }
amis中提供的過(guò)濾器還是非常豐富的,過(guò)濾器是對(duì)數(shù)據(jù)映射的一種增強(qiáng),它的作用是對(duì)獲取數(shù)據(jù)做一些處理,基本用法都大體相同:
json復(fù)制代碼${xxx [ |filter1 |filter2...] }
這里截圖了官網(wǎng)提供的部分過(guò)濾器,更多過(guò)濾器可查看文檔。
表達(dá)式
amis中的表達(dá)式有兩種語(yǔ)法:
markdown復(fù)制代碼1. 純js表達(dá)式的寫(xiě)法:data.xxx === 12. 通過(guò)${}包裹的表達(dá)式:${xxx === 1}
在通過(guò)amis構(gòu)建頁(yè)面的過(guò)程,也很多場(chǎng)景會(huì)需要我們用到表達(dá)式:
- 變量取值my name is ${xxx}
- api地址的參數(shù)取值http://mydomain.com/api/xxx?id=${id}
- api發(fā)送及數(shù)據(jù)映射
json復(fù)制代碼{ "type": "crud", "api": { method: "post" url: "http://mydomain.com/api/xxx", data: { skip: "${(page - 1) * perPage}", take: "${perPage}" } }, ...}
- 組件的顯隱控制
json復(fù)制代碼{ "name": "xxxText", "type": "input-text", "visibleOn": "${ xxxFeature.on }"}
- 表單默認(rèn)值
json復(fù)制代碼{ "name": "xxxText", "type": "input-text", "value": "${ TODAY() }"}
- 等等…
語(yǔ)法
JS的大多語(yǔ)法在amis中基本都可以正常書(shū)寫(xiě),基礎(chǔ)數(shù)據(jù)類(lèi)型、數(shù)組、對(duì)象、三元/二元表達(dá)式等。
json復(fù)制代碼{ "type": "page", "data": { "a": 1, "key": "y", "obj": { "x": 2, "y": 3 }, "arr": [ 1, 2, 3 ] }, "body": [ "a is ${a} <br />", "a 1 is ${a 1} <br />", "obj.x is ${obj.x} <br />", "obj['x'] is ${obj['x']} <br />", "obj[key] is ${obj[key]} <br />", "arr[0] is ${arr[0]} <br />", "arr[a] is ${arr[a]} <br />", "arr[a 1] is ${arr[a 1]} <br />" ]}js復(fù)制代碼// 結(jié)果a is 1 a 1 is 2 obj.x is 2 obj['x'] is 2 obj[key] is 3 arr[0] is 1 arr[a] is 2 arr[a 1] is 3
需要注意的是箭頭函數(shù)只支持單表達(dá)式,不支持多條語(yǔ)句,舉個(gè)例子現(xiàn)在有個(gè)() => abc的箭頭函數(shù),ARRAYMAP(arr, () => abc),這里的ARRAYMAP等同于Array.prototype.map,以及類(lèi)似的想法會(huì)在下文繼續(xù)介紹,這里強(qiáng)調(diào)的是箭頭函數(shù)的應(yīng)用。
特殊字符變量名的獲取比較特別,需要轉(zhuǎn)義,正常情況下${xxx.yyy}指的是需要取數(shù)據(jù)域中xxx變量的yyy屬性,如果有個(gè)變量名就叫xxx.yyy的話,就需要通過(guò)${xxx.yyy}來(lái)獲取。(amis版本需要>=1.6.1)
公式/函數(shù)
除了簡(jiǎn)單的表達(dá)式,還集成了如上文提到的ARRAYMAP等公式/函數(shù)。下面舉一些示例:
json復(fù)制代碼{ "type": "page", "body": [ { "type": "form", "wrapWithPanel": false, "data": { "val": 3.5 }, "body": [ { "type": "static", "label": "IF(true, 2, 3)", "tpl": "${IF(true, 2, 3)}" }, { "type": "static", "label": "MAX(1, -1, 2, 3, 5, -9)", "tpl": "${MAX(1, -1, 2, 3, 5, -9)}" }, { "type": "static", "label": "ROUND(3.5)", "tpl": "${ROUND(3.5)}" }, { "type": "static", "label": "ROUND(val)", "tpl": "${ROUND(val)}" }, { "type": "static", "label": "AVG(4, 6, 10, 10, 10)", "tpl": "${AVG(4, 6, 10, 10, 10)}" }, { "type": "static", "label": "UPPERMONEY(7682.01)", "tpl": "${UPPERMONEY(7682.01)}" }, { "type": "static", "label": "TIMESTAMP(DATE(2021, 11, 21, 0, 0, 0), 'x')", "tpl": "${TIMESTAMP(DATE(2021, 11, 21, 0, 0, 0), 'x')}" }, { "type": "static", "label": "DATETOSTR(NOW(), 'YYYY-MM-DD')", "tpl": "${DATETOSTR(NOW(), 'YYYY-MM-DD')}" } ] } ]}js復(fù)制代碼IF(true, 2, 3) // 2MAX(1, -1, 2, 3, 5, -9) // 5ROUND(3.5) // 3.5ROUND(val) // 3.5AVG(4, 6, 10, 10, 10) // 8UPPERMONEY(7682.01) // 柒仟陸佰捌拾貳元壹分TIMESTAMP(DATE(2021, 11, 21, 0, 0, 0), 'x') // 1640016000000DATETOSTR(NOW(), 'YYYY-MM-DD') // 2024-01-22
amis提供了很多常用公式組合,有邏輯函數(shù)、數(shù)學(xué)函數(shù)、文本函數(shù)、日期函數(shù)、數(shù)組函數(shù)等,詳情可移步文檔。
事件動(dòng)作
事件動(dòng)作用于解決復(fù)雜的 UI 交互場(chǎng)景,支持渲染器事件監(jiān)聽(tīng)和響應(yīng)設(shè)計(jì),無(wú)需關(guān)心組件層級(jí)關(guān)系。例如:
- http 請(qǐng)求:發(fā)送 http 請(qǐng)求
- 彈窗提示:執(zhí)行彈窗、抽屜打開(kāi)和 toast 提示
- 頁(yè)面跳轉(zhuǎn):頁(yè)面鏈接跳轉(zhuǎn)
- 瀏覽器相關(guān):回退、前進(jìn)、后退、刷新
- 刷新組件:聯(lián)動(dòng)刷新表單數(shù)據(jù),即數(shù)據(jù)重新加載
- 組件狀態(tài):控制指定組件的顯示/隱藏、啟用/禁用、展示態(tài)/編輯態(tài)
- 組件特性動(dòng)作:執(zhí)行指定組件的專有動(dòng)作,例如執(zhí)行表單的提交動(dòng)作
- 組件數(shù)據(jù):更新指定組件的數(shù)據(jù)域
- 廣播:多個(gè)組件監(jiān)聽(tīng)同一個(gè)事件做出不同響應(yīng)
- JS 腳本:通過(guò)編寫(xiě) JS 代碼片段實(shí)現(xiàn)所需邏輯,同時(shí)支持 JS 代碼內(nèi)執(zhí)行動(dòng)作
- 邏輯編排:條件、循環(huán)、排他、并行
onEvent
通過(guò)onEvent屬性實(shí)現(xiàn)渲染器事件與響應(yīng)動(dòng)作的綁定。onEvent內(nèi)配置事件和動(dòng)作的映射關(guān)系,actions是事件對(duì)應(yīng)的響應(yīng)動(dòng)作的集合。
json復(fù)制代碼{ "type": "button", "label": "嘗試點(diǎn)擊、鼠標(biāo)移入/移出", "level": "primary", "onEvent": { "click": { // 監(jiān)聽(tīng)點(diǎn)擊事件 "actions": [ // 執(zhí)行的動(dòng)作列表 { "actionType": "toast", // 執(zhí)行toast提示動(dòng)作 "args": { // 動(dòng)作參數(shù) "msgType": "info", "msg": "派發(fā)點(diǎn)擊事件" } } ] }, "mouseenter": {{ // 監(jiān)聽(tīng)鼠標(biāo)移入事件 "actions": [ { "actionType": "toast", "args": { "msgType": "info", "msg": "派發(fā)鼠標(biāo)移入事件" } } ] }, "mouseleave": {{ // 監(jiān)聽(tīng)鼠標(biāo)移出事件 "actions": [ { "actionType": "toast", "args": { "msgType": "info", "msg": "派發(fā)鼠標(biāo)移出事件" } } ] } }}
上下文
執(zhí)行動(dòng)作的時(shí)候,可以通過(guò)${event.data}獲取事件對(duì)象的數(shù)據(jù)、通過(guò)${__rendererData}獲取組件當(dāng)前的數(shù)據(jù)域。 下面來(lái)根據(jù)示例了解它的使用吧:
json復(fù)制代碼{ "type": "page", "data": { "p1": "p1" }, "body": { "type": "form", "debug": true, "api": { "url": "/amis/api/mock2/form/saveForm", "method": "post", "data": { "&": "$$", "job": "coder" } }, "data": { "job": "hr" }, "body": [ { "type": "alert", "body": "監(jiān)聽(tīng)姓名值變化,執(zhí)行動(dòng)作時(shí)讀取輸入的內(nèi)容;監(jiān)聽(tīng)年齡值變化,執(zhí)行動(dòng)作時(shí)讀取input-text組件當(dāng)前數(shù)據(jù)域(表單數(shù)據(jù))", "level": "info", "className": "mb-1" }, { "type": "input-text", "name": "name", "label": "姓名:", "onEvent": { "change": { "actions": [ { "actionType": "toast", "args": { "msg": "${name}" } } ] } } }, { "type": "input-text", "name": "age", "label": "年齡:", "onEvent": { "change": { "actions": [ { "actionType": "toast", "args": { "msg": "${__rendererData|json}" } } ] } } } ], "onEvent": { "submitSucc": { "actions": [ { "actionType": "toast", "args": { "msg": "${event.data|json}" } }, { "actionType": "toast", "args": { "msg": "${__rendererData|json}" } } ] } } }}
當(dāng)我們修改年齡內(nèi)容時(shí),會(huì)打開(kāi)彈窗。
這里的交互邏輯主要是根據(jù)以下配置出現(xiàn)的
json復(fù)制代碼 { "type": "input-text", "name": "age", "label": "年齡:", "onEvent": { // 綁定change事件 "change": { "actions": [ // 設(shè)置響應(yīng)動(dòng)作 { "actionType": "toast", "args": { // 這個(gè)操作其實(shí)還是挺常用的,相當(dāng)于是直接格式化輸入的內(nèi)容 "msg": "${__rendererData|json}" } } ] } } }
然后我們輸入姓名后提交表單,可以看到提示了三個(gè)彈窗信息:
json復(fù)制代碼"onEvent": { // 綁定提交成功的事件 "submitSucc": { "actions": [ // 設(shè)置彈窗toast的響應(yīng)動(dòng)作 { "actionType": "toast", "args": { // 彈窗里的內(nèi)容是接口響應(yīng)的結(jié)果,并通過(guò)過(guò)濾器進(jìn)行格式化處理 "msg": "${event.data|json}" } }, { "actionType": "toast", "args": { // 將當(dāng)前數(shù)據(jù)域中的內(nèi)容JSON化后,放置在toast中彈出 "msg": "${__rendererData|json}" } } ] }}
運(yùn)行日志
這個(gè)功能點(diǎn)很實(shí)用,能夠在瀏覽器控制臺(tái)非常清晰看到事件執(zhí)行順序及相關(guān)日志內(nèi)容,在我們?nèi)粘U{(diào)試需要自定義設(shè)置交互邏輯的時(shí)候,這個(gè)功能發(fā)揮作用就非常關(guān)鍵。
>>事件動(dòng)作是amis開(kāi)發(fā)過(guò)程中非常重要的知識(shí)點(diǎn),這里我介紹了事件動(dòng)作的基礎(chǔ)使用,在開(kāi)發(fā)過(guò)程中事件動(dòng)作的使用十分頻繁,一定要仔細(xì)閱讀這塊內(nèi)容!
API
日常開(kāi)發(fā)過(guò)程中,和后端同學(xué)聯(lián)調(diào)一定是我們經(jīng)常做的開(kāi)發(fā)任務(wù)之一,大家通常通過(guò)代碼開(kāi)發(fā)時(shí),大多情況下是使用ajax、axios、fetch等方式發(fā)起網(wǎng)絡(luò)請(qǐng)求,在amis里,我們不需要使用這些方式,同樣也是使用配置項(xiàng)完成請(qǐng)求調(diào)用的任務(wù)。
格式:[<method>:]<url>
- method:支持get、post、put、delete(默認(rèn)為get)
- url:接口地址
json復(fù)制代碼{ "api": "get:/amis/api/initData", // get 請(qǐng)求 "api": "post:/amis/api/initData", // post 請(qǐng)求 "api": "put:/amis/api/initData", // put 請(qǐng)求 "api": "delete:/amis/api/initData" // delete 請(qǐng)求}
接口返回格式(重要)
所有配置在 amis 組件中的接口,都要符合下面的返回格式。
json復(fù)制代碼{ "status": 0, "msg": "", "data": { ...其他字段 }}
- status: 返回 0,表示當(dāng)前接口正確返回,否則按錯(cuò)誤請(qǐng)求處理;
- msg: 返回接口處理信息,主要用于表單提交或請(qǐng)求失敗時(shí)的 toast 顯示;
- data: 必須返回一個(gè)具有 key-value 結(jié)構(gòu)的對(duì)象。 status *、* msg 和 data 字段為接口返回的必要字段。
amis為了方便更多場(chǎng)景使用,還兼容了以下這些錯(cuò)誤返回格式:
- errorCode 作為 status、errorMessage 作為 msg
- errno 作為 status、errmsg/errstr 作為 msg
- error 作為 status、errmsg 作為 msg
- error.code 作為 status、error.message 作為 msg
- message 作為 msg
前端可以通過(guò)響應(yīng)攔截調(diào)整響應(yīng)數(shù)據(jù)結(jié)構(gòu),比如axios中的AxiosInstance.interceptors.response.use
所以正確的格式需要是以下結(jié)構(gòu):
json復(fù)制代碼{ "status": 0, "msg": "", "data": { // 正確 "text": "World!" } // "data": "some string" 是錯(cuò)誤格式,需要使用包裝}
配置請(qǐng)求數(shù)據(jù)
上文也有提到,當(dāng)我們數(shù)據(jù)綁定的字段值和發(fā)起請(qǐng)求時(shí)需要傳給后端的字段值不同或者需要特殊處理時(shí),我們可以通過(guò)data屬性就行處理。
json復(fù)制代碼{ "type": "page", "body": { "type": "form", "api": { "method": "post", "url": "/amis/api/mock2/form/saveForm", // 設(shè)置data屬性,配置自定義接口請(qǐng)求數(shù)據(jù)體 "data": { "myName": "${name}", "myEmail": "${email}" } }, "body": [ { "type": "input-text", "name": "name", "label": "姓名:" }, { "name": "email", "type": "input-email", "label": "郵箱:" } ] }}
注意:當(dāng)method是get時(shí),data中的值會(huì)默認(rèn)添加到請(qǐng)求路徑中,默認(rèn)如果值是undefined時(shí)也會(huì)作為空字符串發(fā)送。這個(gè)由于歷史原因無(wú)法修改了,如果希望滿足undefined的效果,需要進(jìn)行一下的配置。
json復(fù)制代碼"data": { "myName": "${name|default:undefined}", "myEmail": "${email|default:undefined}"}
配置返回?cái)?shù)據(jù)
如果接口返回的數(shù)據(jù)結(jié)構(gòu)不符合預(yù)期,可以通過(guò)配置 responseData來(lái)修改.
同樣支持?jǐn)?shù)據(jù)映射,可用來(lái)映射的數(shù)據(jù)為接口的實(shí)際數(shù)據(jù)(接口返回的 data 部分),額外加 api 變量。其中 api.query 為接口發(fā)送的 query 參數(shù),api.body 為接口發(fā)送的內(nèi)容體原始數(shù)據(jù)。
注意:當(dāng)數(shù)據(jù)域里的 key 為 & 且值為 $$ 時(shí), 表示將所有原始數(shù)據(jù)打平設(shè)置到 data 中.
json復(fù)制代碼{ "type": "page", "initApi": { "method": "get", "url": "/amis/api/xxx", "responseData": { "&": "$$", "first": "${items|first}" } }}
假如接口實(shí)際返回為:
json復(fù)制代碼{ "status": 0, "msg": "", "data": { "items": [{"a": 1}, {"a": 2}] }}
經(jīng)過(guò)映射,給組件的數(shù)據(jù)為:
json復(fù)制代碼{ "items": [{"a": 1}, {"a": 2}], // 打平到了data中 "first": {"a": 1} // 獲取items中的第一項(xiàng)}
還有一種常見(jiàn)場(chǎng)景是,通過(guò)接口返回?cái)?shù)據(jù)去組裝下拉框select的數(shù)據(jù)源,這個(gè)情況相信大家開(kāi)發(fā)過(guò)程中也經(jīng)常遇到:
json復(fù)制代碼// 接口響應(yīng)結(jié)果{ "data": [ { "myLabel": "lab", "myValue": 1 } ]}
select需要的數(shù)據(jù)格式是[{"label": "lab", "value": 1}],那么應(yīng)該如何進(jìn)行映射呢?
json復(fù)制代碼{ "type": "select", "source": { "method": "get", "url": "http://xxx", "responseData": { "options": "${items|pick:label~myLabel,value~myValue}" } }}
API中還能進(jìn)行請(qǐng)求數(shù)據(jù)格式的調(diào)整、接口緩存、攔截請(qǐng)求、配置請(qǐng)求條件等等,所以>>>API這塊內(nèi)容請(qǐng)務(wù)必仔細(xì)閱讀,上述大致介紹了API的常用知識(shí)點(diǎn)能夠完成普遍的交互場(chǎng)景,當(dāng)涉及特殊交互邏輯時(shí),相信大家會(huì)需要這塊知識(shí)點(diǎn)。
踩坑記錄及一些使用心得
簡(jiǎn)單介紹下筆者開(kāi)發(fā)項(xiàng)目的技術(shù)棧vue3 ts,此次項(xiàng)目中接入amis框架作為編寫(xiě)代碼 低代碼的混合開(kāi)發(fā)模式。
目前amis也是將可視化編輯器作為獨(dú)立的npm發(fā)布出來(lái),所以基于此開(kāi)源包做二次開(kāi)發(fā),發(fā)布到公司內(nèi)部統(tǒng)一使用。
可視化編輯器npm包二次開(kāi)發(fā)作為內(nèi)部amis編輯器后臺(tái)使用記錄新增組件的id低代碼組件id匯總頁(yè)面
筆者這次開(kāi)發(fā)的功能,就是點(diǎn)擊按鈕打開(kāi)彈窗進(jìn)行數(shù)據(jù)的新增的需求,以及還有些簡(jiǎn)單的交互(就是這個(gè)預(yù)期是想簡(jiǎn)單了,沒(méi)想到低代碼配置起來(lái)還是有點(diǎn)麻煩的。。)
原本拿代碼開(kāi)發(fā)小半天就完事了,拿低代碼開(kāi)發(fā)從開(kāi)始到完成功能愣是搞了兩三天,中間也是碰到了各種奇奇怪怪的問(wèn)題,大家開(kāi)發(fā)前一定要給自己留出閱讀文檔的時(shí)間,雖然在這之中碰到了一些目前無(wú)法解決或者框架自身的bug等問(wèn)題,但是絕大多數(shù)還是自己對(duì)文檔的不熟悉導(dǎo)致的。
一開(kāi)始心里或多或少存在抵觸心理,代碼寫(xiě)的好好的,拿低代碼開(kāi)發(fā)多麻煩還要學(xué)習(xí)新內(nèi)容,未來(lái)?yè)Q了家公司又換了個(gè)框架有什么意義之類(lèi)的洗腦之類(lèi)的想法。但是仔細(xì)想想,低代碼能夠在過(guò)去幾年成為一個(gè)熱點(diǎn)以及成為商用產(chǎn)品一定有它的理由,無(wú)論出于什么目的,大家能夠在工作中實(shí)戰(zhàn)應(yīng)用新技術(shù)的機(jī)會(huì)還是需要把握的,和工作相關(guān)的內(nèi)容經(jīng)過(guò)長(zhǎng)久的沉淀一定會(huì)有收獲!
遺留的問(wèn)題
- 確認(rèn)彈窗的按鈕顏色無(wú)法修改
json復(fù)制代碼{ "type": "page", "body": { "label": "ajax請(qǐng)求", "type": "button", "actionType": "ajax", "confirmText": "確認(rèn)要發(fā)出這個(gè)請(qǐng)求?", "confirmTitle": "炸彈", "api": "/amis/api/mock2/form/saveForm" }}
大概率得通過(guò)修改源碼的方式處理,因?yàn)槭峭ㄟ^(guò)confirmText屬性直接配置出來(lái)的,目前沒(méi)找到合適的方式處理。
后續(xù):TL告訴我可以通過(guò)amis.embed來(lái)處理,公司使用的是ant-design-vue這個(gè)UI庫(kù),導(dǎo)入Modal組件覆蓋confirm屬性處理。(感覺(jué)還是文檔沒(méi)看仔細(xì))
- 上傳組件按鈕無(wú)法禁用
本來(lái)項(xiàng)目中已經(jīng)有現(xiàn)有的交互,只支持上傳一個(gè)文件,且上傳按鈕在上傳以后禁用,發(fā)現(xiàn)在這個(gè)上傳組件中無(wú)法實(shí)現(xiàn)。
- 上傳列表tooltip動(dòng)態(tài)計(jì)算位置異常
在可視化編輯器平臺(tái)上顯示是正常的
放到項(xiàng)目中以后,動(dòng)態(tài)計(jì)算出的left是0,top也是0
- radio組件的label插入圖標(biāo)
產(chǎn)品需求很簡(jiǎn)單,就是往radio單選框的文本框旁邊放一個(gè)圖標(biāo)鼠標(biāo)移入后展示文本提示效果,然后我看了下文檔中關(guān)于label屬性的描述。
(我。。。哎只能和產(chǎn)品說(shuō)目前低代碼應(yīng)該是不支持這樣,當(dāng)然我也嘗試了很多方法,文檔上提供的屬性不能滿足prd的需求,后來(lái)和產(chǎn)品溝通換成了其他提示方式的交互,淚目。)
大多碰到的還是些樣式交互上的問(wèn)題,后續(xù)在選用使用低代碼的場(chǎng)景也會(huì)更加注意評(píng)估,盡可能還是在交互邏輯場(chǎng)景簡(jiǎn)單的情況下進(jìn)行使用。(本來(lái)評(píng)估這次需求也挺簡(jiǎn)單的沒(méi)想到還是有點(diǎn)問(wèn)題,其他同學(xué)如果有碰到類(lèi)似的問(wèn)題,歡迎在評(píng)論區(qū)給出建議?。?/span>
使用心得
上面一直在說(shuō)碰到的問(wèn)題,也吐槽了開(kāi)發(fā)周期變長(zhǎng)的問(wèn)題,但是我想說(shuō)在相對(duì)簡(jiǎn)單的CURD的功能需求中,低代碼開(kāi)發(fā)模式比一般封裝的組件/開(kāi)源包來(lái)說(shuō)還是便捷了非常多。
在修改增加一些小的功能點(diǎn)的時(shí)候,低代碼模式?jīng)]有了傳統(tǒng)開(kāi)發(fā)模式中如提交代碼、構(gòu)建項(xiàng)目這些流程,在可視化編輯器中保存完畢后,讓測(cè)試同學(xué)刷新頁(yè)面,前端重新獲取JSON渲染即可。
amis的可視化編輯器整體使用起來(lái)也是比較簡(jiǎn)潔易懂的,推薦大家使用的時(shí)候,右側(cè)菜單事件里的動(dòng)作配置可以多看看,里面提供了比較豐富的動(dòng)作配置選項(xiàng)。
使用不同組件,對(duì)應(yīng)添加事件也會(huì)發(fā)生變化。
文本框可以添加的事件:
下拉框可以添加的事件:
事件添加完以后,點(diǎn)擊旁邊的?就可以出現(xiàn)動(dòng)作配置彈窗,里面提供了比較多可以配置的動(dòng)作類(lèi)型。
打開(kāi)彈窗動(dòng)作經(jīng)常發(fā)生在接口請(qǐng)求完畢以后,需要提示接口響應(yīng)結(jié)果,可以設(shè)置這個(gè)動(dòng)作的執(zhí)行/阻斷條件,動(dòng)態(tài)觸發(fā)這個(gè)動(dòng)作配置。
變量配置可以將請(qǐng)求結(jié)果/組件值改變等情況下變化的值存在特定的組件上,當(dāng)然如果響應(yīng)的結(jié)果剛好是數(shù)據(jù)域中已有的數(shù)據(jù)那就不需要有這一步了,很多時(shí)候接口響應(yīng)的其他數(shù)據(jù)可能會(huì)作為執(zhí)行其他業(yè)務(wù)邏輯的前置條件,此時(shí)就能派上用場(chǎng)了。
自定義JS在需求里使用了,在彈窗發(fā)起接口成功后,需要調(diào)取刷新列表接口,這時(shí)候可以從外部把方法傳入到amis渲染器中,然后通過(guò)這個(gè)動(dòng)作配置進(jìn)行觸發(fā),不過(guò)傳入到amis渲染的這個(gè)過(guò)程需要依賴amis.embed這個(gè)api。(>>>amis.embed詳情)
js復(fù)制代碼let amis = amisRequire('amis/embed');let amisJSON = { type: 'page', body: { type: 'tpl', tpl: '${myData}' }};let amisScoped = amis.embed('#root', amisJSON, { data: { // 初始化的時(shí)候可以定義一個(gè)特殊的props對(duì)象,解構(gòu)到data中使用 ...[props], myData: 'amis' }, context: { amisUser: { id: 1, name: 'test user' } }});
新的事物總會(huì)帶來(lái)利弊,嘗試能夠帶來(lái)經(jīng)驗(yàn)和價(jià)值的東西,總體還是值得的。
寫(xiě)到最后
本來(lái)想寫(xiě)Vue開(kāi)發(fā)者重學(xué)React(雖然這個(gè)題材比較常見(jiàn)),但是最近正好在用amis開(kāi)發(fā)業(yè)務(wù)需求,就讓amis的文章插個(gè)隊(duì)啦。(這也是筆者第一次寫(xiě)較長(zhǎng)的技術(shù)文章,希望大家能指正寫(xiě)得不好的地方!)
感謝看到這里的你,如果覺(jué)得文章對(duì)你有幫助,那真的是我非常開(kāi)心和榮幸的事了(順手能點(diǎn)個(gè)贊就更好啦!),有問(wèn)題也歡迎大家及時(shí)指正。
作者:LEIZ_ 鏈接:https://juejin.cn/post/7327198130050449408