Практика Babel по повышению эффективности интерфейса

JavaScript Babel
Практика Babel по повышению эффективности интерфейса

контур

  1. Возникшие проблемные сценарии и сравнение решений
  2. Что такое вавилон?
  3. процесс решения
  4. текущие проблемы
  5. В настоящее время реализуется функциональный API
  6. Ссылаться на

Возникшие проблемные сценарии и сравнение решений

В настоящее время мы используем платформу antd + react(umi) для развития бизнеса. В процессе развития бизнеса будут более частые и очень похожие сценарии, такие как добавление, удаление, изменение и проверка на основе таблицы, с которой, я думаю, все хорошо знакомы. Я полагаю, что при получении нового бизнес-требования многие люди захотят скопировать код с аналогичными функциями, а затем преобразовать его в соответствии с текущим бизнесом на основе этого кода.Конечно, я делаю то же самое сейчас~

На самом деле идея вынести эту функцию в публичный компонент витала давно, недавно я начал делать базовые компоненты, поэтому начал с этого. Примерно через неделю написание основных компонентов было завершено.

Посмотреть базовую поддержкуAPI функциональной точки.

Основная идея состоит в том, чтобы сгенерировать некоторую абстрактную конфигурацию через json, а затем, наконец, сгенерировать страницу, проанализировав абстрактную конфигурацию json + рендерер. Конфигурация json покрывает основные требования 80% текущих бизнес-сценариев, но масштабируемость очень низкая. Например, некоторые сложные бизнес-сценарии:表单的关联校验,数据关联显示,多级列表下钻и так далее. Хотя можно включить эти функции с помощью более сложной обработки, конечный компонент будет чрезвычайно большим и сложным в обслуживании.

Итак, могу ли я использовать какой-либо инструмент для создания соответствующего кода с помощью этих конфигураций json? Таким образом, проблемы, упомянутые выше, вообще не существуют, потому что это точно такой же код, который мы написали сами, а инструмент просто помогает нам завершить процесс инициализации. Поэтому позже я придумал множество способов, первоначально используяtemplate stringЭтот метод относительно прост и груб, и это не что иное, как вывод кода путем оценки вложенных переменных в строке. Однако при написании было обнаружено много проблем, таких как

  1. functionВывод (JSON.stringify будет игнорировать функцию)
  2. Как получить окончательный отрендеренный код узла после вложения нескольких уровней функций
  3. Как реализовать встроенные переменные, как генерировать дополнительные словарные запросы в 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, который работает в различных браузерах. Основная функция:

  1. Преобразование синтаксиса
  2. Функциональность полифилла отсутствует в среде
  3. преобразование исходного кода
  4. Посмотреть больше функций Babel

Understanding ASTs by Building Your Own Babel Plugin

Базовый процесс 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               // 主入口

Основной процесс:

  1. Указывает путь для создания кода.
  2. В соответствии с текущим путем конфигурации json в схеме вызовите метод генерации кода каждого модуля в каталоге genCode, чтобы получить соответствующий код.
  3. Запишите соответствующий файл по указанному пути.
  4. воплощать в жизньeslint ${filePath} --fixОтформатируйте сгенерированный код.
  5. Скопируйте зависимые файлы 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Сгенерировано, но не работает после конвертации*символ, после многократной проверки документа решения нет. Наконец, сгенерированный код может быть решен только с помощью регулярной замены. Если вы столкнулись с подобными проблемами, добро пожаловать на обсуждение ~

вопрос

  1. Используемые в настоящее время компоненты редактора: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]);

  1. Как сгенерированный код удаляет неиспользуемые зависимости? Использование eslint --fix не удаляет неиспользуемые определения переменных.
  2. Как изменить код после инициализации? Поскольку текущий метод завершает только процесс инициализации кода, нет решения для будущего процесса модификации.

Функциональный 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 Отображается в режиме простого запроса, если в данных есть значение, содержащее это поле и истинное, переключатель простой/сложной фильтрации включен

Ссылаться на