Каждый интерфейс заслуживает собственной библиотеки компонентов, как арбуз каждое лето 🍉

Архитектура внешний интерфейс React Native
Каждый интерфейс заслуживает собственной библиотеки компонентов, как арбуз каждое лето 🍉

⚠️Эта статья является первой подписанной статьей сообщества Nuggets, и её перепечатка без разрешения запрещена.

Привет всем, яЛуожу🎋, деревянный фронтенд, живущий в Ханчжоу 🧚🏻‍♀️, если вам понравилась моя статья 📚, вы можете помочь мне собрать духовную силу ⭐️ лайком.

Эта статья была впервые опубликована вКолонка самородков, синхронизированный с официальным аккаунтом «Program Life» иБлог Луожу.

У Луо Чжу есть друг Сяо Хэй, которого недавно спросили в интервью, как проектировать библиотеку компонентов интерфейса. Неопытный Xiao Hei отвечал за извлечение и инкапсуляцию бизнеса в библиотеку и вторичную инкапсуляцию бизнеса на основе antd. В конце концов, Сяо Хэй был убит HR из-за недостаточной духовной силы. На самом деле этот вопрос исследует не концепцию ложного и пустого пространства, а об управлении складом разработчика, проектировании компонентов, модульном тестировании, непрерывной интеграции, управлении совместной работой и других возможностях. Поэтому, чтобы дать Xiaohei возможность идеально ответить на этот вопрос, я решил привести Xiaohei к созданию библиотеки компонентов React Native шаг за шагом.

Это практическое руководство по созданию библиотеки компонентов с большим количеством сухих товаров, которое не только охватывает общие спецификации кода, спецификации отправки, обслуживание документов, модульное тестирование и объяснения конфигурации GitHub Action, но также включает в себя управление несколькими пакетами на основе lerna. Архитектура и библиотека иконок React Native Построение, разработка и отладка библиотеки компонентов React Native, принцип и реализация загрузки по требованию. Идея инженерии общая, поэтому независимо от того, какой фреймворк вы используете, эту статью стоит прочитать.

Если вы заинтересованы в разработке библиотеки компонентов перед компьютером, вы можете сначала поставить лайк, а затем продолжить следить за развитием библиотеки компонентов Luozhu и Xiaohei. ПС: сотрудничатьсклада такжеДокументация библиотеки компонентовЛучше прочтите эту статью!

Стоя на плечах Vant Design

Поддержание и развитие библиотеки компонентов, несомненно, требует много времени и энергии. Можно сказать что вначале все сложно.Прежде всего надо иметь самопознание.Ввиду отсутствия дизайнеров и любительской разработки,я выбрал вариант реализации React Native для существующего UI Design для запуска библиотеки компонентов путь развития. под следствиемvant,fishd-mobileа такжеantd-mobileТогда я выбрал вант. Вот сравнение статус-кво нескольких складов:

Библиотека компонентов команда Github Star Еженедельные загрузки NPM Обслуживание
vant Понравилось 17.7K 27,789 Большой размер и высокая популярность
antd-mobile Ant Design Team 8.9K 31,470 Почти никакого обслуживания, говорят, что муравьи не используются внутри
fishd-mobile Интерфейс облачного бизнеса NetEase 29 22 Похоже на проект KPI без сомнений

Определено направление движения, которое заключается в том, чтобы дать нашей библиотеке компонентов подходящее имя и слоган, который выражается в образе фронтенд-инженеров.package.jsonизnameа такжеdescriptionПоле:

// package.json
{
    "name": "vant-react-native",
    "description": "Lightweight React Native UI Components inspired on Vant"
}

Поскольку наша библиотека компонентов находится в RN версии vant, ссылаясь на метод именования lottie-react-native, styled-react-native и jpush-react-native, мы называем библиотеку компонентов vant-react-native, и так же надеюсь, что библиотека компонентов по доработке будет доступна официальная поддержка от vant.

Многопакетная архитектура управления на основе Lerna

Lerna — это инструмент администрирования для управления проектами JavaScript, которые содержат несколько пакетов. Склады, которыми управляет Lerna, обычно называются монорепо. Преимущества многопакетной архитектуры управления на основе Lerna:

  • Разделение на уровне компонентов, независимый контроль версий, и каждый компонент имеет записи версий для отслеживаемости.
  • Компоненты выпускаются отдельно, поддерживая оттенки серого, откат версии и плавное обновление и обновление.
  • Ссылка по требованию, пользователь устанавливает определенный пакет компонентов, и эффект загрузки по требованию может быть достигнут без настройки.
  • Разделение задач, уменьшение масштабной сложности, четкие и контролируемые зависимости между компонентами
  • Принцип единой ответственности снижает сложность участия и вклада друзей с открытым исходным кодом.
.
└── packages
    ├── button # @vant-react-native/button
    └── icons # @vant-react-native/icon

Инициализировать проект lerna

$ mkdir vant-react-native && lerna init --independent

yarn workspaces

использоватьyarn workspacesОбъединить ЛернуuseWorkspacesможет быть реализованLerna Hoisting. Это не лишнее, это позволяет управлять зависимостями в едином месте (корневом каталоге), что экономит время и место.

настроитьlerna.json:

{
  ...
  "npmClient": "yarn",
  "useWorkspaces": true
}

После размещения на воркспейсе пряжи, lerna'spackagesбудет топpackage.jsonизworkspacesпокрытие:

{
  "private": true,
  ...
  "workspaces": [
    "packages/*"
  ],
}

lerna publish config

Если ты совсем не хочешь бытьpackage.jsonЯвно задайте конфигурацию реестра отдельно в файле, например, при использовании частного реестра установитеcommand.publish.registryОчень полезно. настроитьignoreChangesЭто сделано для того, чтобы избежать ненужных обновлений версии.

"ignoreChanges": [
  "ignored-file",
  "**/__tests__/**",
  "**/*.md"
],
"command": {
  "publish": {
    "registry": "https://registry.npmjs.org"
  }
}

Кроме того, если имя вашего пакета ограничено, оно должно быть в этом пакете.package.jsonустановить вpublishConfig.accessдля"public".

lerna version config

При настройкеconventionalCommitsдляtrue, версия lerna будет использоватьConventional Commits Specificationдля подтверждения обновления версии иСоздайте файл CHANGELOG.md.

"command": {
  "version": {
    "conventionalCommits": true,
    "message": "chore(release): publish"
  }
}

канонический коммит

нормализоватьgit commitдля улучшенияgit logУдобочитаемость, контролируемый контроль версий и генерация журнала изменений играют важную роль. Луожу был вОдна статья, чтобы получить нормализованный Git CommitКонцепция обычных коммитов и настройка таких инструментов, как commitizen, cz-customizable, @commitlint/cli, yorkie и commitlint-config-cz, подробно описаны в .

Из-за громоздкой конфигурации я@youngjuning/cliдобавлено вinit-commitКоманда настраивает обычную фиксацию одним щелчком мыши. могу открыть этоcommitПросмотр информации о конфигурации.

Примечание: использование версии Husky High не имеет обратной совместимости, я в этомcommitЗаменил хаски на йорка.

нормализация кода

Важность нормализации кода очевидна.Инструменты, используемые для нормализации кода, включают editorconfig, eslint, prettier и т. д.Установите его | Никогда больше не беспокойтесь о конфигурации ESLintВ этой статье я рассказал, как шаг за шагом создать свой собственный плагин конфигурации eslint и вывести@youngjuning/eslint-configа также@youngjuning/prettier-config.

vant-react-native временно использует @youngjuning/eslint-config, @youngjuning/prettier-config для ограничения спецификаций кода проекта. Соответствующая конфигурация выглядит следующим образом.

eslint

Сначала установите плагины, необходимые для react-native.

yarn add -D eslint-plugin-react \
  eslint-plugin-react-hooks \
  eslint-plugin-jsx-a11y \
  eslint-plugin-import \
  eslint-plugin-react-native

затем настройте.eslintrc.js

// .eslintrc.js
module.exports = {
  extends: ['@youngjuning/eslint-config/react-native']
}

prettier

// .prettierrc.js
module.exports = require('@youngjuning/prettier-config');

План @youngjuning/eslint-config также управляется lerna, которая создает @youngjuning/eslint-config-react, @youngjuning/eslint-config-react-native, @youngjuning/eslint-config-vue, чтобы разработчики не слишком много нужно настраивать.из коробки.

editorconfig

# .editorconfig
# EditorConfig is awesome: http://EditorConfig.org

# top-most EditorConfig file
root = true

# Unix-style newlines with a newline ending every file
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

[*.md]
trim_trailing_whitespace = false

[*.gradle]
indent_size = 4

[BUCK]
indent_size = 4

yorkie & lint-staged

$ yarn add -D yorkie lint-staged
{
  "gitHooks": {
    "commit-msg": "commitlint -e -V",
    "pre-commit": "lint-staged"
  },
  "lint-staged": {
    "**/*.{js,jsx,ts,tsx}": [
      "eslint --fix",
      "git add ."
    ]
  },
}

Первый компонент начинается с Icon

Библиотека зрелых компонентов будет иметь свой собственный набор иконок.Иконки обычно создаются дизайнерами через Sketch, а затем экспортируются в виде файлов svg.

Файл svg для значков ant-design:сохранить локально, который затем генерируется скриптомреактивный компонент,vue-компонента такжеicons-react-nativeи другие компоненты, т.к. поддерживаемый фреймворк относительно полный, нам не нужно реализовывать его самим, мы используем RN напрямуюicons-react-native.

vant и fishd-mobile поддерживают файлы svg через Iconfont, а затем устанавливают@font-faceспособ реализации компонента Icon, как показано на рисунке:

image.png

С файлом ttf мы можем использовать скрипт для создания компонента Icon на основе файла ttf, такого как @ant-design/icons-react-native, но использование шрифта ttf имеет недостаток, то есть каждый раз, когда значок обновляется, файл ttf должен быть обновлен соответствующим образом.Затем снова упаковать и выпустить приложение. И ttf не поддерживает значки нескольких цветов, в результате чего все значки становятся монохромными. Если вы используете react-native-vector-icons, в библиотеку встроено более 10 наборов файлов ttf, которые вместе составляют около 2M; вы можете их не использовать, но они все равно будут упакованы в ваше приложение, что также me Одна из причин, почему я считаю, что react-native-elements — сильная библиотека.

Итак, как нам реализовать React Native версию vant-icons только со ссылками на Iconfont? Здесь Луожу не писал свой собственный скрипт, а использовал инструмент под названием react-native-iconfont-cli.fwh1990Для решения вышеуказанных проблем большой парень использует чистый Javascript для реализации преобразования iconfont в компоненты React, не полагаясь на файлы шрифтов ttf и не загружая значки вручную на локальный сервер.

Создайте подпакет lerna

# 创建主包,主包用来统一导出所有的组件
$ lerna create vant-react-native -y
# 创建 icons 包,我们的第一个组件!
$ lerna create @vant-react-native/icons -y

Наша структура каталогов выглядит так:

.
└── packages
    ├── icons
    │   ├── README.md
    │   └── package.json
    └── vant-react-native
        ├── README.md
        └── package.json

генерировать иконки

Установить плагин

yarn workspace @vant-react-native/icons add -D react-native-svg react-native-iconfont-cli

Создать файл конфигурации

мы вpackages/iconsиспользовать в каталогеnpx iconfont-initкоманда будет генерироватьiconfont.jsonфайл, содержимое после настройки выглядит следующим образом:

{
  "symbol_url": "https://at.alicdn.com/t/font_2553510_7cds497uxwn.js",
  "use_typescript": false,
  "save_dir": "./lib",
  "trim_icon_prefix": "van-icon",
  "default_icon_size": 18
}

Создание стандартных компонентов React Native

воплощать в жизньnpx iconfont-rnкоманда для создания стандартных компонентов React Native. Из-за большого количества файлов значков мы не добавляем продукты значков в управление git. Поэтому нам нужно выполнить команду сборки перед публикацией npm:

{
  "build": "npx iconfont-rn",
  "prepublishOnly": "yarn build"
}

Настроить реактивный натив-вант

мы упоминали ранееpackages/vant-react-nativeэто каталог основного пакета, нам нужно поместить@vant-react-native/iconsПакет добавляется в зависимости основного пакета и экспортируется.

добавить зависимости

$ lerna add @vant-react-native/icons --scope vant-react-native

Компонент «Экспорт значка»

// packages/vant-react-native/src/index.ts
export { default as Icon } from '@vant-react-native/icons';
export * from '@vant-react-native/icons';

конфигурация tsconfig

Мы ожидаем одинаковую конфигурацию для каждого подпакета, поэтому сначала создадим новый в корневом каталоге всего проекта.tsconfig. base.json, вы можете наследовать его в подпакете.

{
  "extends": "../../tsconfig.base.json",
  "compilerOptions": {
    "outDir": "lib",
  },
  "include": ["src/**/*"]
}

Настройте сценарий публикации

а также@vant-react-native/iconsКак и подпакеты, нам нужно добавитьbuildа такжеprepublishOnlyсценарий:

{
  "build": "tsc",
  "prepublishOnly": "yarn build"
}

выпуск пакета

Для первого выпуска обратите внимание на использованиеlerna publish 0.0.1, т.к. команда выпуска lerna не освобождает этот параметр в первый раз, необходимо явно указать начальную версию. Или первоначальная версия может быть установлена ​​​​на0.0.0затем выполнитьlerna publish.

Совет: Если вы хотите просмотреть содержимое пакета после публикации, вы можете пройти черезjsdelivrПроверить. например, только что выпущенныйvant-react-nativeа также@vant-react-native/icons

Разработка и отладка

Полный и проверенный процесс отладки может не только обеспечить проверку того, соответствуют ли компоненты ожиданиям на этапе разработки, но и уменьшить сложность участия членов сообщества открытого исходного кода. Отладка библиотеки компонентов React Native в основном такая же, как и другие процессы стека технологий, но посколькуMetro не поддерживает программные ссылкиА vant-react-native — это единый складской проект на базе lerna, наша конфигурация будет другой.

image.png

Инициализировать приложение React Native

Поскольку это проект React Native, нам нужно инициализировать проект React Native. Сначала найдите место для использованияreact-native init vantapp --template react-native-template-typescriptСоздайте новое приложение React Native. Затем объедините получившееся приложение с нашим основным проектом. Общая структура проекта выглядит следующим образом:

.
├── App.tsx
├── __tests__
│   └── App-test.tsx
├── android
│   ├── app
│   ├── build.gradle
│   ├── gradle
│   ├── gradle.properties
│   ├── gradlew
│   ├── gradlew.bat
│   └── settings.gradle
├── app.json
├── babel.config.js
├── commitlint.config.js
├── index.js
├── ios
│   ├── Podfile
│   ├── Podfile.lock
│   ├── Pods
│   ├── vantapp
│   ├── vantapp.xcodeproj
│   ├── vantapp.xcworkspace
│   └── vantappTests
├── lerna.json
├── metro.config.js
├── package.json
├── packages
│   ├── icons
│   └── vant-react-native
├── tsconfig.base.json
├── tsconfig.json
└── yarn.lock

Основной конфликт заключается в настройке таких инструментов, как Prettier и eslint, слияние не так уж и сложно. Перед запуском проекта нам обычно нужно скомпилировать проект. мы можем использоватьlerna run buildПакет команд запускает подпакеты вbuildнпм скрипты.

Примечание 📢: из-за зависимостей между подпакетами не используйте--parallelпараметр для параллельного выполнения сценария упаковки.

Теперь давайте напишем демонстрацию Jiugongge для проверки:

// App.tsx
import React, { Component } from 'react';
import { View, Text, SafeAreaView, ScrollView } from 'react-native';
import { Icon } from 'vant-react-native';
// 我们也可以只安装 @vant-react-native/icons 包
// import { VanIconAdd } from '@vant-react-native/icons'

type IconNameType = React.ComponentProps<typeof Icon>['name'];

export default class App extends Component {
  render() {
    return (
      <SafeAreaView>
        <ScrollView>
          <Text
            style={{ textAlign: 'center', paddingVertical: 20, fontSize: 25, color: '#007fff' }}
          >
            vant-react-native
          </Text>
          <View style={{ flexWrap: 'wrap', flexDirection: 'row' }}>
            {data.map((item, index) => {
              const lastLineLength = data.length % 4 || 4;
              return (
                <View
                  key={item}
                  style={{
                    width: '25%',
                    marginBottom: index < data.length - lastLineLength ? 40 : 0,
                    alignItems: 'center',
                  }}
                >
                  <Icon name={item} size={40} />
                  <Text style={{ color: '#646566', marginTop: 10 }}>{item}</Text>
                </View>
              );
            })}
          </View>
        </ScrollView>
      </SafeAreaView>
    );
  }
}

const data: IconNameType[] = ['location-o', 'like-o', 'star-o', 'phone-o', 'setting-o', 'fire-o', 'coupon-o', 'cart-o', 'shopping-cart-o', 'cart-circle-o', 'friends-o', 'comment-o', 'gem-o', 'gift-o', 'point-gift-o', 'send-gift-o', 'service-o', 'bag-o', 'todo-list-o', 'balance-list-o', 'close', 'clock-o', 'question-o', 'passed'];

затем выполнитьyarn iosПосмотрите его в действии (затем мы можем выполнитьyarn start --reset-cacheБыстрый старт отладки):

image.png

В приведенном выше примере кода мы видим, что мы напрямую используемimport { Icon } from 'vant-react-native';Вместо относительных путей ссылайтесь на модули в пакетах. Но наш проект не установил эту зависимость, как компилятор ее нашел? Здесь также нет серебряной пули, потому что lerna будет программно связывать подпакеты в node_modules, мы можем использоватьls -alНайдено, чтобы увидеть фактическую точку пакета:

image.png

Мы также можем видеть в подсказке типа, что она на самом деле указывает на файлы в пакетах:

image.png

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

Своевременная компиляция

Теперь наш поток отладки:

  1. Изменить код
  2. воплощать в жизньlerna run buildСкомпилируйте каждый подпакет
  3. воплощать в жизньyarn iosОтладка проекта
  4. Изменить код
  5. воплощать в жизньlerna run buildперекомпилировать
  6. воплощать в жизньyarn start --reset-cacheзапустить проект
  7. Петля 4, 5, 6.

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

Любую повторяющуюся работу можно заменить скриптами. Прежде всего, нам нужно добавить скрипты, компилируемые в реальном времени, в каждый подпакет, такие как rollup, babel, webpack и typescript, имеющие параметры для достижения компиляции в реальном времени:

{
  "scripts": {
    "dev": "tsc -w",
    "build": "tsc",
    "prepublishOnly": "yarn build"
  },
}

И наш пакет @vant-react-native/icons используетnpx iconfontВарианта компиляции в реальном времени нет.После исследований я представилonchangeЭта библиотека может выполнять команду на основе шаблона глобуса, прослушивая изменения файла:

{
  "scripts": {
    "dev": "onchange -i 'iconfont.json' -- yarn build",
  }
}

Затем нам нужно использоватьlerna run dev --parallelПакетное выполнение сценариев компиляции в реальном времени, добавьте сюда--parallelЭто потому, что если подпакет скомпилирован в реальном времени, процесс зависнет. Чтобы исправить это, нам пришлось предварительно скомпилировать@vant-react-native/iconspackage, то по той же причине я ввелnpm-run-allвыполнять параллельноlerna run devа такжеreact-native start, полный скрипт выглядит следующим образом:

{
  "predev": "lerna run build --scope @vant-react-native/icons",
  "dev": "lerna run dev --parallel",
  "start": "react-native start",
  "debug": "run-p dev start",
}

нагрузка по требованию

Сяо Хей: «Брат Луожу, я представил всю библиотеку компонентов, чтобы использовать несколько компонентов react-native-elements. Поскольку эта библиотека компонентов зависит от react-native-vector-icons, пакет пакета становится больше. Если я просто хотите использовать весь набор vant-react-native, как решить эту проблему?»

Как мы все знаем, инструмент упаковки React Native Metroвстряхивание дерева не поддерживается. Способ решить эту проблему на самом деле очень прост, вы можете знать, как сотрудничатьbabel-plugin-importМожно выполнить требования по загрузке по требованию. Но поскольку у нас многопакетная архитектура управления, нам нужно разработать решение для многопакетной архитектуры.

реактивный пакет

Чтобы сравнить размер пакета до и после оптимизации, нам нужно использоватьreact-native bundleКоманда, чтобы увидеть размер чистого пакета JS, давайте кратко рассмотрим эту команду:

react-native bundle --platform ios --entry-file index.js --bundle-output ./bundle/ios/index.ios.jsbundle --assets-dest ./bundle/ios --dev false --reset-cache
  • --entry: входной файл js
  • --bundle-output: Путь к сгенерированному файлу пакета
  • --platform:Платформа
  • --assets-dest: Выходной каталог для ресурсов изображения
  • --dev: Является ли это версией разработки или нет, мы присваиваем ей значение false при печати официальной версии установочного пакета.
  • --reset-cache: сброс кеша, избегайте упаковки старого кеша

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

Ранее мы упоминалиpackages/vant-react-nativeтолько один файлsrc/index.tsИспользуется для экспорта всех подпакетов, теперь мы добавляем новую кнопку пакета, которая выглядит так:

export { default as Icon } from '@vant-react-native/icons';
export * from '@vant-react-native/icons';
export { default as Button } from '@vant-react-native/icons';

При таком способе экспорта пользователи могут толькоimport Button from '@vant-react-native/button';илиimport Button from 'vant-react-native/lib/button';Способ ручной реализации загрузки по требованию не только неудобен для использования разработчиками, но и добавляет много байтов от упаковки продукта. Итак, вопрос в том, какая организационная форма может удовлетворить нагрузку по требованию? Ответ находится в документации плагина babel-plugin-import:

image.png

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

"plugins": [
  ["import", { libraryName: "antd", style: true }]
]

Серебряной пули по-прежнему нет, плагин работает как раз на месте вашей правой руки. Зная принцип, мы можем реорганизовать наш пакет vant-react-native в формате, требуемом документацией:

.
├── CHANGELOG.md
├── lib                    # 上传到 NPM 的编译产物
│   ├── button             # 符合 babel-plugin-import 的默认配置要求
│   │   ├── index.d.ts
│   │   └── index.js
│   ├── icon
│   │   ├── index.d.ts
│   │   └── index.js
│   ├── index.d.ts
│   └── index.js          # export * from './button';
├── package.json
├── src                   # 源码目录
│   ├── button
│   │   └── index.ts
│   ├── icon
│   │   └── index.ts
│   └── index.ts
└── tsconfig.json         # 编译配置,将 ts 文件编译到 lib 文件夹下

вант-реагировать-родной/src/кнопка/index.ts:

import Button from '@vant-react-native/button';
export default Button;
export { Button };

вант-реагировать-родной/src/icon/index.ts:

import Icon from '@vant-react-native/icons';

export default Icon;
export { Icon };
export * from '@vant-react-native/icons';

вант-реагировать-родной/src/index.ts:

export * from './icon';
export * from './button';

Затем измените babel.config.js в проекте:

module.exports = {
  presets: ['module:metro-react-native-babel-preset'],
  plugins: [
    ["import", {libraryName: 'vant-react-native'}]
  ],
};

Пишете плагин для Babel?

Хотя требования можно выполнить, изменив метод экспорта основного пакета, это значительно увеличивает сложность самого проекта. Мы уже знаем, что принцип babel-plugin-import заключается в преобразовании эталонного пути. Так можем ли мы динамически поставить его через плагин?import {Button} from 'vant-react-native'Превратиться вimport Button from '@vant-react-native/button'Шерстяная ткань? Ответ да, вот что у меня есть на основе babel-plugin-importcustomNameConfiguration записывает набор конфигураций и инкапсулирует их в пакет babel-plugin-import-vant:

import camelCase from 'camelcase';

export default (): any[] => [
  [
    'import',
    {
      libraryName: 'vant-react-native',
      customName: (name: string) => {
        if (name === 'icon') {
          return '@vant-react-native/icons';
        }
        if (name.match(/^van-icon-/)) {
          return `@vant-react-native/icons/lib/${camelCase(name, { pascalCase: true })}`;
        }
        return `@vant-react-native/${name}`;
      },
    },
    'vant-react-native',
  ],
  [
    'import',
    {
      libraryName: '@vant-react-native/icons',
      customName: (name: string) => {
        return `@vant-react-native/icons/lib/${camelCase(name, { pascalCase: true })}`;
      },
    },
    '@vant-react-native/icons',
  ],
];

в проектеbabel.config.jsдобавить в конфигурациюplugins: [...require('babel-plugin-import-vant').default()]Его можно загрузить по запросу.

Есть ли что-то еще, что можно оптимизировать? Вы можете обнаружить, что я только что экспортировал конфигурацию через функцию, а не настоящий плагин, поэтому в будущем я настрою собственный плагин загрузки Babel по требованию vant-react-native.

name.match(/^van-icon-/)Это условие суждения связано с тем, что@vant-react-native/iconsВ дополнение к экспортируемому по умолчанию компоненту значка пакет также экспортирует множество отдельных компонентов значка.Чтобы еще больше уменьшить размер пакета, мы также загружаем этот подпакет по запросу.

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

Дисплей достижений

начальный размер пакета Загрузка по запросу не настроена (представляем кнопку) Загрузка по требованию (представляем кнопку) Загрузка по запросу (представить Icon) Загрузка по требованию (представляем VanIconAdd)
723KB 1.8M 725KB 1.8M 1.22M

Причина, по которой пакет Icon большой, заключается в том, что библиотека react-native-svg велика, поэтому не рекомендуется использовать компонент Icon напрямую, а использовать отдельный компонент значка, такой как VanIconAdd и VanIconEye, который довольно ароматен без 593 КБ.

Документация библиотеки компонентов

Самое важное в документации библиотеки компонентов — это интерактивная демонстрация.Я старший пользователь Dumi.С помощью dumi-theme-mobile иumi-plugin-react-nativeМы вполне можем встретить построение документации библиотеки компонентов React Native.

Интегрировать Думи в проект

Установите зависимости:

$ yarn add dumi dumi-theme-mobile umi-plugin-react-native -D

Конфигурационный файл:

Добавьте в корневой каталог проекта.umirc.ts

import { defineConfig, IConfig } from 'dumi';

export default defineConfig({
  title: 'vant-react-native',
  mode: 'site',
  logo: 'https://img01.yzcdn.cn/vant/logo.png',
  favicon: 'https://img01.yzcdn.cn/vant/logo.png',
  resolve: {
    includes: ['docs', 'packages/button', 'packages/icons'],
  },
  // more config: https://d.umijs.org/config
} as IConfig);

Стоит отметить, что Dumi поддерживает репозиторий Lerna и по умолчаниюpackages/[包名]/srcИщет базовый путь в документации Markdown всех подпакетов и создает маршрут. пройти черезresolve.includesКаталог документов, который нюхает dumi, можно настроить, и dumi попытается рекурсивно найти файлы уценки в настроенном каталоге.

Добавьте npm-скрипт:

Примечание 📢: поскольку фактическими зависимостями являются пакеты в пакетах, мы должны сначала скомпилировать все пакеты, иначе развертывание сообщитThis dependency was not found:ошибка.

{
  "scripts": {
    "start:dumi": "dumi dev",
    "build:dumi": "lerna run build && dumi build"
  }
}

Игнорировать файлы (.gitignore):

# umi
.umi
.umi-production
.env.local
dist/

Развертывание на страницах GitHub

Новое в корневом каталоге.github/workflows/gh-pages:

name: github pages
on:
  push:
    branches:
      - main # default branch
jobs:
  deploy:
    runs-on: ubuntu-18.04
    steps:
      - uses: actions/checkout@v2
      - run: yarn install
      - run: yarn build:dumi
      - name: Deploy
        uses: peaceiris/actions-gh-pages@v3
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: ./dist

предварительный просмотр

Теперь мы можем получить доступЭто гигантский Нин, это .org/van T-react-…Оцените эффект:

image.png

image.png

image.png

Оптимизация конфигурации

Сейчас сайт документации на базе думи только инициализирован, многие конфигурации (.umirc.ts) можно оптимизировать, например:

  1. Настроить ускорение CDN на основе jsdeliver
const isProd = process.env.NODE_ENV === 'production';
...
publicPath: isProd ? 'https://cdn.jsdelivr.net/gh/youngjuning/vant-react-native@gh-pages/': '/',
  1. Добавочная публикация и предотвращение кэширования загрузки в браузере
{
  hash: true
}
  1. Статистика сайта Umeng
{
  scripts: ['https://s9.cnzz.com/z_stat.php?id=1280093214&web_id=1280093214'],
  styles: ['a[title=站长统计] { display: none; }'],
}
  1. настроитьexportStatic: {}Выведите все маршруты в виде структуры каталогов HTML, чтобы избежать ошибки 404 при обновлении страницы.

Предварительная версия запроса на вытягивание

Учитывая, что сообщество внесет код и документацию позже. Прежде чем pr будет объединен с основной веткой, нам нужно предварительно просмотреть документ или компонент. Удовлетворением этой потребности является статическая служба хостинга под названием surf.sh. Surge поддерживает бесплатную публикацию файлов HTML, CSS и JS в Интернете с помощью простых команд из командной строки.

Подать заявку на Surge Token

Установите всплеск-cli:

npm install --global surge

Зарегистрируйтесь для увеличения учетной записи:

suerge login

Получить токен:

suerge token

Настроить ЭК

Из-за проблем с безопасностью GitHub нельзя использовать подключаемый модуль действия для предварительного просмотра.Мы настроили CI, сославшись на официальную конфигурацию dumi.Сначала мы скопировали три файла на рисунке ниже в проект.

image.png

затем изменитьpreview-build.ymlсерединаbuild step:

- NODE_OPTIONS='--max-old-space-size=4096' yarn build
+ NODE_OPTIONS='--max-old-space-size=4096' PREVIEW_PR=true yarn build:dumi

добавить переменную окруженияPREVIEW_PR=trueЭто сделано для того, чтобы dumi распознал, что он не упакован в производственной среде, когда он упакован..umirc.tsЕго необходимо соответствующим образом изменить, чтобы:

const isProd =
  process.env.NODE_ENV === 'production' && process.env.PREVIEW_PR !== "true";
...
publicPath: isProd ? 'https://cdn.jsdelivr.net/gh/youngjuning/vant-react-native@gh-pages/': '/',
...

Затем изменитеpreview-deploy.ymlИмя домена развертывания в файлеdumi-previewдляvant-react-native-preview.

Наконец, мы можем добавить Surge Token, полученный ранее, к Секретам склада.

Дисплей достижений

Статус предварительного просмотра развертывания PR:

image.png

Статус успешного развертывания:

image.png

доступvant-react-native-preview-pr-1.surge.sh/Вы можете проверить правильность документа ✅.

модульный тест

я здесьМодульное тестирование React Native с помощью Jest и Enzyme | Технический обзорМодульный тест, представленный в этой статье, как и документация, является важной частью обеспечения качества наименьшего модуля программы. Это правда, что зрелая библиотека компонентов обязательно должна иметь модульные тесты. В этой главе речь пойдет не о модульном тестировании, а в основном о том, как vant-react-native настраивает модульное тестирование.

Установить зависимости

Зависимости jest, babel-jest и @types/jest были установлены.Что нам нужно установить, так это энзим, фреймворк модульного тестирования, основанный на jest.

$ yarn add enzyme jest-enzyme enzyme-adapter-react-16 enzyme-to-json @types/enzyme react-native-mock-render -DW

Enzyme — это утилита для тестирования JavaScript для React, которая упрощает тестирование вывода компонентов React. Вы также можете воздействовать на заданный вывод, перебирать и каким-то образом имитировать среду выполнения.

настроить

jest.config.js:

module.exports = {
  preset: 'react-native',
  verbose: true,
  collectCoverage: true, // 生成测试覆盖率报告
  moduleNameMapper: {
    // for https://github.com/facebook/jest/issues/919
    '^image![a-zA-Z0-9$_-]+$': 'GlobalImageStub',
    '^[@./a-zA-Z0-9$_-]+\\.(png|gif)$': 'RelativeImageStub',
  },
  setupFilesAfterEnv: ['<rootDir>/jest.setup.js'], // 使用 Jest 运行安装文件以配置 Enzyme 和适配器(如下文jest.setup.js中所示),之前是setupTestFrameworkScriptFile,也可以使用setupFiles
  snapshotSerializers: ['enzyme-to-json/serializer'], // 推荐使用序列化程序使用 enzyme-to-json,它的安装和使用非常简单,并允许您编写简洁的快照测试。
};

jest.setup.js:

import 'react-native';
import Enzyme from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';

Enzyme.configure({ adapter: new Adapter() });

Простой пример:

// packages/button/__test__/index.tsx
import React from 'react';
import { shallow } from 'enzyme';
import Button from '../src/index';

function setup(props = {}) {
  const wrapper = shallow(<Button />);
  const instance = wrapper.instance();
  return { wrapper, instance };
}

describe('Button Component', () => {
  it('renders correctly', () => {
    const { wrapper } = setup();
    expect(wrapper).toMatchSnapshot();
  });
});

воплощать в жизньjestПосле команды можно просмотреть покрытие следующим образом:

image.png

написать воинам

Те, кто может писать длинные эссе, — не воины, те, кто может настойчиво видеть здесь, — воины. Луо Чжу хотел бы поблагодарить вас за чтение. Однако разработка библиотеки компонентов — это только отправная точка.Если отзывы на эту статью будут положительными, Луожу расскажет о разработке и реализации конкретных компонентов библиотеки компонентов, о полном руководстве по модульному тестированию React Native и т. д. в последующих статьях.

Рекомендуемые библиотеки пользовательского интерфейса

Конечно, vant-react-native — не единственный ваш выбор.Следующие UI-библиотеки — отличные проекты. При реализации vant-react-native я также позаимствовал некоторые превосходные разработки своих предшественников.