Intro
В этой статье рассказывается, как с нуля разработать каскадный селектор с множественным выбором Antd. Первый взгляд на эффект:
Прочитав эту статью, вы сможете не только узнать, как добитьсяКаскадный множественный выборфункция, вы также можете узнать, кстати:
- Как опубликовать пакет NPM, написанный на Typescript
- Напишите базовые модульные тесты, выполните модульные тесты с помощью Github Action, покажите элегантный значок в файле Readme
(Если вы хорошо разбираетесь в вышеперечисленном, дальнейшее чтение может не сильно помочь 🧐)
задний план
Ant Design является открытым исходным кодом Alibaba, «второй по популярности инфраструктурой пользовательского интерфейса React в мире», нет необходимости представлять, любые студенты, использующие React для разработки и управления фоном, должны быть хорошо знакомы с ним.
Antd предоставляет множество отличных компонентов, Button/DatePicker/Form/Cascader/Tree/Notifaction/Modal, их слишком много, эти компоненты образуют полную экологию компонентов, что значительно повышает эффективность ежедневной разработки обычных разработчиков.
Однако некоторые специальные сценарии и специальные требования могут не поддерживаться компонентами Antd, и их поддержка не планируется. Например, каскадный множественный выбор, упомянутый в этой статье, можно найти на github.issues, Ответ разработчика Antd заключается в том, что он не поддерживается или рекомендуется использовать TreeSelect.
(Наклейки не неуважительны, большое спасибо разработчикам Antd за работу)
Но как рядовой разработчик, такой сложный компонент, как Tree Select, не может быть реализован большинством людей за неделю (здесь имеются в виду все возможности TreeSelect). Более того, в погоне за гибкой поставкой компания, работающая методом проб и ошибок, не может дать вам столько времени на внедрение общего компонента, который не дает немедленных преимуществ.
Вы, должно быть, сталкивались с подобным сценарием, когда сталкивались с жесткими и неопровержимыми потребностями продукта, а у сообщества нет подходящих компонентов с открытым исходным кодом, поскольку «отличный пользователь библиотеки с открытым исходным кодом» немного скромен и беспомощен😿.
В то же время существует несколько обучающих статей, связанных с реализацией компонентов, поэтому в этой статье будет рассказано, как реализовать каскадный селектор с множественным выбором с нуля.
нуждается в понимании
Чтобы дать читателям глубокое понимание требований, краткое описание (которое можно найти вSandboxПоднимите немного руки):
- Щелкните поле ввода, чтобы отобразить всплывающее окно над или под полем ввода, и щелкните другую область за пределами всплывающего окна, чтобы закрыть всплывающее окно.
- Щелкните текст, чтобы развернуть следующее меню, щелкните флажок, чтобы переключить выбранное состояние.
- Флажок имеет три состояния: выбрано (отмечено), частично выбрано (неопределенно), не отмечено (не отмечено).
- Поддерживает операции Cancel и Confirm, Cancel закрывает всплывающее окно, Confirm подтверждает выбор.
- После отправки выбора значение родительского узла отображается в поле ввода, что является стратегией TreeSelect.SHOW_PARENT в Antd.
- Нажмите x рядом с выбранным элементом, чтобы удалить выбранный элемент.
дизайн компонентов
Прежде чем приступить к написанию кода компонента, можно подумать о дополнительной работе, которую может принести неважная функциональная логика, какое состояние имеет компонент и какие параметры он может поддерживать. С таким проектным документом идеи будут понятнее при последующем кодировании.
до съезда
Чтобы уменьшить сложность, некоторые предварительные соглашения могут быть сделаны в соответствии с конкретными сценариями спроса:
- Поддерживается только множественный выбор. (А как насчет одиночного выбора? Конечно, используется каскадный компонент Antd)
- Ключи всех узлов представляют собой строки, уникальные во всем дереве, что удобно для суждения о том, выбран узел или нет.
- Значение компонента представляет собой тип массива строк. Он не поддерживает такие типы, как числа или символы. Строки подходят для большинства сценариев.
- Информация, которую должен предоставить узел, — это значение (обязательно, используется как уникальный маркер), заголовок (обязательно, отображение текста узла) и дочерние элементы (необязательно, массив дочерних узлов).
State
Каскадные компоненты с множественным выбором примерно нуждаются в следующих состояниях:
- Определяет, показывать ли каскадное меню (логическое значение)
- Контролировать, какие узлы (массив строк) выбраны в данный момент, в соответствии с черновиком проекта, onChange выполняется после нажатия кнопки «Подтвердить», текущее выбранное значение ≠= значение компонента)
- Управляет текущими расширенными данными уровня (массив массивов)
- Управлять путем текущего активного состояния, которое на рисунке выделено голубым цветом (массив).
Я забыл прочитать принцип проектирования состояния: «Состояние, которое может быть получено путем расчета, должно быть получено путем расчета». В компоненте каскадного множественного выбора, поскольку он наконец отображается со стратегией TreeSelect.SHOW_PARENT, то, что вы видите, это то, что вы получаете, и значение значения может быть постоянным.['深圳市', '荔湾区']
. Выбранное «состояние» города Гуанчжоу и провинции Гуандун рассчитывается расчетным путем.
Props
В качестве компонента формы есть только те параметры, которые можно легко перечислить.
Props | Type | Description |
---|---|---|
value | string[] | привязка данных |
data | TreeNode[] | данные узла {название: строка, значение: строка, дети?: TreeNode} |
allowClear | boolean | Можно ли быть ясным |
placeholder | string | Placeholder |
onChange | (newVal) => void | функция обратного вызова изменения значения |
className | boolean | Дополнительные имена классов CSS |
style | React.CSSProperties | дополнительный стиль |
disabled | boolean | следует ли отключить |
Значение поддержки и свойства onChange можно использовать в форме Antd.
Детали реализации
Стиль селектора
Первое, что нужно сделать, это поле ввода формы, стиль метки 🏷 выбранного узла, потому что он используется в качестве элемента управления формой Antd при доставке. Все должно соответствовать другим компонентам Select по стилю/поведению.
Вам необходимо учитывать блочную модель внешнего вида, стили состояния наведения/выделения, стили, когда поддерживается allowClear, стили для отключенных и т. д. Эту часть стиля можно реализовать самостоятельно или напрямую из компонента antd Select.
Однако эту ситуацию легко не заметить, и я обеспокоен тем, что некоторые сценарии не были учтены. Самый экономящий время способ — использовать имя класса Antd Selector, напрямую повторно использовать его стиль и притворяться селектором 🤓 .
Анимация всплывающих окон и расширений
Затем обработайте события селектора, чтобы управлять раскрытием и свертыванием меню. Этот шаг, вероятно, сделает следующие вещи
- Привяжите событие слушателя к селектору и отобразите меню, когда происходит щелчок.
- Чтобы не зависеть от контейнера и стиля, в котором находится Селектор, нужно использоватьPortalОтображает меню за пределами всего узла React Root Dom в виде
- Слушайте меню, нажмите внешнее событие и закройте меню, когда событие сработает.
- Должна быть анимация при отображении и сворачивании меню, чтобы поддерживать унифицированное взаимодействие с другими «всплывающими компонентами».
Нагрузка на минимальную реализацию невелика, но это не очень важно, когда мы хотим каскадировать несколько компонентов выбора, которые можно передать публичной библиотеке. используется в Antdrc-cascaderКак вы можете видеть в исходном коде компонента, его внешний слой обертываетrc-triggerКомпонент , нажмите, чтобы увидеть, конечно же, этот абстрактный компонент контейнера компонента помогает нам выполнять функции, упомянутые 👆.
return (
<Trigger
...
>
{React.cloneElement(children, {
onKeyDown: this.handleKeyDown,
tabIndex: disabled ? undefined : 0,
})}
</Trigger>
);
rc-xxx — это базовый компонент, предоставляемый командой разработчиков Antd Компоненты Antd, которые мы обычно используем, являются упаковкой компонентов rc-xxx.
Используя rc-trigger, можно очень быстро выполнить основные функции всплывающего окна.
import { Button, Empty } from 'antd'
import Trigger from 'rc-trigger'
const [popupVisible, setPopupVisible] = useState(false)
return (
<Trigger
action={!disabled ? ['click'] : []}
popup={
<Popup />
}
popupVisible={popupVisible}
onPopupVisibleChange={setPopupVisible}
popupStyle={{
position: 'absolute',
}}
// 对齐方式
popupAlign={{
points: ['tl', 'bl'],
offset: [0, 3]
}}
// 内置动画
popupTransitionName="slide-up"
>
<Selector
{...props}
/>
</Trigger>
)
rc-trigger не только предоставляет всплывающее окно, выравнивание, триггер клика и даже встроенную анимацию, просто нужно установитьpopupTransitionName="slide-up"
Вы можете получить ту же анимацию расширения, что и другие компоненты Select.
Посмотрите, как компонент rc-trigger реализован внутриИнтерпретация этого исходного кода
Checkbox 🌲 Состояние Связывания
Затем введите выделение этого компонента, поддержание статуса Checkbox 🌲.
В следующем примере выбран Шэньчжэнь, после чего все дочерние узлы Шэньчжэня отображаются как выбранные (но не отражаются в значении). Город Гуанчжоу выбран частично, поскольку значение содержит некоторые районы города Гуанчжоу, и то же самое верно для полувыбранного штата провинции Гуандун.
структура
Учитывая, что древовидная структура требует частых операций обхода вверх и вниз, нам может понадобиться двунаправленная древовидная структура с несколькими разветвлениями 🌲. Соединение от родителя к дочернему элементу уже отражено в поле дочерних элементов, и вам необходимо добавить родительский атрибут к каждому дочернему узлу, чтобы указать на родительский узел.
В языках, использующих сборку мусора с подсчетом ссылок, циклические ссылки подвержены утечкам памяти. Современный механизм сборки мусора Javascript — это метка-очистка.
Чтобы облегчить оценку для получения соответствующего узла через значение, после связывания родителя → потомка древовидная структура выравнивается для получения одномерного массива Код выглядит следующим образом:
export function flattenTree(root: TreeNode[]): TreeNode[] {
const res: TreeNode[] = []
function dfs(nodes: TreeNode[], parent: TreeNode | null = null) {
// ...
for (let i = 0; i < nodes.length; i++) {
const node = nodes[i]
const { children } = node
const newNode = { ...node, parent }
res.push(newNode)
if (children) { dfs(children, newNode) }
}
// ...
}
dfs(root)
return res
}
Check
Когда пользователь щелкает определенный уровень флажка, необходимо рекурсивно пройти все прямые родительские узлы вверх, чтобы определить, все ли его дочерние узлы находятся в отмеченном состоянии. Если это так, переключите значение на значение родительского узла и удалите все значения его дочерних узлов.
当前 value
['深圳市', '天河区', '荔湾区']
点击勾选”萝岗区” 之后,需要加到 value 中
['深圳市', '天河区', '荔湾区', '萝岗区']
向上遍历,来到’广州市‘发现所有子节点都被选中了,删除所有子节点 value,加入自身 value。
['深圳市', '广州市']
继续往上判断,来到广东省也是所有子节点都被选中了,删除所有子节点 value,加入自身 value。
['广东省']
由于没有更上一层了,向上遍历中止
код показывает, как показано ниже:
// 状态提升
export function liftTreeState(
item: TreeNode,
curVal: ValueType[]
): ValueType[] {
const { value } = item
// 加入当前节点 value
const nextValue = curVal.concat(value)
let last = item
// eslint-disable-next-line no-constant-condition
while (true) {
// 如果父节点的所有子节点都已经 checked, 添加该节点 value,继续尝试提升
if (
last?.parent?.children!.every((child: TreeNode) => nextValue.includes(child.value))
) {
nextValue.push(last.parent.value)
last = last.parent
} else {
break
}
}
// 移除最后一个满足 checked 的父节点的所有子孙节点 value
return removeAllDescendanceValue(last, nextValue)
}
UnCheck
Когда пользователь нажимает «Снять отметку», логика в основном обратна операции «Проверить».
После снятия отметки «Район Ливан» пройдите от этого узла до родительского узла в текущем значении и временно сохраните этот путь parentPath['荔湾区', '广州市', '广东省']
.
Затем пройдите весь путь вниз от «Провинция Гуандун», если текущий узел напрямую отбрасывается на parentPath, если нет, его необходимо поместить в nextValue.
当前 value
['广东省', '湖南省']
广东省在 parentPath,需要丢弃。
['湖南省']
继续向下递归,深圳市不在 parentPath 上,需要加到 value 中,不在 parentPath 上,子节点可以不再遍历。
['湖南省', '深圳市']
广州市在 parentPath 上,不在 value 中,继续递归。
['湖南省', '深圳市']
荔湾区在 parentPath 上,不在 value 中,直接忽略,天河区和萝岗区不在 parentPath 上,需要加入到 value。
['湖南省', '深圳市', '天河区', '萝岗区']
没有更深的节点了,遍历中止
код показывает, как показано ниже:
// 状态下沉
export function sinkTreeState(root: TreeNode, value: ValueType[]): ValueType[] {
const parentValues: ValueType[] = []
const subTreeValues: ValueType[] = []
// 获取 parentPath
function getCheckedParent(
node: TreeNode | null | undefined
): TreeNode | null {
if (!node) {
return null
}
parentValues.push(node.value)
if (value.includes(node.value)) {
return node
}
return getCheckedParent(node.parent)
}
const checkedParent = getCheckedParent(root)
if (!checkedParent) {
return value
}
// 递归遍历所有子节点
function dfs(node: TreeNode) {
if (!node.children || node.value === root.value) {
return
}
node.children.forEach((item: TreeNode) => {
if (item.value !== root.value) {
if (parentValues.includes(item.value)) {
dfs(item)
} else {
subTreeValues.push(item.value)
}
}
})
}
dfs(checkedParent)
// 替换 checkedParent 下子树的值
const nextValue = removeAllDescendanceValue(checkedParent, value).filter(
(item) => item !== checkedParent.value
)
return Array.from(new Set(nextValue.concat(subTreeValues)))
}
Connected Checkbox
Как упоминалось ранее, мы сохраняем только значение Show_Parent, частично выбранное состояние и выбранное состояние дочернего узла вычисляются во время выполнения. Правила расчета следующие:
- Находится ли дочерний узел в проверенном состоянии ⇒ сам или его собственный проверенный
- Находится ли родительский узел в неопределенном состоянии ⇒ непроверенном состоянии, а некоторые дочерние узлы проверены
- Находится ли дочерний узел в непроверенном состоянии ⇒ состояние по умолчанию
export const ConnectedCheckbox = React.memo(
(props: Pick<MenuItemProps, 'node'>) => {
const { node } = props
const { value: containerValue, handleSelectChange } = MultiCascader.useContainer()
const handleChange = useCallback(
(event: CheckboxChangeEvent) => {
const { checked } = event.target
handleSelectChange(node, checked)
},
[node]
)
// 自己或父节点为 checked
const checked = useMemo(() => hasParentChecked(node, containerValue), [
containerValue,
node,
])
// 自己没有 checked,但是有子节点状态 checked
const indeterminate = useMemo(
() => !checked && hasChildChecked(node, containerValue),
[checked, containerValue, node]
)
return (
<Checkbox
onChange={handleChange}
checked={checked}
indeterminate={indeterminate}
/>
)
}
)
hasParentChecked
а такжеhasChildChecked
Метод заключается в простом перемещении вверх или вниз, код относительно прост и здесь не приводится.
Чтобы облегчить управление состоянием в компоненте (MultiCascader.useContainer в коде), я ввел библиотеку unstated-next, которая по-прежнему использует Hooks + React Context, но код более лаконичен и поддержка Typescript дружелюбна.
Выбрать все функции
С приведенной выше каскадной связью Checkbox логика продолжения реализации [Select All] становится очень простой. Просто добавьте еще один поверх исходных данных.All
Узел, привяжите узел к флажку «Все» в нижнем колонтитуле. Когда все узлы первого уровня выбраны, значение автоматически поднимается выше родительского.
const flattenData = useMemo(() => {
// 如果需要支持全选,在原来的 data 之上添加一个 TreeNode 节点
if (selectAll) {
return flattenTree([
{
title: 'All',
value: All,
parent: null,
children: data,
},
])
}
return flattenTree(data || [])
}, [data, selectAll])
// 如果需要支持全选,在 Footer 渲染 ConnectedCheckbox,赋予 All 节点
{selectAll ? (
<div className={`${prefix}-popup-all`}>
<ConnectedCheckbox node={flattenData[0]} />
{selectAllText}
</div>
) : null}
Каскадное меню
По умолчанию меню развернуто, и необходимо указать все родительские узлы первого уровня. Если весь выбор поддерживается, напрямую возьмите первых дочерних элементов flattenData, в противном случае пройдите flattenData, чтобы найти все дочерние элементы без родителя.
const [menuData, setMenuData] = useState([
selectAll
? flattenData[0].children!
: flattenData.filter((item) => !item.parent),
])
Поскольку каждый неконечный узел сохраняет данные дочерних элементов, каскадное меню фактически добавляет своих дочерних элементов в массив, который поддерживает каскадное состояние после щелчка родительского узла.
const addMenu = useCallback((menu: TreeNode[], index: number) => {
if (menu && menu.length) {
setMenuData((prevMenuData) => [...prevMenuData.slice(0, index), menu])
} else {
// 如果 children 也要更新 menu
// 比如当前展开了三级菜单,点击了另一个二级叶子节点
setMenuData((prevMenuData) => [...prevMenuData.slice(0, index)])
}
}, [])
На данный момент основная логика всего каскадного множественного выбора разработана, а остальная часть мелкозернистой логики здесь не расширяется.Заинтересованные учащиеся могутgithubПосмотрите на исходный код.
В следующем разделе описывается, как публиковать компоненты React, разработанные с помощью Typescript, в NPM.
Typescript NPM Package
Код наших компонентов написан на Typescript, но пользователи не обязательно используют Typescript и не обязательно компилируют файлы в node_modules, поэтому код, публикуемый в NPM, должен быть в форме JS.
В проектах Typescript сначала необходимо установить два пакета typescript и tslib.
$ yarn add typescript tslib -D
Измените package.json, чтобы добавить сценарий tsc и основные поля.
Основное поле — это файл ввода, который указывает, когда другие используют этот пакет. Исходная запись кода Typescript находится в src/index.tsx . Операция связывания выполняется в dev, поэтому ее можно сначала проверить в локальном проекте. tsc watch может немедленно перекомпилировать каждый раз, когда код изменяется. Роль prepublishOnly заключается в перекомпиляции Typescript каждый раз, когда он готов к публикации.
"main": "dist/index.js",
"scripts": {
"dev": "yarn link && yarn tsc --watch",
"tsc": "tsc",
"prepublishOnly": "rm -rf dist/ && npm run tsc"
},
Добавьте файл tsconfig.json в корневой каталог, укажите выходной путь, нужно ли генерировать файл объявления, выполнять jsx и т.д. Когда в поле объявления установлено значение true, Typescript автоматически сгенерирует файл d.ts во время компиляции. Предоставьте эти файлы объявлений, и вы сможете получить функцию предсказания кода в таких редакторах, как VSCode.
{
"compilerOptions": {
//...
"outDir": "dist",
// ...
"declaration": true,
// ...
"jsx": "react"
},
"include": ["./src/**/*"]
}
Как насчет Меньше файлов?
Файлы стилей в компоненте написаны с использованием меньшего количества файлов. Точно так же нельзя требовать от пользователей, которые используют этот пакет, использовать меньшее значение. Нам необходимо предоставить файл css (Typescript не может обрабатывать меньшее количество файлов).
// 在 less 文件中引入 antd 自带的文件,以便使用其提供的变量
@import '../node_modules/antd/es/style/themes/default.less';
@prefix: ~'antd-multi-cascader';
.@{prefix} {
text-align: left;
&-hidden {
display: none;
}
//...
}
установить меньше
$ yarn add -D less
Вернитесь к файлу package.json, добавьте скрипт lessc и укажите компилировать src/index.less в dist/index.less.—js
Параметр связан с тем, что имеется ссылка на файл dfault.less antd, который будет использовать функцию встроенного javascript.
"scripts": {
"compile": "yarn tsc && yarn lessc",
"tsc": "tsc",
"lessc": "lessc src/index.less dist/index.css --js",
"prepublishOnly": "yarn test && rm -rf dist/ && npm run compile"
},
После публикации в npm вы можете применять компоненты и файлы стилей к своему проекту. ✨
import MultiCascader from "antd-multi-cascader";
import "antd-multi-cascader/dist/index.css";
Модульные тесты и действия Github
Модульное тестирование — это недорогой, но эффективный способ обеспечения качества кода.Помимо помощи в выявлении проблем, из-за которых код на самом деле работает не так, как ожидалось, оно также является хорошим инструментом для устранения неполадок. В целом, чем выше покрытие модульным тестом, тем легче получить одобрение от других разработчиков.
Во внешнем мире мы можем писать модульные тесты для методов, модулей или даже компонентов. Для написания и запуска модульных тестов сначала требуется установить среду модульного тестирования, например, шутка Facebook.
yarn add -D jest @types/jest ts-jest
Добавьте файл jest.config.js в корневой каталог проекта, чтобы jest знал, как анализировать и сопоставлять файлы модульных тестов.
module.exports = {
preset: 'ts-jest',
testMatch: ['<rootDir>/src/**/__tests__/*.tsx'],
collectCoverageFrom: ['src/**/*.{ts,tsx}'],
}
Затем вы можете написать код модульного теста. Глобальный метод описания объявляет группу юнит-тестов, в этой группе можно определить хуки жизненного цикла юнит-тестов, до, после, перед каждым, после каждого и т. д., в этих хуках можно создавать данные для запуска юнит-тестов.
В методе it выполняется конкретный вариант использования и используется метод expect, предоставляемый jest.Этот метод принимает параметр и возвращает объект со многими методами утверждения. Вызов этих методов утверждения приводит к проверке ожидаемого выполнения. Например, следующий пример утверждаетhasChildChecked(flattenValue[0], ['1'])
Возвращаемое значение будет true. Учитывая входные данные, метод проверки допускает результат, и если вы случайно измените метод в следующий раз, модульный тест может немедленно сообщить вам об этом и вовремя исправить это, чтобы снежный ком не становился все больше и больше.
import { hasChildChecked } from '..'
describe('src/components/MultiCascader/utils.tsx', () => {
describe('hasChildChecked', () => {
let flattenValue: TreeNode[]
beforeEach(() => {
flattenValue = createFlattenTree()
})
it('should tell has child checked or not', () => {
expect(hasChildChecked(flattenValue[0], ['1'])).toEqual(true)
})
})
})
- В этом компоненте есть много чистых методов, связанных с операциями 🌲, которые очень полезны для написания модульных тестов. Рекомендуется, если вы хотите писать модульные тесты для компонентов React.testing-library.com/
После написания модульных тестов добавьте команду test в сценарии package.json и добавьте тестовую задачу перед prepublishOnly, чтобы модульные тесты можно было запускать каждый раз перед упаковкой.
"scripts": {
"test": "jest",
"prepublishOnly": "yarn test && rm -rf dist/ && npm run compile"
},
В процессе непрерывной интеграции мы можем использовать Github Actions, Gitlab CI, Travis CI и другие инструменты для запуска модульных тестов, lint, sonarqube и других задач, чтобы обеспечить качество отправки кода.
Использовать Github Actions легко, просто создайте файл .github/workflows/test.yml в корневом каталоге проекта. Скопируйте код и запустите службу узла для запуска модульных тестов каждый раз, когда вы отправляете код и pr.
name: Test
on: [push, pull_request]
jobs:
release:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [macos-10.14]
steps:
- uses: actions/checkout@v2
- name: Use Node.js
uses: actions/setup-node@v1
with:
node-version: '12.x'
- run: npm install
- run: npm run test -- --coverage
Чтобы получить значок с указанием покрытия?, войдите в систему.codecov.io/gh, выберите соответствующий репозиторий, заполните CODECOV_TOKEN, отображаемый на странице Настройки-Секреты проекта Github, а затем добавьте следующий код в файл test.yml.
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v1
with:
token: ${{ secrets.CODECOV_TOKEN }}
flags: unittests
name: codecov-umbrella
fail_ci_if_error: true
Затем вы можете пройтиhttps://img.shields.io/codecov/c/github/[user]/[project]/master.svg
Ссылка для получения значка друзей. 🎉
Эпилог
Я считаю, что после прочтения этой статьи вы также сможете разработать каскадный селектор с множественным выбором. Если у вас возникнут вопросы во время чтения, пожалуйста, оставьте сообщение для обсуждения.
В качестве минимальной реализации для удовлетворения требований к продукту содержание этой статьи предназначено только для справки, и его следует использовать с осторожностью в реальных проектах, поскольку все еще существуют по крайней мере следующие дефекты.
- Требования к производительности при большом объеме данных.Объем данных в проекте очень маленький, и я не особо уделял внимание вопросам производительности при кодировании. Когда объем данных большой, вам нужно перейти к виртуальному списку
- Поиск не поддерживается, содержимое меню загружается динамически, пользовательский рендеринг не поддерживается.
- Тщательно не тестировался, изменение параметров во время использования может привести к неожиданным результатам.
- Нет поддержки нажатий клавиш, веб-доступа и т. д.
Для реализации части привязки состояния Checkbox 🌲 обратитесь к другой замечательной библиотеке компонентов с открытым исходным кодом.rsuitejs - multi-cascaderисходный код. Из-за стиля, экологии и для того, чтобы гибко реагировать на потребности проекта, в конечном итоге он был не выбран, а реализован сам собой.
Наконец, весь код находится вgithubВы можете найти его на , если вы хотите узнать более подробную информацию, вы можете нажать здесь 👀 Добро пожаловать звезда
Студенты с такими же потребностями также могут попробовать его.Если у вас возникнут какие-либо проблемы во время использования, вы можете задать вопросы~
// npm
$ npm install antd-multi-cascader
// yarn
$ yarn add antd-multi-cascader