В последнее время мы работаем над собственной библиотекой компонентов в проекте.Что касается формы, то как реализовать более простую схему формы - это проблема, которую мы обсуждали.В предыдущих проектах мы использовалиant-designФорма Form также очень проста в использовании, и мы надеемся сделать более лаконичное решение.
Решения, связанные с формой, перечислены ниже,ReactКолесо сообщества слишком много, чтобы представить:
- Решения Ice Form - Ice FormBinder
- React реализует очень лаконичный компонент формы.
- NoForm — лучшее решение для форм
- Высокопроизводительное решение для форм для сложных сценариев - UForm
- redux-form
- final-form
- Form Form Component Design Road — Высокая простота использования — Fusion Form
- ......
Приведенная выше схема формы в основном фокусируется на следующих моментах:
- Легче собирать данные без рукописного ввода
valueа такжеonChange, некоторые формы добавляют функции (ant-design) или контейнер (FormBinder,Fusionд.), зарегистрироваться для дочерних компонентовvalue,onChange, некоторые на заказFeildкомпоненты (UForm), внутренняя логика, связанная с обработкой - Более эффективный рендеринг, например распределенное управление состоянием поля.
- Простота, снижение затрат на обучение
- Динамический рендеринг формы
О сборе данных и рендеринге
Для сбора данных формы вы можете обратиться к двусторонней привязке данных.Ниже приводится обсуждение двусторонней привязки данных:
И статья о реализации двухсторонней привязки данных:
- Реагируйте на двустороннее связывание сахара с помощью плагина Babel
- Научите, как добавить двустороннюю привязку данных в React (1)
- Научите, как добавить двустороннюю привязку данных в React (2)
Один — сбор данных, другой — рендеринг, то есть так называемая двусторонняя привязка данных.Подводя итог, есть три способа:
- Вы можете выполнять преобразование кода во время компиляции, внедрять операторы присваивания и методы мониторинга данных компонентов.Это выглядит высоким и должно быть написано самостоятельно.
Babelплагин - Измените виртуальный DOM во время выполнения, например.
ant-design,iceПодождите, это действительно полезно, перечисленные выше статьи можно изучать, это очень содержательно - почерк
valueа такжеonChange, если только в вашей системе нет только одной формы. . .
Сначала поставьте цель
Увидев реализацию больших парней, мы тоже хотим построить колесо.Надеемся, что оно может быть более лаконичным и сделать форму более приятной для написания.Когда в системе много форм, их приходится связывать вручную.valueа такжеonChangeКонечно нет, даже еслиant-design,iceи т. д., и дополнительные функции или контейнеры, поэтому цель состоит в том, чтобы выглядеть так:
import {Form,Input} form 'form';
export default class FormDemo extends Component<any, any> {
public state = {
value: {
name: '',
school: '',
},
}
public onFormChange = (value) => {
console.log(value);
this.setState({
value,
});
}
public onFormSubmit = () => {
// console.log('submit')
}
public render() {
const me = this;
const {
value,
} = me.state;
return (
<Form
value={value}
enableDomCache={false}
onChange={me.onFormChange}
onSubmit={me.onFormSubmit}
>
<div className="container">
<input
className="biz-input"
data-name="name"
data-rules={[{ max: 10, message: '最大长度10' }]}
type="text"
/>
<Input
data-name="school"
data-rules={[{ max: 10, message: '最大长度10' }]}
type="text"
/>
<Button type="primary" htmlType="submit">提交</Button>
</div>
</Form>
)
}
}
- Простой, близкий к оригиналу, низкая стоимость обучения
- Компоненты совместимы со всеми реализациями
value,onChangeкомпоненты, такие какant-designкомпонент формы - Проверка формы, следите
ant-designдизайн, использованиеasync-validatorбиблиотека, чтобы сделать
Видно, что мыant-designПоклонникам и откровенно говоря, планы больших парней достаточно просты.ant-designпионер, преемникIce , FusionПодождите много тестовant-design, стараясь давать более лаконичные решения, они действительно очень лаконичны, особенноFusionизFieldкомпоненты, ощущение блеска,UFormиспользовать что-то вродеJSON Schema(JSchema)синтаксис для записи формы,Uformа такжеfinal-formДелает упор на распределенное управление полями и высокую производительность.Однако эти две схемы имеют определенную стоимость обучения, а схема реализации, естественно, сложна.
Однако, когда я упомянул нашу реализацию, все, наверное, пожаловались бы, потому что наша реализация была слишком простой (закрывая нам лица), и она была настолько простой, что мы сомневались в жизни.
выполнить
Для достижения вышеуказанных целей очевидно, что список статей в начале статьи уже отработан, а код вводится при компиляции, но приходится добавлять новый.BabelПлагин, я не знаю, нравится он вам или нет.
Наша реализация заключается в использовании运行时修改虚拟DOMДа, это делается не во время компиляции, то есть во время выполнения, однако это не добавит в компонент дополнительных функций или контейнеров, просто используйтеFormКонтейнер реализован, об этом наверняка подумали все, обязательно ли проходить все дочерние узлы? Будет ли это иметь дополнительные накладные расходы на производительность?
Сначала реализуйте, а потом оптимизируйте.
Во-первых, необходимо обойти всех детей虚拟DOMУзел, сначала глубина, определите, имеет ли узелdata-nameилиnameатрибут, если есть, для прикрепления к этому компонентуvalueа такжеonChangeсвойства, такие какcheckbox, radio, selectи другие компоненты, специальная обработка.
Основной код значения привязки и onChange (с удалениями) выглядит следующим образом:
public bindEvent(value, childList) {
const me = this;
if (!childList || React.Children.count(childList) === 0) {
return;
}
React.Children.forEach(childList, (child) => {
if (!child.props) {
return;
}
const { children, onChange } = child.props;
const bind = child.props['data-name'];
const rules = child.props['data-rules'];
// 分析节点类型,获取对应的属性名是value,还是checked等
const valuePropName = me.getValuePropName(child);
if (bind) {
child.props[valuePropName] = value[bind];
if (!onChange) {
child.props.onChange = me.onFieldChange.bind(me, bind, valuePropName);
}
}
me.bindEvent(value, children);
});
}
Код для onFieldChange:
public onFieldChange(fieldName, valuePropName, e) {
const me = this;
const {
onChange = () => null,
onFieldChange = () => null,
} = me.props;
let value;
if (e.target) {
value = e.target[valuePropName];
} else {
value = e;
}
me.updateValue(fieldName, value, () => {
onFieldChange(e);
const allValues = me.state.formData.value;
onChange(allValues);
})
}
Даже если приведенный выше код достигает нашей цели, нам не нужно привязывать его вручнуюvalueа такжеonChange.
Демо:
Следующим шагом является реализация проверки формы, проверка формы или продолжение использованияant-designреализация, использованиеasync-validatorЭту библиотеку делать, настраивать путь иant-designэто то же самое. Для отображения сообщения об ошибке проверки добавлен контейнер FormItem, и метод использования также близок кant-design.
FormItemРеализация использует контекстный API React, и вы можете просмотреть исходный код реализации для получения подробной информации, поскольку это не является предметом этой статьи, поэтому я не буду говорить об этом.
а такжеant-designто же самое, пока это реализованоvalue,onChangeЗдесь можно использовать компоненты интерфейса, не ограничиваясь нативными компонентами HTML.
Сомнения в производительности
Вышеприведенный код достигает того, что мы хотим, но все еще есть сомнения: этот глубокий обход дочерних узлов каждый раз при рендеринге не будет ли проблем с производительностью?
ответ:影响微乎其微
Прошел испытание,1000Элементы управления формой внутри него разницы не чувствуют.1000пара подкомпонентовReactДругими словами, алгоритм diff также очень дорог.
Однако для повышения производительности мы провели оптимизацию и добавили虚拟 DOM 缓存.
Если мы кешируем созданный виртуальный DOM после первого рендеринга, второй рендеринг не нужно создавать заново, а также нет необходимости глубокого обхода узлов для добавленияvalueа такжеonChangeОн был обновленvalue, необходимо получитьdata-nameСсылка на узел, размещающий компонент какdata-nameзначениеkeyПоместите его в объект и передайте при обновленииdata-nameзначение, чтобы получить этот компонент, непосредственно обновите виртуальный компонент этого компонентаDOMАтрибуты можно получить напрямуюDOMОбновление цитированияDOM, что выглядит оченьJQueryБар?
Благодаря приведенной выше оптимизации производительность может быть удвоена.
но,如果表单内组件有动态显示、隐藏的话,就不能用虚拟DOM缓存, поэтому мы предоставляем свойствоenableDomCache, это может быть логическое значение или функция, параметр — это предыдущее значение формы, и пользователь сравнивает текущее значение с предыдущим значением, чтобы определить, следует ли использовать кеш для следующего рендеринга. Однако его можно использовать только при возникновении проблем с производительностью.В большинстве случаев проблем с производительностью нет.enableDomCacheПо умолчанию установлено значениеfalse,
Пример:
import {Form} form 'form';
export default class FormDemo extends Component<any, any> {
state = {
value: {
name: '',
school: '',
},
}
onFormChange = (value) => {
this.setState({
value,
});
}
onFormSubmit = () => {
// console.log('submit')
}
enableDomCache=(preValue)=>{
const me=this;
const {
value,
} = me.state;
if(preValue.showSchool!==value.showSchool){
return false;
}
return true;
}
render(){
const me=this;
const {
value,
} = me.state;
return (
<Form
value={value}
enableDomCache={me.enableDomCache}
onChange={me.onFormChange}
onSubmit={me.onFormSubmit}
>
<input
data-name={`name`}
data-rules={[ { max: 3, message: '最大长度3', } ]}
type="text"
/>
{
value.showSchool&&(
<input
data-name={`school`}
data-rules={[ { max: 3, message: '最大长度3', } ]}
type="text"
/>
)
}
</Form>
)
}
}
Мысли о распределенном управлении полями
Если поля формы каждый раз модифицировать, то вся форма будет перерисовываться, что не идеально, поэтому есть идея распределенного управления полями.
Попробуйте добавить формуreduxизstore, каждый компонент элемента формы подписываетсяstore, поддерживать собственное состояние данных, элементы формы не влияют друг на друга, поэтому поля формы распределены,storeПоследние данные формы сохраняются.
Однако в большинстве случаев даже после повторного рендеринга пользователь не заметит разницы,ant-designЭто повторный рендеринг, повторный рендеринг здесь является повторным рендерингомrenderСоздать виртуальныйDOM,фактическиReactпровестиdiffПосле этого настоящий DOM полностью не визуализируется.
Конечно, в погоне за совершенством, избегатьReactпровестиdiff, это лучше всего, поэтому для тяжелых компонентов внутри форм рассмотрите возможность использованияshouldComponentUpdateДля управления обновлениями используетсяReduxСтуденты знают, чтоconnectВнутри компонента более высокого порядка выполняется сравнение свойств, чтобы контролировать, обновляется ли компонент.
Еще момент, влияние управляемых компонентов и неуправляемых компонентов, если сама форма является управляемым компонентом, то ее свойства меняются, это обязательно приведет к собственному перерендерингу расчета, поэтому для лучшей производительности лучше всего использовать не- Режим управляемого компонента зависит от конкретных потребностей, поскольку в большинстве случаев состояние будет выбирать глобальное состояние, а неуправляемый компонент не будет обновляться из-за изменений внешнего состояния, поэтому может возникнуть несогласованное состояние пользовательского интерфейса и глобальное состояние. Если изменение данных формы контролируется только самой формой, вы можете уверенно использовать неуправляемый режим.
Добавки, как контролируемые, так и неконтролируемые, могут быть использованыshouldComponentUpdateОптимизируйте сам компонент.
О вложенности форм
В обсуждении предыдущей статьи, видя потребность пользователя во вложенности форм, об этом нетрудно подумать, если сама форма соответствуетvalue onChangeинтерфейс, то формы также могут вкладывать формы, например:
import {Form,Input} form 'form';
export default class FormDemo extends Component {
render(){
const me=this;
const {
value,
} = me.state;
return (
<Form value={value} onChange={me.onFormChange} onSubmit={me.onFormSubmit} >
<input data-name="name" type="text" />
<Input data-name="school" type="text" />
<Form name="children1">
<input data-name="name" type="text" />
<Input data-name="school" type="text" />
<Form name="children2">
<input data-name="name" type="text" />
<Input data-name="school" type="text" />
<Form name="children3">
<input data-name="name" type="text" />
<Input data-name="school" type="text" />
</Form>
<Form name="children4">
<input data-name="name" type="text" />
<Input data-name="school" type="text" />
</Form>
</Form>
</Form>
</Form>
)
}
}
Демо:
Несмотря на то, что реализована вложенность форм, эта реализация проблематична, последует изменение данных в подчиненных формах.onChangeМетод передается уровень за уровнем, при большом объеме данных и глубоком уровне вложенности будут проблемы с производительностью.
Демонстрация изменения данных вложенной формы:
Лучше всего походить на распределенное управление полями.Каждая форма отвечает только за свою отрисовку и не будет вызывать повторную отрисовку других форм.Для повышения производительности мы оптимизировали и предоставилиFormGroupконтейнер, этот контейнер можно пройтиFormузел, сборкаFormотношение ссылки узла, для каждогоFormсоздать уникальныйID, положить всеFormГосударство объединяетFormGroupУправление состоянием эквивалентно выравниванию, а не исходному дочернему элементу.FormизValueУправляется родителем.
После выравнивания состояния изменение каждой формы вызовет повторную визуализацию только самой себя, не затрагивая другие формы.
Демо:
Однако приведенная выше оптимизация ограничена неконтролируемыми состояниями, поскольку в контролируемых состояниях или по внешним атрибутамvalueДатьFormGroup, а внутреннийvalueи переданный атрибутvalueСтруктура противоречива, одна плоская структура, а другая древовидная.Условий перехода от древовидной структуры к плоской структуре недостаточно, так как вложенная структура формы не известна, поэтомуvalueконвертацию сделать нельзя.
Короче говоря, простая древовидная структура может использоваться безFormGroup. Комплекс можно считатьFormGroup, и установитеdefaultValueвместоvalue, чтобы использовать неуправляемый режим.
наконец
В этой статье делается попытка построить более краткую схему формы, используя深度遍历子节点метод присвоения значений дочерним компонентамvalueИ регистрацияonChangeСобытия, написание формы ближе к оригиналу, лаконичнее, а также использует缓存虚拟DOMМетод оптимизирует производительность глубокого обхода дочерних узлов, пытается добиться вложенности форм и используетFormGroupКонтейнер выполняет обновление и выравнивание данных, я не знаю, есть ли у вас выигрыш.
последний из последних
Это очень похоже на Vue, не так ли? , у React не так много инструкций, как у Vue, чтобы помочь, поэтому существует так много схем для упрощения формы, но в ретроспективе вышеописанный метод очень похож на парсинг и выполнение ast, хотя это нельзя сделать во время компиляции. , но во время выполнения. Кроме того, будет ли компонент шаблона для предоставления магических инструкций?
Затем напишите следующий код:
<Template>
<div v-if={true}>
{name}
</div>
<div v-show={true}>
<div/>
</div>
</Template>
Статья носит справочный характер и содержит идеи для решения задач.Комментарии приветствуются, спасибо!