前言:
前端面试中,请你设计一个通用的Select组件,要求手撸代码或说一下设计思想?
我只会写常用的业务组件,最多再把数据抽象一下,其他的就不知如何下手了。
今天的文章我们就先了解一下组件的设计原则,懂理论才能更好的实践。
Основные принципы проектирования компонентов
компонентсложностьОсновным источником является их собственное состояние; сколько не зависит от состояния внешнего входа, который требуется для поддержания самого компонента.
При разработке компонентов наиболее важной задачей является разделение данных и пользовательского интерфейса.
В процессе разработки компонента всегда помните и учитывайте, соответствует ли он следующим принципам, которые могут помочь вам разработать более полный компонент общего назначения.
единственная ответственность
Соответствует ли ваш компонентРеализуйте только одну обязанность и только одну причину для изменения состояния?
Например, запрос на выборку и логика рендеринга должны быть разделены. Поскольку запрос на выборку вызывает повторную визуализацию компонента, стиль или формат данных во время визуализации также вызывают повторную визуализацию компонента.
Единая ответственность обеспечивает высочайшую степень детализации компонентов и облегчает повторное использование. Но слишком мелкозернистый иногда может привести к фрагментации компонентов.
Таким образом, компонент единой ответственности встроенмногоразовыйНа основе , для одноразовых компонентов с одной ответственностью мы можем использовать их только как внутренние компоненты независимых компонентов.
общность
Разработка компонентов должна служить бизнесу, а для лучшего повторного использования они должны быть отделены от бизнеса.
Следующий код реализует требования A: Реализация выбранных компонентов базы: меню: выберите раскрывающийся список, раздел меню над полем выбора заголовка, содержащий значение и стрелку.
<div className={dropdownClass}>
<div
className={`${baseClassName}-control ${disabledClass}`}
onMouseDown={this.handleMouseDown.bind(this)}
onTouchEnd={this.handleMouseDown.bind(this)}
>
{value}
<span className={`${baseClassName}-arrow`} />
</div>
{menu}
</div>
В настоящее время существует новое требование B, которое требует отображения заголовка поля выбора в виде изображения.
Хотя режим взаимодействия B точно такой же, как и у A, из-за огромной разницы в структуре DOM между ними мы не можем повторно использовать вышеупомянутый Select для его достижения. Вы можете только изменить исходный код или переписать компонент, соответствующий требованиям.
Таким образом, лучшая практика в разработке компонентов состоит в том, чтобыОтказаться от контроля над DOM, доступен толькоСамый простой DOM, логика взаимодействия, который передает структуру домо-разработчика.
Следующий код представляет собой DropDown компонента Antd.Вы можете видеть, что только самый простой DOM предоставляет несколько функций рендеринга и логику обработки.
return (
<Trigger
{...otherProps}
prefixCls={prefixCls}
ref="trigger"
popupClassName={overlayClassName}
popupStyle={overlayStyle}
builtinPlacements={placements}
action={trigger}
showAction={showAction}
hideAction={hideAction}
popupPlacement={placement}
popupAlign={align}
popupTransitionName={transitionName}
popupAnimation={animation}
popupVisible={this.state.visible}
afterPopupVisibleChange={this.afterVisibleChange}
popup={this.getMenuElement()}
onPopupVisibleChange={this.onVisibleChange}
getPopupContainer={getPopupContainer}
>
{children}
</Trigger>
);
- Когда компонент используется повторно, его обязанности также используются повторно, поэтому проще всего повторно использовать компоненты только с одной обязанностью.
- Когда компонент по ошибке имеет несколько обязанностей, это увеличивает накладные расходы на повторное использование.
- Старайтесь избегать дублирования кода, повторяйте код дважды или более и подумайте, можно ли его использовать повторно?
Хотя универсальность хороша, она отнимет у разработчиков много энергии, поэтому, прежде чем абстрагироваться от бизнес-компонентов, спросите себя:
* 存在代码重复吗?如果只使用一次,或者只是某个特定用例,可能嵌入组件中更好。
* 如果它只是几行代码,分隔它反而需要更多的代码,那是否可以直接嵌入组件中?
* 性能会收到影响吗?更改state/props会导致重新渲染,当发生这种情况时,你需要的是 只是重新去渲染经过diff之后得到的相关元素节点。在较大的、关联很紧密的组件中,你可能会发现状态更改会导致在不需要它的许多地方重新呈现,这时应用的性能就可能会开始受到影响。
* 你是否有一个明确的理由?分离代码我想要实现什么?更松散的耦合、可以被复用等,如果回答不了这个问题,那最好先不要从组件中抽离。
* 这些好处是否超过了成本?分离代码需要花费一定的时间和精力,我们要在业务中去衡量,有所取舍。
упаковка
Хорошая упаковка компонентов должнаСкрыть внутренние детали и смысл реализации, и пройтиpropsуправлять поведением и выводом.
Ограничение доступа к глобальным переменным: потому что они нарушают инкапсуляцию, создают непредсказуемое поведение и затрудняют тестирование. Вы можете использовать глобальные переменные в качестве свойств компонента вместо прямых ссылок.
комбинация
具有多个功能的组件,应该转换为多个小组件。
单一责任原则描述了如何将需求拆分为组件,封装描述了如何组织这些组件,组合描述了如何将整个系统粘合在一起。
Чистые компоненты и нечистые компоненты
非纯组件有显示的副作用,我们要尽量隔离非纯代码。
将全局变量作为props传递给组件,而非将其注入到组件的作用域中。
将网络请求和组件渲染分离,只将数据传递给组件,保证组件职责的单一性,也能将非纯代码从组件中隔离。
Тестируемый
测试不仅仅是自动检测错误,更是检测组件的逻辑。
如果一个组件测试不易于测试,很大可能是你的组件设计存在问题。
осмысленный
开发人员大部分时间都在阅读和理解代码,而不是实际编写代码。
有意义的函数、变量命名,可以让代码具有良好的可读性。
Лучшие практики проектирования компонентов
Диаграмма классов UML для компонентов
Архитектура внешнего компонента на самом деле представляет собой древовидную диаграмму.Когда мы проектируем компонент, рекомендуется использовать форму диаграммы классов UML для преобразованияСтруктура компонента, состояние потока данных, функция обработкичетко обозначены. Сначала придумайте детали компонентов, а затем напишите код, чтобы избежать повторения кода.
- Нарисуйте древовидную структуру компонентов на веб-странице в виде диаграммы классов UML.
- В каждой диаграмме классов укажите требуемое состояние, пропсы, методы,
Мы хотим реализовать компонент таблицы, который содержит количество строк (RowCount), заголовок и тело.
Данные источника данных таблицы и количество строк RowCount все поступают из реквизита, для функции сортировки внутри таблицы требуемое состояние sortPerperty и по возрастанию исходят из состояния;
Операции с таблицами включают onRowClick из реквизита, сортировку setSortProperty из состояния;
Диаграмма классов UML показана ниже, вы можете интуитивно понять структуру уровня пользовательского интерфейса компонента, поток данных и функции обработки, и вы больше не боитесь рефакторинга при написании кода ^_^
Разделение вспомогательного кода
Чтобы следующий коллега лучше понял код, мы иногда добавляем необходимые комментарии к основному коду, чтобы сделать код более понятным.
let params = {
pageNum: pageNum,
pageSize: pageSize,
status: 4, // 参照:ROBOT_STATUS, 0-新导入,1-审核中,2-审核通过,3-审核未通过,4-上架,5-下架
title: search,
queryType: 0 // 0--只查询列表, 1--查询申请状态
};
Код такой же, как и выше, при чтении кода вы сможете быстро понять смысл каждого поля, но вы будете лишний раз отвлекаться на чтение комментариев, а ваши идеи будут сбиты с толку, что будет прерывать логический анализ всей функции .
следовательно,Поддельные данные, нетехническая документация, код конфигурации, рекомендуется размещать его вне кода, а не в основном коде, это повлияет на работу пользователя.
Сплющенное состояние и реквизит
При передаче свойств компонентам рекомендуется использовать более плоские свойства вместо вложенных объектов или массивов.
<DetailModal
{...modalData}
visible={showModal}
tagType={ROBOT_TYPE}
sceneList={sceneList}
handleCloseModal={() => this.handleCloseModal()} />
- Как показано выше, передаваемая структура данных является модальдата, но пользователь не знает, какие атрибуты содержали в ModalData, и могут быть избыточные свойства, которые должны быть максимально наилучшими.Избегайте перехода к компонентам, которые не нужныАтрибуты.
- В React, если вы хотите изменить объекты и массивы, вы должны создать копию; страница может быть перерисована из-за поверхностного копирования.
Ссылка на справочную статью:
1.Принципы разработки интерфейсных компонентов
2.7 рекомендаций по проектированию компонентов Solid React