контур
- Возникшие проблемные сценарии и сравнение решений
- Что такое вавилон?
- процесс решения
- текущие проблемы
- В настоящее время реализуется функциональный API
- Ссылаться на
Возникшие проблемные сценарии и сравнение решений
В настоящее время мы используем платформу antd + react(umi) для развития бизнеса. В процессе развития бизнеса будут более частые и очень похожие сценарии, такие как добавление, удаление, изменение и проверка на основе таблицы, с которой, я думаю, все хорошо знакомы. Я полагаю, что при получении нового бизнес-требования многие люди захотят скопировать код с аналогичными функциями, а затем преобразовать его в соответствии с текущим бизнесом на основе этого кода.Конечно, я делаю то же самое сейчас~
На самом деле идея вынести эту функцию в публичный компонент витала давно, недавно я начал делать базовые компоненты, поэтому начал с этого. Примерно через неделю написание основных компонентов было завершено.
Посмотреть базовую поддержкуAPI функциональной точки.
Основная идея состоит в том, чтобы сгенерировать некоторую абстрактную конфигурацию через json, а затем, наконец, сгенерировать страницу, проанализировав абстрактную конфигурацию json + рендерер. Конфигурация json покрывает основные требования 80% текущих бизнес-сценариев, но масштабируемость очень низкая. Например, некоторые сложные бизнес-сценарии:表单的关联校验,数据关联显示,多级列表下钻и так далее. Хотя можно включить эти функции с помощью более сложной обработки, конечный компонент будет чрезвычайно большим и сложным в обслуживании.
Итак, могу ли я использовать какой-либо инструмент для создания соответствующего кода с помощью этих конфигураций json? Таким образом, проблемы, упомянутые выше, вообще не существуют, потому что это точно такой же код, который мы написали сами, а инструмент просто помогает нам завершить процесс инициализации. Поэтому позже я придумал множество способов, первоначально используяtemplate stringЭтот метод относительно прост и груб, и это не что иное, как вывод кода путем оценки вложенных переменных в строке. Однако при написании было обнаружено много проблем, таких как
-
functionВывод (JSON.stringify будет игнорировать функцию) - Как получить окончательный отрендеренный код узла после вложения нескольких уровней функций
- Как реализовать встроенные переменные, как генерировать дополнительные словарные запросы в umi-models-effects/reducer и т. д.
Наконец, я изучил некоторые инструменты для генерации кода, такие как angular-cli, и несколько статей о генерации кода js, в основном благодаря этой статье на Zhihu.ОбсуждатьЯ знаю, как люди решают эту проблему. Наконец решил использовать экологическую цепочку Babel для решения вышеуказанных проблем.
В настоящее время мы используем способ, которым мы пишем общий шаблон CRUD на основе antd+react (umi), а затем используем генератор кода для анализа конфигурации в json для генерации соответствующего кода.Общий процесс таков:
React --> JavaScript AST ---> Code Generator --> Compiler --> Page
В настоящее время функция является только предварительной версией, и она будет открыта после того, как приложение будет использоваться в проекте в течение определенного периода времени и станет стабильным ~
Что такое вавилон?
Babel — это набор инструментов для компиляции кода ECMAScript 2015+ в обратно совместимый JavaScript, который работает в различных браузерах. Основная функция:
- Преобразование синтаксиса
- Функциональность полифилла отсутствует в среде
- преобразование исходного кода
- Посмотреть больше функций Babel
Базовый процесс Babel и статья, посвященная AST, приведены выше.
В моем понимании, например, кусок кода строкового типа, сначала через babel.transform код будет преобразован в Object содержащий AST (Abstract Syntax Tree).Также можно использовать @babel/generator для преобразования AST в код для завершения обратного процесса. Например, код объявления переменной:
const a = 1;
Структура после разбора такая:
{
"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"
}
Первый типVariableDeclaration, в первую очередь его типconst, вы можете нажать, чтобы просмотреть API и другиеlet,varценность . после чего следует заявлениеdeclarationsраздел, значение здесь является массивом, потому что мы можем определить несколько переменных одновременно. Тип значений в массивеVariableDeclarator,Включатьidа такжеinitДвумя параметрами являются имя переменной и значение переменной.idимеет типIdentifier, который транслируется в модификатор, являющийся именем переменной.initТипLiteral, то есть константы, обычно используемыеstringLiteral,numericliteral,booleanliteralЖдать. На этом процесс присвоения переменной завершен.
Конечно, это просто преобразование синтаксиса.Если вы хотите узнать больше о преобразованиях и типах, вы можете обратиться к следующим двум официальным ссылкам:
процесс решения
Сначала определите структуру каталогов:
.
├── genCode // 代码生成器
| ├── genDetail // 需要新页面打开时单独的detail目录
| └── genIndex // 首页
| └── genModels // umi models
| └── genServices // umi services
| └── genTableFilter // table筛选区域
| └── genTableForm // 非新页面模式,新增/更新模态框
| └── genUpsert // 新页面模式下,新增/更新页面
| └── genUtils // 生成工具类
├── schema // 模型定义文件
| ├── table // 当前要生成的模型
| └── ├──config.js // 基础配置
| └── └──dataSchema.js // 列表、新增、更新配置
| └── └──querySchema.js // 筛选项配置
├── scripts // 生成脚本
| ├── generateCode.js // 生成主文件
| └── index.js // 入口
| └── utils.js // 工具类
├── toCopyFiles // 生成时需要拷贝的文件,比如less
└── index.js // 主入口
Основной процесс:
- Указывает путь для создания кода.
- В соответствии с текущим путем конфигурации json в схеме вызовите метод генерации кода каждого модуля в каталоге genCode, чтобы получить соответствующий код.
- Запишите соответствующий файл по указанному пути.
- воплощать в жизнь
eslint ${filePath} --fixОтформатируйте сгенерированный код. - Скопируйте зависимые файлы less и другие файлы из папки toCopyFiles в соответствующую папку в соответствии с конфигурацией.
Основной модуль — это процесс генерации кода согласно конфигурации json в папке genCode.
Взяв 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 || '请求失败')
}
},
...,
${dynamicYieldFunction(dicArray)}
},
reducers: {
save(state, action) {
return {
...state,
data: action.payload,
};
},
...,
${dynamicReducerFunction(dicArray)}
},
};
`
}
Поскольку данные списка могут иметь элементы словаря для получения значений из фона для отображения соответственно, поэтомуimport,effects,reducersМодули имеют код, который необходимо динамически генерировать на основе конфигурации.
Возьмите DynamecImport в качестве примера:
function dynamicImport (dicArray, namespace) {
// 基础api import
let baseImport = [
'queryData', 'removeData', 'addData', 'updateData', 'findById'
]
// 判断json数据中是否有需从后台加载项
if (dicArray && dicArray.length) {
baseImport = baseImport.concat(dicArray.map(key => getInjectVariableKey(key)))
}
// 遍历生成依赖项
const _importDeclarationArray = map(specifier => (
_importDeclarationArray.push(t.importSpecifier(t.identifier(specifier), t.identifier(specifier)))
))
// 定义importDeclaration
const ast = t.importDeclaration(
_importDeclarationArray,
t.stringLiteral(`../services/${namespace}`)
)
// 通过@babel/generator 将ast生成code
const { code } = generate(ast)
return code
}
Другая логика генерации кода аналогична.Если вы не знаете, как сгенерировать часть, вы можете обратиться по ссылке, указанной выше, чтобы завершить преобразование кода, а затем сгенерировать ее.
Если есть код, который нельзя сгенерировать с помощью Babel-преобразования, это можно сделать с помощью регулярных выражений.
Например, следующее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 || '请求失败')
}
}
Базовый код доступен черезyieldExpressionСгенерировано, но не работает после конвертации*символ, после многократной проверки документа решения нет. Наконец, сгенерированный код может быть решен только с помощью регулярной замены.
Если вы столкнулись с подобными проблемами, добро пожаловать на обсуждение ~
вопрос
- Используемые в настоящее время компоненты редактора:braft-editor, но использование initialValue в сочетании с antd не действует, необходимо использовать setFieldsValue. Однако при использовании useEffects props.form будет добавлен в качестве зависимости по умолчанию, а props.form будет продолжать изменяться, вызывая бесконечный цикл.В настоящее время у нас нет другого выбора, кроме как отключить eslint react-hooks/exhaustive-deps.
useEffect(() => {
props.form.setFieldsValue({
editorArea: BraftEditor.createEditorState(current.editorArea),
editorArea2: BraftEditor.createEditorState(current.editorArea2)
});
}, [current.editorArea, current.editorArea2]);
- Как сгенерированный код удаляет неиспользуемые зависимости? Использование eslint --fix не удаляет неиспользуемые определения переменных.
- Как изменить код после инициализации? Поскольку текущий метод завершает только процесс инициализации кода, нет решения для будущего процесса модификации.
Функциональный API
Справочник по спецификации параметраreact-antd-adminФункциональная конфигурация состоит из трех основных файлов конфигурации:
-
config.jsonНастройка основных свойств -
dataSchema.jsonСписок конфигурации и новые измененные поля -
querySchema.jsonНастройка полей области фильтра
config.json
Список конфигурации
| параметр | Необходимый | Типы | По умолчанию | иллюстрировать |
|---|---|---|---|---|
| namespace | true | string | null | Пространства имен |
| showExport | false | boolean | true | показывать ли экспорт |
| showCreate | false | boolean | true | Показывать ли создание |
| showDetail | false | boolean | true | Показывать ли представление |
| showUpdate | false | boolean | true | показывать ли изменения |
| showDelete | false | boolean | true | Показывать удаление |
| newRouterMode | false | boolean | false | Добавить/редактировать/просмотреть детали на новой странице. Если включен редактор форматированного текста, рекомендуется установить для этого значения значение true, форматированный текст, отображаемый в модальном окне, не очень красив. |
| showBatchDelete | false | boolean | true | Отображать ли пакетное удаление, multiSelection должен быть истинным |
| multiSelection | false | boolean | true | Поддерживать ли множественный выбор |
| defaultDateFormat | false | string | 'YYYY-MM-DD' | Формат даты |
| upload | false | object | null | Загрузка соответствующей конфигурации, загрузка изображений и загрузка общих файлов настраиваются отдельно. Подробнее см. свойство загрузки ниже. |
| pagination | false | object | null | Конфигурация, связанная с нумерацией страниц, см. свойство пагинации ниже |
| dictionary | false | array | null | Запрашиваемый элемент словаря используется, когда значение раскрывающегося списка или treeSelect получается из серверной части. Его можно использовать в dataSchema и querySchema. Подробности см. в атрибуте словаря ниже. |
upload
| параметр | Необходимый | Типы | По умолчанию | иллюстрировать |
|---|---|---|---|---|
| uploadUrl | false | string | null | Интерфейс загрузки по умолчанию. Priority image/fileApiUrl > uploadUrl > Global.apiPath. |
| imageApiUrl | false | string | null | Интерфейс загрузки изображений по умолчанию |
| fileApiUrl | false | string | null | Интерфейс загрузки файлов по умолчанию |
| image | false | string | '/uploadImage' | Интерфейс загрузки изображений по умолчанию |
| imageSizeLimit | false | number | 1500 | Ограничение размера изображения по умолчанию, в КБ |
| file | false | string | '/uploadFile' | Интерфейс загрузки файлов по умолчанию |
| fileSizeLimit | false | number | 10240 | Ограничение размера файла по умолчанию, в КБ |
pagination
| параметр | Необходимый | Типы | По умолчанию | иллюстрировать |
|---|---|---|---|---|
| pageSize | false | number | 10 | Показать количество на странице |
| showSizeChanger | false | boolean | false | Можно ли изменить pageSize |
| pageSizeOptions | false | array | ['10', '20', '50', '100'] | Укажите, сколько строк может отображаться на странице |
| showQuickJumper | false | boolean | false | Можно ли быстро перейти на страницу |
| showTotal | false | boolean | true | показывать ли общую сумму |
dictionary
| параметр | Необходимый | Типы | По умолчанию | иллюстрировать |
|---|---|---|---|---|
| key | true | string | null | Идентификатор переменной |
| url | true | string | null | Запросить адрес данных |
dataSchema.json
Список конфигурации
| параметр | Необходимый | Типы | По умолчанию | иллюстрировать |
|---|---|---|---|---|
| key | true | string | null | уникальный идентификатор |
| title | true | string | null | показать имя |
| primary | false | boolean | false | Первичный ключ Если вы не укажете первичный ключ, вы не сможете обновить/удалить, но сможете вставить; Если первичный ключ указан, значение первичного ключа не может быть заполнено во время вставки/обновления; |
| showType | false | string | input | тип дисплея input/textarea/inputNumber/datePicker/rangePicker/radio/select/checkbox/multiSelect/image/file/cascader/editor |
| disabled | false | boolean | false | Отключить ли редактирование этого столбца в форме |
| addonBefore | false | string/ReactNode | null | showType может установить переднюю метку для ввода |
| addonAfter | false | string/ReactNode | null | showType для ввода может установить заднюю метку |
| placeholder | false | string | null | Текст подсказки по умолчанию |
| format | false | string | null | Формат типа даты |
| showInTable | false | boolean | true | Должен ли этот столбец отображаться в таблице |
| showInForm | false | boolean | true | Показывать ли в новых или измененных формах |
| validator | false | boolean | null | Чтобы установить правила проверки, обратитесь к https://github.com/yiminghe/async-validator#rules. |
| width | false | string/number | null | ширина колонки |
| options | false | array | null | формат: [{ ключ: '', значение: '' }] или строка. Когда showType имеет значение cascader, это поле не поддерживает Array, и данные могут быть получены только асинхронно. |
| min | false | number | null | Минимальное значение цифрового входа |
| max | false | number | null | Максимальное значение цифрового входа |
| accept | false | string | null | Ограничения формата загружаемого файла |
| sizeLimit | false | number | 20480 | Ограничения формата загружаемого файла |
| url | false | string | null | Загрузить URL изображения. Интерфейс загрузки изображения можно настроить отдельно для каждого компонента загрузки. Если он не настроен отдельно, используйте значение по умолчанию в config.js; если URL-адрес начинается с http, используйте этот интерфейс напрямую; Настройте, чтобы определить, следует ли добавлять хост |
| sorter | false | boolean | false | сортировать ли |
| actions | false | array | null | действовать |
actions
| параметр | Необходимый | Типы | По умолчанию | иллюстрировать |
|---|---|---|---|---|
| keys | false | array | null | Какие поля разрешено обновлять, если ключи не установлены, все поля разрешено обновлять |
| name | true | string | null | отображать заголовок |
| type | false | string | null | update/delete/newLine/component |
querySchema.json
Список конфигурации
| параметр | Необходимый | Типы | По умолчанию | иллюстрировать |
|---|---|---|---|---|
| key | true | string | null | уникальный идентификатор |
| title | true | string | null | показать имя |
| placeholder | false | string | null | намекать |
| showType | false | string | input | Тип отображения, некоторые перечисляемые поля, такие как тип, могут отображаться в виде переключателей или раскрывающихся списков. input, который представляет собой обычное поле ввода, и в этом случае поле showType можно опустить В настоящее время доступно showType: input/inputNumber/datePicker/rangePicker/select/radio/checkbox/multiSelect/cascader |
| addonBefore | false | string/ReactNode | null | showType может установить переднюю метку для ввода |
| addonAfter | false | string/ReactNode | null | showType для ввода может установить заднюю метку |
| defaultValue | false | string/array/number | null | Значение по умолчанию множественного выбора представляет собой массив |
| min | false | number | null | Минимальное значение может быть установлено, когда showType имеет значение inputNumber. |
| max | false | number | null | Максимальное значение может быть установлено, когда showType равно inputNumber. |
| options | false | array | null | Ключ опций должен быть строкой, иначе будет предупреждение normal-format: [{"key": "", "value": ""}] cascader-format: [{"value": "", "label": "", children: ["value": "", "label": "", children: []]}] Если значение является строкой, оно представляет данные, полученные асинхронно, и получается значение, соответствующее ключу в текущем пространстве имен. |
| defaultValueBegin | false | string | null | Начальное значение по умолчанию может быть установлено, когда showType имеет значение rangePicker. |
| defaultValueEnd | false | string | null | Конечное значение по умолчанию может быть установлено, когда showType имеет значение rangePicker. |
| placeholderBegin | false | string | Дата начала | Приглашение запуска по умолчанию можно установить, если для showType установлено значение rangePicker. |
| placeholderEnd | false | string | Дата окончания | Когда showType имеет значение rangePicker, можно установить окончательную подсказку по умолчанию. |
| format | false | string | null | Формат фильтра даты |
| showInSimpleMode | false | boolean | false | Отображается в режиме простого запроса, если в данных есть значение, содержащее это поле и истинное, переключатель простой/сложной фильтрации включен |