Обзор общего ядра
Что это
Заголовок раздела «Что это»@lh-ui/share/core/shared — общий алгоритмический слой всей семьи lh-ui: 12 не имеющих зависимостей TypeScript-модулей, которые сводят в единую реализацию логику, нужную каждому компоненту, но которую каждый порт легко пишет заново (и обязательно с расхождениями в деталях):
позиционирование плавающих слоёв, виртуальная прокрутка, клавиатурная навигация, ловушка фокуса, построение aria-атрибутов, примитивы дат, санитизация чисел, проброс стилей… Эти алгоритмы не принадлежат ни одному фреймворку: на входе обычные данные, на выходе обычные данные, никакого реактивного состояния и никакого глобального состояния DOM (несколько функций читают DOM, но за SSR-предохранителями).
Один и тот же компонент в lh-ui-plus, lh-ui-vue2, lh-ui-react и lh-ui-uniapp использует одни и те же computePosition и getNextListIndex. Исправили один баг — исправлено сразу на четырёх концах; добавили граничный случай — он сразу появился на всех четырёх.
12 подмодулей
Заголовок раздела «12 подмодулей»| Модуль | Ответственность | Ключевые экспорты |
|---|---|---|
sx |
props sx → инлайн-стили с фиксированным приоритетом «одна сторона > пара осей > сокращение» | sxToStyle / mergeSx / LhSxProps |
popper |
Движок позиционирования плавающих слоёв: 12 размещений, переворот по главной оси, кламп по поперечной оси, смещение стрелки | computePosition / parsePlacement / LhPlacement |
virtual |
Диапазон виртуальной прокрутки фиксированной высоты (virtual-list и table разделяют один алгоритм) | computeRange / computeWindow / LH_VIRTUAL_OVERSCAN |
keyboard |
Клавиатурное взаимодействие: определение клавиш, навигация по списку / сетке / range, type-ahead | getNextListIndex / getNextGridCell / LH_KEYS |
focus-trap |
Управление фокусом: перечисление фокусируемых элементов, ловушка фокуса Tab, реестр владельцев inert | trapTabKey / setInertOutside / focusFirst |
a11y |
Атомарные построители aria-атрибутов (единая семантика «пропускать при ложном значении») | getPopupTriggerAria / ariaExpanded / getDecorativeAria |
date |
Примитивы дат: количество дней в месяце, сравнение, определение заблокированных, форматирование токенами фиксированной ширины | formatDate / parseDate / isDateDisabled |
format |
Интерполяция шаблонов текстов (плейсхолдеры {key}) |
formatMessage |
delay |
Нормализация задержки показа/скрытия (общая для tooltip / popover / popconfirm) | resolveDelay |
pt |
Слияние пробрасываемых props PT (поверхностное слияние class / style / attrs) | mergePt / LhPtAttrs |
sanitize |
Санитизация чисел: NaN / Infinity перехватываются в одной точке входа | sanitizeNumber / clamp / safePrecision |
types |
Общие union-типы: размер / язык / тип / позиция | LhComponentSize / LhComponentType |
Сводный вход @lh-ui/share/core/shared реэкспортирует все 12 модулей через export *; можно также импортировать модуль точечно — @lh-ui/share/core/shared/popper, — чтобы сборщик унёс только то, что используется.
Философия дизайна
Заголовок раздела «Философия дизайна»Чистые функции, состояние живёт в UI-слое
Заголовок раздела «Чистые функции, состояние живёт в UI-слое»Функции слоя core не хранят состояние компонентов. Возьмём popper: computePosition(trigger, popper, viewport, options) принимает три обычных rect и возвращает { top, left, placement, arrowOffset } — «где сейчас триггер, насколько велик плавающий слой, насколько велик вьюпорт» — всё это UI-слой измеряет в момент вызова и передаёт. Это означает:
- Одна и та же функция может работать и внутри
watchEffectво Vue3, и внутриuseLayoutEffectв React, и внутриonPageScrollв uniapp. - Для unit-тестов не нужно рендерить компоненты: множество случаев в
tests/unit/— это «создать набор rect, проверить вывод», и все четыре порта разделяют одни и те же результаты тестов (красная линия 3: unit-тесты core существуют только в этом репозитории и в единственном экземпляре).
SSR-предохранители как контракт по умолчанию
Заголовок раздела «SSR-предохранители как контракт по умолчанию»Несколько функций обязаны читать DOM (getWindowViewport, getFocusableElements, saveActiveElement). Все они в точке входа проверяют typeof document === 'undefined' / typeof window === 'undefined' и возвращают безопасное пустое значение ([] / null / rect нулевого размера), чтобы серверный рендеринг в Nuxt / Next / uniapp не падал. В этой документации каждая функция помечена как «SSR-безопасная» или с указанием конкретного поведения предохранителя.
aria-соглашение «пропускать при ложном значении»
Заголовок раздела «aria-соглашение «пропускать при ложном значении»»Все построители модуля a11y следуют одному соглашению: булевы aria-атрибуты при ложном значении возвращают undefined, чтобы фреймворк не отрендерил атрибут (aria-required: false как явное отрицание в большинстве случаев — шум). Исключения — атрибуты, семантически требующие явного объявления: aria-expanded: false и aria-invalid: false выводятся как есть. Подробнее см. документацию a11y.
Санитизация на входе, без защитного кода downstream
Заголовок раздела «Санитизация на входе, без защитного кода downstream»Существование sanitize позволяет остальным функциям core не писать проверку Number.isFinite повсюду. Числа, приходящие извне — номер страницы, интервал, шаг, точность, — перед попаданием в алгоритм проходят через sanitizeNumber, а NaN / Infinity заменяются безопасным значением по умолчанию. Это компромисс «перехватили в одном месте — чисто везде»: вызывающая сторона либо сама гарантирует конечность и использует clamp напрямую, либо явно доверяет санитизации на входе.
Межмодульные зависимости минимальны
Заголовок раздела «Межмодульные зависимости минимальны»Их всего три:
keyboard←sanitize.clamp(кламп навигационных границ)popper←sanitize.clamp+sanitize.sanitizeNumber(кламп и санитизация параметров)shared/types←constants.LH_SIZE_CONST/LH_LANGUAGE_CONST/LH_TYPES_CONST(import type; union-типы выводятся из констант)
Кольцевых зависимостей нет, обратных зависимостей от locales или UI-слоя тоже. Существование модуля format — прямой продукт этой линии: интерполяции вроде '{n} 星' в rate или '共 {total} 条' в pagination раньше требовали обращения к синглтону locales, а после очищения до чистой строковой функции core-компоненты больше не зависят от модульных побочных эффектов locales.
Попробовать
Заголовок раздела «Попробовать»@lh-ui/share/core/sanitize → clamp( )(填好参数后点运行)Внизу страницы документации каждого подмодуля есть такой блок-песочница: заполните параметры, запустите прямо в браузере и посмотрите результат.
Copyright © 2024-2026 FengMo WenXue