Говоря об опыте разработки библиотеки компонентов Vue UI

Vue.js

Компоненты пользовательского интерфейса представляют собой инкапсуляцию набора связанных взаимодействий и стилей, предоставляя простые методы вызова и интерфейсы, позволяющие разработчикам легко использовать функции, предоставляемые компонентами, для достижения бизнес-требований.

я вAdmin UIизVueБиблиотека компонентов пользовательского интерфейса (адрес GitHub:GitHub.com/B мальчик трепет Y/Ade…библиотека компонентов. Но даже если выReactпользователи , также могут ссылаться на опыт, приведенный в этой статье, потому что, если вы планируете писатьReactбиблиотеки компонентов пользовательского интерфейса, вам придется столкнуться почти с той же проблемой.

Эта статья также является содержанием технического обмена в моей компании. В основном я обсуждаю здесь только идеи и стараюсь не затрагивать конкретные реализации. И мои решения этих проблем не совсем разумны.Если есть какие-то ошибки или неточности, просьба их исправить.

1 Организуйте свой проект

Когда вы начинаете разрабатывать библиотеку компонентов, первое, что нужно сделать, это, вероятно, настроить проект, потому что онVueбиблиотека компонентов, вы, скорее всего, будете использовать ее официальную рекомендациюvue-cliинструмент для создания проекта.

1.1 Соответствующая файловая структура

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

Давайте назовем вашу библиотеку компонентов на данный моментadmin-ui, чтобы облегчить последующее написание

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

На данный момент ваша файловая структура выглядит так:

组件库文件夹和示例页面文件夹

1.2 Напишите или сгенерируйте документацию по использованию библиотеки компонентов

Вполне вероятно, что через какое-то время вы написали пробную страницу для каждого компонента, и некоторые из пробных страниц сами по себе уже потрясающие. Если в один прекрасный день основная разработка библиотеки компонентов будет завершена, эти экземпляры страниц будут бесполезны. Таким образом, вы можете улучшить эти примеры страниц, поместить функции и списки интерфейсов каждого компонента или даже использовать пример кода на их примерных страницах, а затем развернуть его на сервере для просмотра вашими пользователями в любое время. К счастью, они не пропали даром, все они превратились в документацию для библиотеки компонентов!

1.3 Управление документами разработки самой библиотеки компонентов

Тогда вы можете осознать, насколько слаб один человек, и вы приглашаете других разработчиков участвовать в вашем проекте, но даже если вы используетеvue-cliОн уже был включен при создании проектаESlint, но недостаточно полагаться на унификацию стиля кода для совместной разработки полной библиотеки пользовательского интерфейса несколькими людьми. Вам может понадобиться создать документ разработки, в котором вы опубликуете различные соглашения и проекты, а также другую информацию, которой необходимо поделиться. Итак, вы создали новый в корневом каталоге проекта.documentationкаталог и использоватьGitBookДокумент создается и одновременно синхронизируется с сервером GitBook, чтобы ваши друзья могли просматривать его в Интернете, даже если они не синхронизированы.

使用gitbook建立的组件开发者文档

1.4 Поддержка различных способов установки

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

Первое, что приходит на ум, это самый популярный способ установки:npm. Если вы публикуете библиотеку компонентов непосредственно вnpm, ваши пользователи смогут его пройти илиyarnОчень легко установить и обновить. Но в настоящее время ваша библиотека компонентов только начинает работать, и вы не хотите сразу открывать ее исходный код. Итак, вы обратились в компанию за построенным вашей компанией самостоятельноgitlabрепозиторий в , затем в той же папке, где находится ваша библиотека компонентовgit initинициализировалgitproject и синхронизируйте его с репозиторием, на который вы подали заявку. В это время коллеги по компании уже могут проходить

npm install admin-ui git+ssh://admin-ui-git-location.git --save

Пришло время установить вашу библиотеку компонентов.

Конечно, если компания уже развернула или вы можете развернуть частные репозитории npm в компании, наиболее рекомендуемой практикой является публикация непосредственно в частных репозиториях компании.

Тогда второй метод установки, о котором вы можете подумать, — это CDN. ваши пользователи, встроенные на их страницу

<script src="admin/ui/cdn/location"></script>

Чтобы использовать вашу библиотеку компонентов, необходимо упаковать вашу библиотеку компонентов. В этом сценарии вам необходимо упаковать библиотеку компонентов целиком.admin-ui.jsjs-файл, подобный этому. Мы продолжим обсуждение упаковки в следующем разделе.

Конечно, последний способ установки — использовать исходный код напрямую и поместить библиотеку компонентов непосредственно в исходный код проекта для справки.

1.5 Упаковка и публикация вашей библиотеки компонентов

Определив, какие методы установки необходимо поддерживать, вы, возможно, поняли, что теперь есть две части вашего проекта, которые необходимо упаковать и распространить:

  • ваша библиотека компонентов (в этом суть)
  • Ваше использование библиотеки компонентов документа (то есть самого проекта)

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

npm run build

Но ключевой проблемой является ваша библиотека компонентов admin-ui. В настоящее время он существует как часть исходного кода вашего проекта. Таким образом, вы должны начать думать о том, как упаковать эту часть кода отдельно.

Конечно, вы также можете не упаковывать свою библиотеку компонентов, а напрямую публиковать ее исходный код в виде пакета npm, но в этом случае пользователям необходимо полагаться на инструменты упаковки для упаковки вашего кода при его использовании. Для проектов, созданных с помощью таких инструментов, как vue-cli, код из папки node_modules не будет упакован по умолчанию.Пользователи должны изменить конфигурацию сборки, чтобы вручную указать расположение кода для упаковки, что очень неудобно.

Итак, вы смотрите наbuild.js,webpack.prod.conf.jsа такжеprod.env.jsв корневом каталоге проектаbuildа такжеconfigдобавлено в папкуpublish.js,webpack.publish.conf.jsа такжеpublish.env.jsфайл и ознакомился с документацией по веб-пакету, удалил некоторые ненужные функциональные конфигурации и настроил конфигурацию для упаковки вашей библиотеки компонентов.

Вы ожидаете, что упакованный код будет помещен в папку вашей библиотеки компонентов с именемdist, то исходные файлы вашей библиотеки компонентов необходимо переместить вsrcПод содержанием.

组件库源码与打包后的代码并存

Когда webpack упаковывает код, вам нужно указать файл входа.В это время вы обнаружите, что в самой вашей библиотеке компонентов нет файла экспорта.

1.6 Полная загрузка и загрузка по требованию

вы находитесь в библиотеке компонентовsrcСоздал новую папкуindex.jsфайл, который импортирует и экспортирует все компоненты.

import Button from './components/button'
import Icon from './components/icon'
// ...省略的代码...
export {
  Button,
  Icon
  // ...省略的代码...
}

На этом этапе вы можете просто спланировать файловую структуру самой библиотеки компонентов:

组件库本身文件结构

В этом формате вывода ваши пользователи могут

import { Button } from 'admin-ui'

чтобы получить компонент Button из библиотеки компонентов. Однако это только поддержка данного формата (это не загрузка по требованию), пользователям также необходимо иметь возможность выполнять полную загрузку, то есть вводить сразу все компоненты и регистрировать их все автоматически. Как и тыindex.jsУстановите все компоненты вadminUiобъект, а затем смонтировать на этом объектеinstall()метод поддержкиVue.use()и, наконец, вывести объект напрямую. теперь твойindex.jsЭто выглядит так:

import Button from './components/button'
import Icon from './components/icon'
// ...省略的代码...
export {
  Button,
  Icon
  // ...省略的代码...
}

const adminUi = {
  Button,
  Icon,
  // ...省略的代码...
}

adminUi.install = function (Vue, options = {}) {
  Vue.component('cu-button', Button)
  Vue.component('cu-icon', Icon)
  // ...省略的代码,你也可以用循环来写...
}
export default adminUi

install()В методе много чего можно сделать, кроме регистрации компонентов, вполне вероятно, что вы еще и монтируете в нем какие-то экземплярные методы

На этом этапе ваши пользователи могут

import adminUi from 'adminUi'
Vue.use(adminUi)

для полной загрузки.

Следующим шагом является загрузка по запросу. вы обнаружите, что если только через вашindex.jsВходной файл используется для загрузки определенного компонента.Хотя другие компоненты не вводятся пользователем, они все же компилируются в пользовательский код. Так что придется думать о новых способах. Поскольку его нельзя загрузить из одной точки входа, можно ли указать точку загрузки для каждого компонента? Вы хотите, чтобы ваши пользователи могли

import Button from 'admin-ui/button'

Загрузите один компонент таким образом, чтобы не было лишних компонентов. Таким образом, вы понимаете, что каждый компонент также должен быть упакован отдельно. Экспорт файлов для каждого компонента (вероятно, такжеindex.jsЗдесь следует понимать, что файловую структуру каждого компонента можно последовательно привести к пакету, упаковать каждый компонент в отдельный модуль, чтобы разместить его.distсерединаlibпод папку. В настоящее время поддерживается загрузка по запросу.

打包后的组件库的文件结构

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

Затем вы с удовольствием попытались упаковать, но с разочарованием обнаружили, что независимо от того, использовали ли вы экспортный файл самой библиотеки компонентов в качестве записи или когда вы упаковывали каждый компонент по отдельности, результат был почти один..jsфайл, также будет.cssдокумент. Независимо от того, загружаются ли ваши пользователи полностью или загружаются по запросу, когда вы вводите.jsСоответствующий файл также должен быть введен.cssдокумент. При полной загрузке, поскольку он загружается только один раз, это не имеет большого значения. Но если он загружается по запросу, это немного проблематично, потому что его нужно вводить несколько раз.

Отдельный файл css предназначен для повышения производительности, файл css может кэшироваться браузером, а самому компоненту не нужно генерировать css при рендеринге.

Есть два решения, одно из которых — рекомендовать пользователям использоватьbabel-plugin-component, другой заключается в том, что сам упакованный компонент больше не предоставляет файл css, но полностью загруженный файл css вводится глобально. Любой подход работает, но я использую последний.

Причин две: во-первых, стили компонентов невелики по размеру, и они контролируются в пределах 60 КБ после сжатия и упаковки (большинство из которых — шрифтовые коды стилей, а все стили компонентов не превышают 5 КБ). ; во-вторых, потому что с font-awesome, если каждый компонент вводит свой собственный стиль, компоненты, зависящие от font-awesome, будут иметь повторяющиеся стили.

2 Создайте систему тем

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

2.1 Определение функциональных границ предметной системы

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

  • Цвет (это главное)
  • тень
  • закругленные углы

Итак, давайте начнем с установки границ вашей системы тем для этих трех факторов.

2.2 Выберите правильную реализацию

Затем вы начинаете думать о возможных идеях реализации системы тем:

  • Замена строки в специальном формате
  • Предварительно скомпилированные файлы темы
  • класс стиля

Замена строки в специальном формате, несомненно, является самым простым.Когда вы сталкиваетесь со стилем, который должен контролироваться системой тем во время разработки, вы можете напрямую использовать строку специального формата в CSS и заменить ее во время выполнения. Например:

div {
  color: ?primary?;
}

Среда выполнения заменяется вашим скриптом на:

div {
  color: #00f;
}

Преимущество этого решения в том, что оно очень удобно в разработке, в принципе не влияет на опыт разработки и даже улучшает. Не такая уж большая проблема в традиционную эпоху jquery, но что касается проектов Vue, существует проблема «времени замены». Вы можете поместить все страницы на страницу после инициализации проекта<style>специальные символы в тегах заменяются, но при изменении страницы новый компонентstyleвставляется вheadВ середине тоже нужно снова произвести замену, а найти подходящий момент для этого сложно.

Предварительная компиляция файла темы в настоящее время является основным решением для реализации темы на рынке. То есть сама библиотека пользовательского интерфейса предоставляет инструменты для генерации файлов CSS разных тем, и заранее компилируется несколько наборов файлов разных стилей темы. Преимущество в том, что он прост и понятен, удобен и прост в использовании. Но недостатки тоже очевидны: замена тем во время выполнения становится очень грубой (крупнозернистой) — заменять можно только целые наборы.

Класс стиля предназначен для разработки правил стиля и применения класса стиля к необходимым элементам:

.au-theme-font-color--primary {
  color: #00f;
}
<p class="au-theme-font-color--primary">主色</p>

Класс стилей также имеет свои весьма очевидные дефекты: во-первых, нужно иметь очень четкое оформление правил класса стилей, а затем влияние на разработку также очень существенное: все стили, охватываемые системой тем, могут только быть написано в классе стиля, а не напрямую написано в css. Эти два момента налагают на пользователей определенные когнитивные и эксплуатационные нагрузки. Но преимущества также очевидны: степень детализации управления может быть очень тонкой, нет проблемы синхронизации замены, и в то же время можно напрямую использовать не только тему самой библиотеки компонентов, но и весь проект.

Экспериментально я выбрал класс стиля, так что предположим, что вы сделали тот же выбор.

2.3 Использование классов стилей для разработки и реализации вашей системы тем

Если вы не знаете, с чего начать, попробуйте начать с точки зрения пользователей вашей системы тем. Вы ожидаете, что ваши пользователи смогут управлять темой с помощью простой функции:

adminUi.theme(config)

тогда, естественно, вы определяетеconfigСтруктура. В соответствии с ранее определенной системной функцией темы вы определите ее следующим образом:

const config = {
  colors: {
    'base-0': '#000',
    'base-1': '#232a29',
    'base-2': '#2f3938',
    'base-3': '#44514f ',
    'base-4': '#616e6c',
    'base-5': '#7c8886',
    'base-6': '#b1bbba',
    'base-7': '#d9dedd',
    'base-8': '#eaf0ef',
    'base-9': '#f3f7f6',
    'base-10': '#f7fcfb',
    'base-11': '#fdfdfd',
    'base-12': '#fff',

    'primary-top': '#01241d',
    'primary-up': '#3fd5b8',
    'primary': '#19bc9d',
    'primary-down': '#169f85',
    'primary-bottom': '#e7f7f4',

    'info-top': '#011725',
    'info-up': '#f0faf8',
    'info': '#3498db',
    'info-down': '#2d82ba',
    'info-bottom': '#e6f3fc',

    'warning-top': '#251800',
    'warning-up': '#fec564',
    'warning': '#ffb433',
    'warning-down': '#e99b14',
    'warning-bottom': '#fbf3e5',

    'danger-top': '#220401',
    'danger-up': '#f56354',
    'danger': '#e74c3c',
    'danger-down': '#c33a2c',
    'danger-bottom': '#fae7e5',

    'success-top': '#012401',
    'success-up': '#7fcb7f',
    'success': '#5cb95c',
    'success-down': '#3da63d',
    'success-bottom': '#e7fae7'
  },
  shadows: {
    'base': '0 1px 4px rgba(0, 0, 0, .2)',
    'primary': '0 0 4px rgba(25, 188, 157, .6)',
    'info': '0 0 4px rgba(52, 152, 219, .6)',
    'warning': '0 0 4px rgba(255, 180, 51, .6)',
    'danger': '0 0 4px rgba(231, 76, 60, .6)',
    'success': '0 0 4px rgba(92, 185, 92, .6)'
  },
  radiuses: {
    'small': '2px',
    'large': '5px'
  }
}
  • primary,warning,danger,info,successосновной цвет
  • [COLOR]-up,[COLOR]-downЭто вторичный цвет, светлота которого ближе к основному цвету.
  • [COLOR]-top,[COLOR]-bottomЭто вспомогательный цвет с большим отличием по яркости от основного цвета.
  • base-0,base-12самый темный ахроматический и самый яркий ахроматический
  • base-[1~11]ахроматический (серый), отсортированный по светлоте

Причина, по которой не используются слова с информацией о цвете (например, светлый, темный-первичный и т. д.), а используются числа и направления в качестве названий цветов, заключается в том, чтобы облегчить пользователям определение любого цвета по имени, если вы поместите название чистого черного. как темный, но пользовательская конфигурация #fff чисто белая, это имя приведет к недоразумению. В ахроматических цветах мы используем числа в качестве имен, а в цвете мы используем направления в качестве имен, которые могут не только соответствовать иерархическому дизайну цвета, но и избегать двусмысленности.

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

В то же время, чтобы еще больше упростить настройку цвета, вы решили использовать значения по умолчанию теней, неосновных цветов и ахроматических цветов на основеprimaryцвета и некоторые вспомогательные настройки для их автоматического расчета. Таким образом, фактическая конфигурация пользователя может быть дополнительно упрощена:

export default {
  theme: {
    colors: { // 彩色配置
      primary: '#1c86e2',
      info: '#68217a',
      warning: '#f5ae08',
      danger: '#ea3a46',
      success: '#0cb470'
    },
    shadows: { // 阴影配置
      // primary: '0 0 4px #1c86e2',
      // info: '0 0 4px #68217a',
      // warning: '0 0 4px #f5ae08',
      // danger: '0 0 4px #ea3a46',
      // success: '0 0 4px #0cb470'
    },
    radiuses: {
      small: '3px',
      large: '5px'
    }
  },
  lightnessReverse: false, // 反转lightness排序(黑白主题)
  colorTopBottom: 5, // top和bottom颜色距离纯黑和纯白的lightness的距离,越小越接近纯黑纯白
  colorUpDown: 10, // 彩色上下接近色与正色的lightness距离
  baseColorLeve: 12, // 无彩色分级数量
  baseColorHue: '20%', // 无彩色饱和度
  baseShadowOpacity: 0.2, // 无彩色阴影不透明度
  colorShadowOpacity: 0.6 // 彩色阴影不透明度
}

Структура файловой системы темы выглядит следующим образом:主题系统文件结构

Затем подумайте о том, как пользователи будут применять систему тем к элементам после ее настройки. Классы стилей, предоставляемые вашей системой тем, нуждаются в запоминаемом синтаксисе для удобства пользователей. В настоящее время вы можете разработать синтаксические правила, подобные следующим:

前缀 [-伪类名] -属性名 --属性值 [-权重]
  • префикс: префикс класса стиля темы
  • имя псевдокласса: необязательно, если тема применяется к псевдоклассу текущего элемента, вы можете конкатенировать имя псевдокласса в имени класса
  • имя атрибута: имя атрибута стиля
  • Значение атрибута: значение атрибута стиля, то есть имя, настроенное в конфигурации.
  • вес: необязательный, используйте его, чтобы добавить стиль к этой теме!importantсуффикс

В соответствии с этим набором правил грамматики пользователи используют его следующим образом:

<div class="
  au-theme-background-color--base-12
  au-theme-border-color--primary
  au-theme-font-color--base-3
  au-theme-box-shadow--base
  au-theme-radius--small"></div>

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

3 Предоставьте набор компонентов формы

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

Итак, прежде всего, вы перечислите часто используемые компоненты формы: поле ввода текста, множественный выбор, одиночный выбор, переключатель, раскрывающийся список, каскад, дата, время, загрузка файла, эти компоненты помещаются вами в СПИСОК TODO. .

3.1 Единый интерфейс формы

Вы обнаружите, что многие компоненты формы ведут себя одинаково, например, требуютvalueинтерфейс, должен поддерживатьv-model, обеinputилиchangeсобытия и т.д. Эти унифицированные поведения лучше всего сочетать вместе, поэтому вы можете использовать Vue’smixinФункция для извлечения этих унифицированных поведений вместе, с одной стороны, проста в управлении, а с другой стороны, она может сделать функцию и поведение компонентов формы максимально согласованными, чтобы снизить когнитивные затраты пользователей. .

3.2 Единый метод проверки

Фактически, эта часть функции проверки строго говоря, она может рассматриваться как единый интерфейс формы, поэтому он также может быть помещен в вышеуказанный файл, но логика верификации является относительно независимой, поэтому вы, вероятно, отделить его и сделайте еще одинminxinсправляться.

Если вы много пишете формы, нетрудно обнаружить, что на самом деле есть только два случая, когда дело доходит до проверки:

  • Интерактивная проверка: когда пользователь заполняет элемент формы, запускается проверка этого элемента.
  • Проверка отправки: пользователь запускает проверку всех элементов всей формы при отправке.

Поддерживать интерактивную аутентификацию на самом деле очень просто, и этого можно добиться, просто используя события. Для поддержки проверки отправки каждый компонент формы должен предоставлять определенные методы проверки для внешних вызовов, например:this.$refs.$userNameInput.validate(), результат проверки можно получить, вызвав эту функцию извне, при отправке формы метод проверки всех компонентов формы можно вызвать один раз.

И когда ваша программа запускает проверочный код, возможны две ситуации:

  • Синхронная проверка
  • Асинхронная проверка

Поддерживать синхронную проверку очень просто, просто вызовите заданную внешнюю функцию проверки правильности, а затем верните ее результат. Но если это асинхронная проверка, это будет более хлопотно. Давайте копнем немного глубже, предположив, что в настоящее время пользователь указал что-то вроде<au-input/>Валидатор для:

<au-input
  :validatiors="[
    {
      validator (v) { return v > 0 },
      warning: '必须大于0'
    }
  ]"/>

Когда вы получаете валидатор, вы не можете знать, синхронный он или асинхронный, поэтому вы можете попросить пользователя указать синхронный или асинхронный:

<au-input
  :validatiors="validators"/>
export default {
  data () {
    return {
      validators: [
        {
          validator (v) { return v && v.length && !/^\s*$/g.test(v) },
          warning: '不能为空'
        },
        {
          validator () {
            return new Promise(resolve => {
              axios.get('is-duplicated-name')
                .then(data => resolve(data.result))
            })
          },
          warning: '已经有重复的名字了',
          async: true
        }
      ]
    }
  }
}

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

// the common validation logic of enhanced form components
export default {
  // ... 省略的代码 ...
  methods: {
    validate () {
      let vm = this
      if (vm.warnings && vm.warnings.length) return false
      if (!vm.validators) return false
      // separate async and sync
      let syncStack = []
      let asyncStack = []
      vm.validators.forEach((v) => {
        if (v.async) {
          asyncStack.push(v)
        } else {
          syncStack.push(v)
        }
      })

      // handler warnings
      let handleWarnings = (res, i, warning) => {
        if (!res) {
          vm.$set(vm.localWarnings, i, warning)
        } else {
          vm.$delete(vm.localWarnings, i)
        }
      }

      return new Promise((resolve) => {
        let asyncCount = asyncStack.length
        // execute sync validation first
        syncStack.forEach((v, i) => {
          handleWarnings(v.validator(vm.value), i, v.warning)
        })
        // if sync validation passed, execute async validationg
        if (!vm.hasLocalWarnings) {
          if (asyncCount <= 0) { // no async
            resolve(!vm.hasLocalWarnings)
          } else {
            Promise.all(asyncStack.map((av, i) => {
              return av.validator(vm.value).then(res => {
                handleWarnings(res, i, av.warning)
              })
            })).then(results => {
              if (results.includes(false)) resolve(!vm.hasLocalWarnings)
              else resolve(!vm.hasLocalWarnings)
            })
          }
        } else { // if sync validation failed
          resolve(!vm.hasLocalWarnings)
        }
      })
    }
  }
}

Метод проверки компонента формы возвращаетpromise,В егоresolveКонкретный результат проверки возвращается в методе.Преимущество в том, что при отправке проверки пользователю не нужно различать синхронный и асинхронный, и все обрабатываются единообразно, что просто и удобно:

export default {
  validateAllFormitems () {
    Promise.all([
      this.$refs.name.validate(),
      this.$refs.age.validate(),
      this.$refs.gender.validate()
    ]).then(results => {
      if (!results.includes(false)) this.submit()
    })
  }
}

3.3 Компоновка упаковки

Существует два распространенных расположения форм: одно — расположение меток и элементов формы вверх и вниз, а другое — расположение слева и справа. Компоновка компонентов формы должна быть согласованной, поэтому для этого можно создать компонент-контейнер компонентов формы. Разумеется, в вашем интерфейсе компонента формы также должен быть соответствующий интерфейс для управления набором текста.

При расположении вверх и вниз, в дополнение к обычной ширине, такие компоненты, как поля ввода текста и раскрывающиеся поля выбора, также должны учитывать ширину 100%. На этом этапе вам может понадобиться другойfull-widthИнтерфейс, позволяющий пользователю выбирать, заполнять ли его на всю ширину.

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

Вы инкапсулируете эти функции вform-itemв компоненте-контейнере и используйте его в каждом компоненте формы.

3.4 Даты, время и диапазоны дат и времени

Что касается компонентов, соответствующих трем функциям даты, времени и диапазона даты и времени, я надеюсь, вы сможете подумать над вопросом: как разделить функции компонентов.

Общее деление это

  • Выбор даты: вы можете выбрать дату одной точки или диапазон дат.
  • Селектор времени: вы можете выбрать время в одну точку или временной диапазон
  • Выбор даты и времени: вы можете выбрать дату и время одной точки или вы можете выбрать диапазон даты и времени.

Тем не менее, мой более рекомендуемый дивизион

  • Выбор даты: можно выбрать только одну точечную дату
  • Выбор времени: можно выбрать только одно момент времени
  • Выбор диапазона даты и времени: можно выбрать только диапазон, но это может быть просто диапазон дат, или только диапазон времени, или диапазон даты и времени.

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

4 Предоставьте набор библиотек значков

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

В более ранних версиях пользовательского интерфейса администратора использовался более красивыйIoniconsБиблиотека иконок, но ее типов иконок немного меньше, а последующие версии заменены наFont AwesomeБиблиотека иконок.

Какой значок вы выберете, не имеет большого значения.Вы даже можете не использовать стороннюю библиотеку значков, а только предоставить минимальный набор значков, требуемый вашей библиотекой компонентов, и оставить выбор, какую библиотеку значков использовать, вам. Компонент user-icon должен поддерживать использование сторонних значков.

5 Необходимые системы сетки

Почти большинство библиотек пользовательского интерфейса на рынке поставляются с системой сетки, которая позволяет разработчикам быстро адаптироваться к страницам макета. Более ранние технологии, такие как библиотеки пользовательского интерфейса, такие как Bootstrap, используютfloatа такжеwidthи другие свойства CSS и медиа-запросы CSS для реализации системы сетки. И современные библиотеки пользовательского интерфейса в основном используютflexреализовать сетку.

Возможно, вы захотите использовать современные методы для реализации системы сетки, такой как Bootstrap. Однако в Bootstrap использование сеток зависит от классов стилей, которым требуется родительский элемент и несколько дочерних элементов для формирования необходимой структуры. Вы можете быть недовольны этим. Применение классов стиля может использоватьpropsВместо этого фиксированный родительский элемент, о котором вы, вероятно, не хотите, чтобы пользователь заботился. Для этого вы рассматриваете возможность использования только одногоgridкомпоненты для реализации всей системы сетки, поэтому при инициализации вам необходимо иметь дело с родительским элементом, особенно необходимость иметь дело с родительским элементом предполагает использованиеdisplayСвойство, поскольку вам нужно всегда использовать его на родительском элементеflexАтрибуты.

Вполне возможно использовать медиа-запросы иflexсвойства для завершения системы сетки. Но когда дело доходит до интервалов, это может быть сложно реализовать с помощью CSS.flexСвойства могут обеспечить характеристики горизонтального расположения сетки, но ширины самой сетки, поскольку она включает в себя расчет интервалов, вы можете использовать для этого JavaScript.

пользователь черезpropsпроходить вwidthLgСвойства , сообщают количество сеток, занимаемых компонентом под большим экраном, черезspaceСвойство сообщает расстояние между компонентом и следующим компонентом. В это время необходимо ввести понятие строки. Вам нужно рассчитать, какие компоненты находятся в строке, потому что последний в конце строкиgridС правой стороны не может быть места, иначе оно будет сжато до следующей строки, потому что превышает общую ширину одной строки.

Реализация сетки не сложна, главное требование — это характеристики самой сетки: количество сеток, занимаемых экранами разной ширины, расстояние смещения и шаг. Вы понимаете, я не собираюсь говорить о конкретной реализации, но вы можете пойти и посмотретьисходный кодпроверить это. Честно говоря, реализация этой части не элегантна, в основном какие-то экспериментальные практики. Добро пожаловать на рефакторинг!

6 Модульное тестирование и интернационализация

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

7 Заключение

Если принять во внимание конкретные детали реализации, то эта статья по сути не имеет никакого отношения. Я просто перечислил проблемы, с которыми вам нужно столкнуться, чтобы написать библиотеку компонентов, и рассказал о решениях в целом. В любом случае, надеюсь, что это может вдохновить вас.

В конце концов, даже в сборке колес есть свое удовольствие. Сначала выкопайте дыру здесь.В будущем я могу выбрать некоторые интересные компоненты из компонентов и написать еще одну статью, чтобы поделиться подробностями реализации конкретных компонентов, так что следите за обновлениями~

В то же время вы также можете разветвить эту библиотеку пользовательского интерфейса, полную личных вещей.