摘要:經(jīng)過(guò)一周左右的時(shí)間完成了基礎(chǔ)組件的編寫(xiě)。配置涵蓋了目前的業(yè)務(wù)場(chǎng)景的基本需求,但是可擴(kuò)展性很低。最終決定采用的生態(tài)鏈來(lái)解決上述遇到的問(wèn)題。在指定的路徑下寫(xiě)入對(duì)應(yīng)的文件。
大綱
遇到的問(wèn)題場(chǎng)景及解決方案對(duì)比
什么是babel?
解決過(guò)程
目前遺留的問(wèn)題
目前實(shí)現(xiàn)功能API
參考
遇到的問(wèn)題場(chǎng)景及解決方案對(duì)比
我們目前采用的是antd + react(umi)的框架做業(yè)務(wù)開(kāi)發(fā)。在業(yè)務(wù)開(kāi)發(fā)過(guò)程中會(huì)有較多頻繁出現(xiàn)并且相似度很高的場(chǎng)景,比如基于一個(gè)table的基礎(chǔ)的增刪改查,這個(gè)相信大家都非常熟悉。在接到一個(gè)新的業(yè)務(wù)需求的時(shí)候,相信有不少人會(huì)選擇copy一份功能類(lèi)似的代碼然后基于這份代碼去改造以滿足當(dāng)前業(yè)務(wù),當(dāng)然我目前也是這樣做的~
其實(shí)想把這塊功能提取成一個(gè)公共組建的想法由來(lái)已久,最近開(kāi)始做基礎(chǔ)組件,便拿這個(gè)下手了。經(jīng)過(guò)一周左右的時(shí)間完成了基礎(chǔ)組件的編寫(xiě)。
查看基礎(chǔ)支持的功能點(diǎn)API。
基本的思路是通過(guò)json生成一些抽象配置,然后通過(guò)解析json的抽象配置+渲染器最終生成頁(yè)面。json配置涵蓋了目前80%的業(yè)務(wù)場(chǎng)景的基本需求,但是可擴(kuò)展性很低。比如一些復(fù)雜的業(yè)務(wù)場(chǎng)景:表單的關(guān)聯(lián)校驗(yàn)、數(shù)據(jù)關(guān)聯(lián)顯示、多級(jí)列表下鉆等等功能。雖然通過(guò)一些較為復(fù)雜的處理可以把這些功能融入進(jìn)來(lái),但最終組件將會(huì)異常龐大難以維護(hù)。
所以,我能不能通過(guò)這些json配置通過(guò)某種工具生成對(duì)應(yīng)的代碼?這樣一來(lái)以上提到的問(wèn)題就完全不存在了,因?yàn)檫@和我們自己寫(xiě)的代碼完全一樣,工具只是幫我們完成初始化的過(guò)程。所以后來(lái)想了很多辦法,最初采用template string的方式,這種方式較為簡(jiǎn)單粗暴,無(wú)非通過(guò)string中嵌套變量的判斷來(lái)輸出code。但是在實(shí)際寫(xiě)的時(shí)候發(fā)現(xiàn)很多問(wèn)題,比如
function的輸出(JSON.stringify會(huì)將function忽略)
多層函數(shù)嵌套之后怎么獲取最終渲染的節(jié)點(diǎn)code
嵌入變量怎么實(shí)現(xiàn)、umi-models-effects/reducer中額外的字典查詢(xún)?cè)趺瓷傻鹊?.
最終學(xué)習(xí)了一些生成代碼的工具比如angular-cli以及一些關(guān)于js生成代碼的文章,主要是通過(guò)知乎上的這篇討論了解到了大家是怎么處理這種問(wèn)題的。最終決定采用babel的生態(tài)鏈來(lái)解決上述遇到的問(wèn)題。
我們目前采用的方式是基于antd+react(umi)編寫(xiě)通用的CRUD模板,然后通過(guò)代碼生成器解析json中的配置生成對(duì)應(yīng)的代碼,大致的流程是:
React --> JavaScript AST ---> Code Generator --> Compiler --> Page
目前功能只是完成了初步版本,待應(yīng)用在項(xiàng)目中使用一段時(shí)間穩(wěn)定之后將會(huì)開(kāi)源~
什么是babel?
Babel是一個(gè)工具鏈,主要用于編譯ECMAScript 2015+代碼轉(zhuǎn)換為向后兼容的可運(yùn)行在各種瀏覽器上的JavaScript。主要功能:
語(yǔ)法轉(zhuǎn)換
環(huán)境中缺少的Polyfill功能
源代碼轉(zhuǎn)換
查看更多Babel功能
Understanding ASTs by Building Your Own Babel Plugin
如上提供了babel基本的流程及一篇介紹AST的文章。
我的理解中比如一段string類(lèi)型code,首先通過(guò)babel.transform會(huì)將code轉(zhuǎn)為一個(gè)包含AST(Abstract Syntax Tree)的Object,同樣可以使用@babel/generator將AST轉(zhuǎn)為code完成逆向過(guò)程。 例如一段變量聲明代碼:
const a = 1;
在解析之后的結(jié)構(gòu)為:
{ "type": "Program", "start": 0, "end": 191, "body": [ { "type": "VariableDeclaration", "start": 179, "end": 191, "declarations": [ { "type": "VariableDeclarator", "start": 185, "end": 190, "id": { "type": "Identifier", "start": 185, "end": 186, "name": "a" }, "init": { "type": "Literal", "start": 189, "end": 190, "value": 1, "raw": "1" } } ], "kind": "const" } ], "sourceType": "module" }
首先類(lèi)型為VariableDeclaration,首先他的類(lèi)型是const,可以通過(guò)點(diǎn)擊查看api其它還有let、var的值。其次是聲明declarations部分,這里值為數(shù)組,因?yàn)槲覀兛梢酝瑫r(shí)定義多個(gè)變量。數(shù)組中值的類(lèi)型為VariableDeclarator,包含id和init兩個(gè)參數(shù),分別為變量名稱(chēng)以及變量值。id的類(lèi)型為Identifier,譯為修飾符即是變量名稱(chēng)。init類(lèi)型為Literal,即是常量,一般常用的有stringLiteral、numericliteral、booleanliteral等。此時(shí)即完成了變量賦值的過(guò)程。
當(dāng)然這只是很簡(jiǎn)單的語(yǔ)法轉(zhuǎn)換,如果大家想學(xué)習(xí)更多關(guān)于轉(zhuǎn)換及類(lèi)型的知識(shí),可參考如下兩個(gè)官方鏈接:
babel-types
ast轉(zhuǎn)換工具
解決過(guò)程
首先定義目錄結(jié)構(gòu):
. ├── genCode // 代碼生成器 | ├── genDetail // 需要新頁(yè)面打開(kāi)時(shí)多帶帶的detail目錄 | └── genIndex // 首頁(yè) | └── genModels // umi models | └── genServices // umi services | └── genTableFilter // table篩選區(qū)域 | └── genTableForm // 非新頁(yè)面模式,新增/更新模態(tài)框 | └── genUpsert // 新頁(yè)面模式下,新增/更新頁(yè)面 | └── genUtils // 生成工具類(lèi) ├── schema // 模型定義文件 | ├── table // 當(dāng)前要生成的模型 | └── ├──config.js // 基礎(chǔ)配置 | └── └──dataSchema.js // 列表、新增、更新配置 | └── └──querySchema.js // 篩選項(xiàng)配置 ├── scripts // 生成腳本 | ├── generateCode.js // 生成主文件 | └── index.js // 入口 | └── utils.js // 工具類(lèi) ├── toCopyFiles // 生成時(shí)需要拷貝的文件,比如less └── index.js // 主入口
主體流程為:
指定要生成代碼的路徑。
根據(jù)schema中當(dāng)前json配置路徑,依次調(diào)用genCode目錄中各個(gè)模塊的代碼生成方法獲取對(duì)應(yīng)code。
在指定的路徑下寫(xiě)入對(duì)應(yīng)的文件。
執(zhí)行eslint ${filePath} --fix格式化生成的代碼。
根據(jù)配置對(duì)應(yīng)復(fù)制toCopyFiles文件夾中依賴(lài)的less等文件到對(duì)應(yīng)的文件夾。
其中主要模塊為genCode文件夾中根據(jù)json配置生成代碼的過(guò)程。 以genModels為例,首先提取可以使用template string完成的部分,減少代碼解析的工作量。
module.exports = (tableConfig) => { return ` import { message } from "antd"; import { routerRedux } from "dva/router" import { parse } from "qs" ${dynamicImport(dicArray, namespace)} export default { namespace: "${namespace}", state: { ... }, effects: { *fetch({ payload }, { call, put }) { const response = yield call(queryData, payload); if (response && response.errorCode === 0) { yield put({ type: "save", payload: response.data, }); } else { message.error(response && response.errorMessage || "請(qǐng)求失敗") } }, ..., ${dynamicYieldFunction(dicArray)} }, reducers: { save(state, action) { return { ...state, data: action.payload, }; }, ..., ${dynamicReducerFunction(dicArray)} }, }; ` }
因?yàn)榱斜頂?shù)據(jù)可能有字典項(xiàng)從后臺(tái)獲取值來(lái)對(duì)應(yīng)顯示,所以import、effects、reducers模塊均有需根據(jù)配置動(dòng)態(tài)生成的代碼。 以dynamecImport為例:
function dynamicImport (dicArray, namespace) { // 基礎(chǔ)api import let baseImport = [ "queryData", "removeData", "addData", "updateData", "findById" ] // 判斷json數(shù)據(jù)中是否有需從后臺(tái)加載項(xiàng) if (dicArray && dicArray.length) { baseImport = baseImport.concat(dicArray.map(key => getInjectVariableKey(key))) } // 遍歷生成依賴(lài)項(xiàng) const _importDeclarationArray = map(specifier => ( _importDeclarationArray.push(t.importSpecifier(t.identifier(specifier), t.identifier(specifier))) )) // 定義importDeclaration const ast = t.importDeclaration( _importDeclarationArray, t.stringLiteral(`../services/${namespace}`) ) // 通過(guò)@babel/generator 將ast生成code const { code } = generate(ast) return code }
其它代碼生成邏輯類(lèi)似,有不確定如何生成的部分可參考上方提供的鏈接完成代碼轉(zhuǎn)換再去生成。
若有通過(guò)babel轉(zhuǎn)換無(wú)法生成的代碼,可通過(guò)正則來(lái)完成。
例如以下umi-models代碼:
*__dicData({ payload }, { call, put }) { const response = yield call(__dicData, payload); if (response && response.errorCode === 0) { yield put({ type: "updateDic", payload: response.data, }); } else { message.error(response && response.errorMessage || "請(qǐng)求失敗") } }
基礎(chǔ)代碼可通過(guò)yieldExpression生成,但是轉(zhuǎn)換之后無(wú)function之后的*符號(hào),反復(fù)查了文檔之后沒(méi)有解決辦法,最后只能將生成完的code利用正則替換來(lái)解決。 如果大家有遇到類(lèi)似的問(wèn)題歡迎討論~
問(wèn)題
目前使用的編輯器組件為braft-editor,但是結(jié)合antd使用initialValue不生效,必須使用setFieldsValue。但是使用useEffects時(shí)會(huì)默認(rèn)添加props.form作為依賴(lài)并且props.form會(huì)不斷變化而觸發(fā)死循環(huán),目前無(wú)奈只有禁用eslint react-hooks/exhaustive-deps。
useEffect(() => { props.form.setFieldsValue({ editorArea: BraftEditor.createEditorState(current.editorArea), editorArea2: BraftEditor.createEditorState(current.editorArea2) }); }, [current.editorArea, current.editorArea2]);
生成的代碼怎么刪除未使用的依賴(lài)?使用eslint --fix不會(huì)刪除未使用的變量定義。
初始化之后的代碼要修改怎么辦?因當(dāng)前方法只會(huì)完成代碼初始化過(guò)程,以后修改的過(guò)程暫無(wú)思路解決。
功能API
參數(shù)規(guī)范參考react-antd-admin 功能配置包含三個(gè)基礎(chǔ)配置文件:
config.json配置基本屬性
dataSchema.json配置列表及新增修改字段
querySchema.json配置篩選區(qū)域字段
config.json
參數(shù) | 必填 | 類(lèi)型 | 默認(rèn)值 | 說(shuō)明 |
---|---|---|---|---|
namespace | true | string | null | 命名空間 |
showExport | false | boolean | true | 是否顯示導(dǎo)出 |
showCreate | false | boolean | true | 是否顯示創(chuàng)建 |
showDetail | false | boolean | true | 是否顯示查看 |
showUpdate | false | boolean | true | 是否顯示修改 |
showDelete | false | boolean | true | 是否顯示刪除 |
newRouterMode | false | boolean | false | 在新的頁(yè)面新增/編輯/查看詳情。若包含富文本編輯器,建議此值設(shè)為true,富文本在模態(tài)框展示不是非常美觀。 |
showBatchDelete | false | boolean | true | 是否顯示批量刪除,需multiSelection為 true |
multiSelection | false | boolean | true | 是否支持多選 |
defaultDateFormat | false | string | "YYYY-MM-DD" | 日期格式 |
upload | false | object | null | 上傳相關(guān)配置,上傳圖片和上傳普通文件分別配置。 詳見(jiàn)下方upload屬性 |
pagination | false | object | null | 分頁(yè)相關(guān)配置, 詳見(jiàn)下方pagination屬性 |
dictionary | false | array | null | 需要請(qǐng)求的字典項(xiàng),用于下拉框或treeSelect的值為從后端獲取的情況,可在dataSchema 和querySchema中使用, 詳見(jiàn)下方dictionary屬性 |
參數(shù) | 必填 | 類(lèi)型 | 默認(rèn)值 | 說(shuō)明 |
---|---|---|---|---|
uploadUrl | false | string | null | 默認(rèn)的上傳接口.優(yōu)先級(jí)image/fileApiUrl > uploadUrl > Global.apiPath |
imageApiUrl | false | string | null | 默認(rèn)的圖片上傳接口 |
fileApiUrl | false | string | null | 默認(rèn)的文件上傳接口 |
image | false | string | "/uploadImage" | 默認(rèn)的上傳圖片接口 |
imageSizeLimit | false | number | 1500 | 默認(rèn)的圖片大小限制, 單位KB |
file | false | string | "/uploadFile" | 默認(rèn)的上傳文件接口 |
fileSizeLimit | false | number | 10240 | 默認(rèn)的文件大小限制, 單位KB |
參數(shù) | 必填 | 類(lèi)型 | 默認(rèn)值 | 說(shuō)明 |
---|---|---|---|---|
pageSize | false | number | 10 | 每頁(yè)顯示數(shù)量 |
showSizeChanger | false | boolean | false | 是否可以改變pageSize |
pageSizeOptions | false | array | ["10", "20", "50", "100"] | 指定每頁(yè)可以顯示多少條 |
showQuickJumper | false | boolean | false | 是否可以快速跳轉(zhuǎn)至某頁(yè) |
showTotal | false | boolean | true | 是否顯示總數(shù) |
參數(shù) | 必填 | 類(lèi)型 | 默認(rèn)值 | 說(shuō)明 |
---|---|---|---|---|
key | true | string | null | 變量標(biāo)識(shí) |
url | true | string | null | 請(qǐng)求數(shù)據(jù)地址 |
dataSchema.json
參數(shù) | 必填 | 類(lèi)型 | 默認(rèn)值 | 說(shuō)明 |
---|---|---|---|---|
key | true | string | null | 唯一標(biāo)識(shí)符 |
title | true | string | null | 顯示名稱(chēng) |
primary | false | boolean | false | 主鍵 如果不指定主鍵, 不能update/delete, 但可以insert; 如果指定了主鍵, insert/update時(shí)不能填寫(xiě)主鍵的值; |
showType | false | string | input | 顯示類(lèi)型 input/textarea/inputNumber/datePicker/rangePicker/radio/select/checkbox/multiSelect/image/file/cascader/editor |
disabled | false | boolean | false | 表單中這一列是否禁止編輯 |
addonBefore | false | string/ReactNode | null | showType 為input可以設(shè)置前標(biāo)簽 |
addonAfter | false | string/ReactNode | null | showType 為input可以設(shè)置后標(biāo)簽 |
placeholder | false | string | null | 默認(rèn)提示文字 |
format | false | string | null | 日期類(lèi)型的格式 |
showInTable | false | boolean | true | 這一列是否要在table中展示 |
showInForm | false | boolean | true | 是否在新增或編輯的表單中顯示 |
validator | false | boolean | null | 設(shè)置校驗(yàn)規(guī)則, 參考https://github.com/yiminghe/async-validator#rules |
width | false | string/number | null | 列寬度 |
options | false | array | null | format:[{ key: "", value: "" }]或string。showType為cascader時(shí),此字段暫不支持Array,數(shù)據(jù)只能通過(guò)異步獲取。 |
min | false | number | null | 數(shù)字輸入的最小值 |
max | false | number | null | 數(shù)字輸入的最大值 |
accept | false | string | null | 上傳文件格式限制 |
sizeLimit | false | number | 20480 | 上傳文件格式限制 |
url | false | string | null | 上傳圖片url。圖片的上傳接口, 可以針對(duì)每個(gè)上傳組件多帶帶配置, 如果不多帶帶配置就使用config.js中的默認(rèn)值;如果這個(gè)url是http開(kāi)頭的, 就直接使用這個(gè)接口; 否則會(huì)根據(jù)config.js中的配置判斷是否加上host |
sorter | false | boolean | false | 是否排序 |
actions | false | array | null | 操作 |
參數(shù) | 必填 | 類(lèi)型 | 默認(rèn)值 | 說(shuō)明 |
---|---|---|---|---|
keys | false | array | null | 允許更新哪些字段, 如果不設(shè)置keys, 就允許更所有字段 |
name | true | string | null | 展示標(biāo)題 |
type | false | string | null | update/delete/newLine/component |
querySchema.json
參數(shù) | 必填 | 類(lèi)型 | 默認(rèn)值 | 說(shuō)明 |
---|---|---|---|---|
key | true | string | null | 唯一標(biāo)識(shí)符 |
title | true | string | null | 顯示名稱(chēng) |
placeholder | false | string | null | 提示語(yǔ) |
showType | false | string | input | 顯示類(lèi)型, 一些可枚舉的字段, 比如type, 可以被顯示為單選框或下拉框 input, 就是一個(gè)普通的輸入框, 這時(shí)可以省略showType字段 目前可用的showType: input/inputNumber/datePicker/rangePicker/select/radio/checkbox/multiSelect/cascader |
addonBefore | false | string/ReactNode | null | showType 為input可以設(shè)置前標(biāo)簽 |
addonAfter | false | string/ReactNode | null | showType 為input可以設(shè)置后標(biāo)簽 |
defaultValue | false | string/array/number | null | 多選的defaultValue是個(gè)數(shù)組 |
min | false | number | null | showType為 inputNumber 時(shí)可設(shè)置最小值 |
max | false | number | null | showType為 inputNumber 時(shí)可設(shè)置最大值 |
options | false | array | null | options的key要求必須是string, 否則會(huì)有warning normal-format: [{"key": "", "value": ""}] cascader-format: [{"value": "", "label": "", children: ["value": "", "label": "", children: []]}] 如果值為string,代表異步獲取的數(shù)據(jù),則獲取當(dāng)前命名空間下該key對(duì)應(yīng)的值 |
defaultValueBegin | false | string | null | showType為 rangePicker 時(shí)可設(shè)置默認(rèn)開(kāi)始值 |
defaultValueEnd | false | string | null | showType為 rangePicker 時(shí)可設(shè)置默認(rèn)結(jié)束值 |
placeholderBegin | false | string | 開(kāi)始日期 | showType為 rangePicker 時(shí)可設(shè)置默認(rèn)開(kāi)始提示語(yǔ) |
placeholderEnd | false | string | 結(jié)束日期 | showType為 rangePicker 時(shí)可設(shè)置默認(rèn)結(jié)束提示語(yǔ) |
format | false | string | null | 日期篩選格式 |
showInSimpleMode | false | boolean | false | 在簡(jiǎn)單查詢(xún)方式下展示,若數(shù)據(jù)中有一項(xiàng)包含此字段且為true的值,則開(kāi)啟簡(jiǎn)單/復(fù)雜篩選切換 |
參考
react-antd-admin
AST語(yǔ)法轉(zhuǎn)換器
babel-types-api
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/6655.html
摘要:配置涵蓋了目前的業(yè)務(wù)場(chǎng)景的基本需求,但是可擴(kuò)展性很低。最終決定采用的生態(tài)鏈來(lái)解決上述遇到的問(wèn)題。在指定的路徑下寫(xiě)入對(duì)應(yīng)的文件。 大綱 遇到的問(wèn)題場(chǎng)景及解決方案對(duì)比 什么是babel? 解決過(guò)程 目前遺留的問(wèn)題 目前實(shí)現(xiàn)功能API 參考 遇到的問(wèn)題場(chǎng)景及解決方案對(duì)比 我們目前采用的是antd + react(umi)的框架做業(yè)務(wù)開(kāi)發(fā)。在業(yè)務(wù)開(kāi)發(fā)過(guò)程中會(huì)有較多頻繁出現(xiàn)并且相似度很高的場(chǎng)...
摘要:讓人又愛(ài)又恨的微信小程序自微信小程序以下簡(jiǎn)稱(chēng)小程序誕生以來(lái),就伴隨著贊譽(yù)與爭(zhēng)議不斷。同時(shí)于開(kāi)發(fā)者來(lái)說(shuō),小程序的生態(tài)不斷在完善,許多的坑已被踩平,雖然還是存在一些令人詬病的問(wèn)題,但已經(jīng)足見(jiàn)微信的誠(chéng)意了。 Taro 介紹 在互聯(lián)網(wǎng)不斷發(fā)展的今天,前端程序員們也不斷面臨著新的挑戰(zhàn),在這個(gè)變化多端、不斷革新自己的領(lǐng)域,每一年都有新的美好事物在發(fā)生。從去年微信小程序的誕生,到今年的逐漸火熱,以及...
摘要:前端基礎(chǔ)架構(gòu)和硬核介紹技術(shù)棧的選擇首先我們構(gòu)建前端架構(gòu)需要對(duì)前端生態(tài)圈有一切了解,并且最好帶有一定的技術(shù)前瞻性,好的技術(shù)架構(gòu)可能日后會(huì)方便的擴(kuò)展,減少重構(gòu)的次數(shù),即使重構(gòu)也不需要大動(dòng)干戈,我通常選型技術(shù)棧會(huì)參考以下三點(diǎn)一提出自身業(yè)務(wù)的需求是 # 前端基礎(chǔ)架構(gòu)和硬核介紹 showImg(https://segmentfault.com/img/remote/146000001626972...
摘要:前端基礎(chǔ)架構(gòu)和硬核介紹技術(shù)棧的選擇首先我們構(gòu)建前端架構(gòu)需要對(duì)前端生態(tài)圈有一切了解,并且最好帶有一定的技術(shù)前瞻性,好的技術(shù)架構(gòu)可能日后會(huì)方便的擴(kuò)展,減少重構(gòu)的次數(shù),即使重構(gòu)也不需要大動(dòng)干戈,我通常選型技術(shù)棧會(huì)參考以下三點(diǎn)一提出自身業(yè)務(wù)的需求是 # 前端基礎(chǔ)架構(gòu)和硬核介紹 showImg(https://segmentfault.com/img/remote/146000001626972...
摘要:前端準(zhǔn)備前端了解過(guò)關(guān)了嗎前端基礎(chǔ)架構(gòu)和硬核介紹技術(shù)棧的選擇首先我們構(gòu)建前端架構(gòu)需要對(duì)前端生態(tài)圈有一切了解,并且最好帶有一定的技術(shù)前瞻性,好的技術(shù)架構(gòu)可能日后會(huì)方便的擴(kuò)展,減少重構(gòu)的次數(shù),即使重構(gòu)也不需要大動(dòng)干戈,我通常選型技術(shù)棧會(huì)參考以下三 # 前端準(zhǔn)備 :前端了解過(guò)關(guān)了嗎?前端基礎(chǔ)架構(gòu)和硬核介紹 showImg(https://segmentfault.com/img/remote/...
閱讀 3611·2021-10-09 09:43
閱讀 6257·2021-09-07 10:15
閱讀 2804·2019-08-30 14:03
閱讀 3141·2019-08-29 11:01
閱讀 1830·2019-08-29 10:56
閱讀 1159·2019-08-28 17:52
閱讀 3562·2019-08-26 11:42
閱讀 2627·2019-08-26 10:33