предисловие
Пока вы пишете интерфейсный код, вы не можете избежать использования популярныхUI
библиотека компонентов, напримерElementUI
,Ant Design
.但是我们只停留在使用层面上的话,未免显得肤浅。因此作者一直都想编写一个属于自己的UI
Компонентная библиотека.
На самом деле, знания, участвующие в создании собственной библиотеки компонентов, все еще очень обширны:
- Уровень языка:
TypeScipt
,React
,Sass
; -
Jest
,React Testing Library
; - Фронтенд-инжиниринг: инфраструктура библиотеки компонентов, упаковка библиотеки компонентов, публикация библиотеки компонентов, публикация документации библиотеки компонентов.
Базовые знания, необходимые на уровне языка и уровне теста, не будут объясняться в этой статье из-за ограничений по объему, надеюсь, у вас уже есть определенная база.
Помимо написания общих компонентов, сложность создания библиотеки компонентов связана с инженерным аспектом. Поэтому в этой статье шаг за шагом объясняется, как создать и опубликовать библиотеку компонентов уровня предприятия в следующем порядке.
- Построение базовой библиотеки компонентов;
- Напиши
Button
компонент; - Документация библиотеки компонентов и отладка;
- Упаковать и опубликовать библиотеку компонентов;
- Электронная документация публикуется автоматически.
Базовая структура библиотеки компонентов
Структура каталогов
├──README.md // 文档说明
├──node_modules
├──package.json
├──tsconfig.json // ts 配置文件
├──.gitignore
└──src
├──components // 组件库
├──styles // 公用样式库
└──index.js // 组件库入口文件
Через структуру каталогов библиотеки компонентов вы обнаружите, что знакомыеApp.tsx
Ушли, действительно мы библиотека компонентов, а не приложение, так что нам это не нужно.
Просмотрите полный код в этой сводке
стилевые решения
использоватьcss
язык предварительной обработкиsass
,оsass
Для использования вы можете обратиться к его официальной документации, которая не будет здесь подробно объясняться.
Основная структура каталогов и анализ функций:
└──styles
├──_variables.scss // 各种变量以及可配置设置
├──_mixins.scss // 全局 mixins
├──_reboot.scss // 重置样式
├──_functions.scss // 全局 functions
└──index.scss // import styles 中所有样式
└──components
└──Button
└──style.scss // 组件独立样式
- форма
- кнопка
- Границы и тени
- ...
Определите файл переменных:_variables.scss
Преимущество определения переменных и констант заключается в том, что вся библиотека компонентов унифицирована, что будет проблематично при первом написании.Как только проект достигнет определенного размера, вы сможете ощутить его преимущества.
# 基础色彩系统
$blue: #0d6efd !default;
$indigo: #6610f2 !default;
...
# 字体系统
$font-family-sans-serif: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji" !default;
// 等宽字体
$font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace !default;
// 主要字体
$font-family-base: $font-family-sans-serif !default;
// 字体大小
$font-size-base: 1rem !default; // Assumes the browser default, typically `16px`
$font-size-lg: $font-size-base * 1.25 !default;
$font-size-sm: $font-size-base * .875 !default;
$font-size-root: null !default;
// 字重
$font-weight-lighter: lighter !default;
$font-weight-light: 300 !default;
...
// 行高
$line-height-base: 1.5 !default;
...
// 标题大小
$h1-font-size: $font-size-base * 2.5 !default;
...
!default
Что это такое?
Sass
при условии!default
логотип. Переменной присваивается значение только в том случае, если переменная не определена или ее значение пусто. В противном случае будет использовано существующее значение.
сброс стиля
использоватьnormalize.css
решение, егоgithub
адресGitHub.com/Колас/ни….
Что оно делает?
- со многими
CSS
сброс отличается, сохраняйте полезные значения по умолчанию; - Стандартизировать стили различных элементов;
- Исправление ошибок и распространенных несоответствий браузера;
- Улучшить удобство использования за счет незначительных модификаций;
- Примечание описано с использованием подробного кода действия.
_reboot.scss
body {
margin: 0; // 1
font-family: $font-family-base;
font-size: $font-size-base;
font-weight: $font-weight-base;
line-height: $line-height-base;
color: $body-color;
text-align: $body-text-align;
background-color: $body-bg; // 2
-webkit-text-size-adjust: 100%; // 3
-webkit-tap-highlight-color: rgba($black, 0); // 4
}
...省略
уже использован_variables
стиль импорта
index.scss
// config
@import "variables";
//layout
@import "reboot";
ранее определяемый как_variables
а также_reboot
, почему при импорте нет подчеркивания?
Если у вас есть файл Scss или Sass, который необходимо импортировать, но вы не хотите, чтобы он был скомпилирован в файл CSS, вы можете запретить его компиляцию, добавив подчеркивание к имени файла. Это укажет Sass не компилировать его в файл CSS. Затем вы можете импортировать файл как обычно, но также опустить подчеркивание перед именем файла.
src/index.tsx
Представляем файлы стиля
...
import './styles/index.scss';
import Button from "./components/Button";
ReactDOM.render(
<React.StrictMode>
<Button />
</React.StrictMode>,
document.getElementById('root')
);
Просмотрите полный код в этой сводке
Компонент кнопки
Анализ требований к компонентам
давайте сначала посмотримAnt Design
серединаButton
На что это похоже?
Ant Design
использовать в
<Button type="primary">Primary Button</Button>
<Button type="primary" disabled>Primary(disabled)</Button>
...
Кратко резюмируем:
- согласно с
type
Делить:Primary
,Default
,Danger
,Link
; - согласно с
size
Делить:Normal
,Small
,Large
; - согласно с
disabled
Статус: Отключено, Нормально.
Спрос - это простой анализ здесь, давайте начнем кодировать.
компонентное кодирование
пишуButton
Прежде чем впервые представить полезный инструментclassnames
, который является простым в использованииJavaScript
библиотека, посредством условного суждения будетclassName
Сшейте вместе.
Установитьclassnames
yarn add classnames @types/classnames
использоватьclassnames
classNames('foo', 'bar'); // => 'foo bar'
classNames('foo', { bar: true }); // => 'foo bar'
classNames({ 'foo-bar': true }); // => 'foo-bar'
classNames({ 'foo-bar': false }); // => ''
classNames({ foo: true }, { bar: true }); // => 'foo bar'
classNames({ foo: true, bar: true }); // => 'foo bar'
// lots of arguments of various types
classNames('foo', { bar: true, duck: false }, 'baz', { quux: true }); // => 'foo bar baz quux'
// other falsy values are just ignored
classNames(null, false, 'bar', undefined, 0, 1, { baz: null }, ''); // => 'bar 1'
После введения инструмента мы официально начнем, потому что технический отбор используетTypeScript
, поэтому мы можем сначала определить соответствующие типы в соответствии с приведенным выше анализом спроса:
// 定义按钮大小类型
export type ButtonSize = 'lg' | 'sm';
// 定义按钮type种类
export type ButtonType = 'primary' | 'default' | 'danger' | 'link'
// 定义Button组件基础入参属性
interface BaseButtonProps {
className: string;
disabled: boolean;
size: ButtonSize;
btnType: ButtonType;
children: React.ReactNode;
href: string;
}
// 定义按钮的基础类型与原生按钮的联合类型
type NativeButtonProps = BaseButtonProps & React.ButtonHTMLAttributes<HTMLElement>;
// 定义按钮的基础类型与原生A标签的联合类型
type AnchorButtonProps = BaseButtonProps & React.AnchorHTMLAttributes<HTMLElement>;
// 执行 Partial,相当于所有属性都变为可选,如 {disabled?:boolean,...}.
export type ButtonProps = Partial<NativeButtonProps & AnchorButtonProps>;
Тип определен, следующая реализацияButton
Основные функции компонента:
src/components/Button/Button.tsx
const Button: React.FC<ButtonProps> = (props)=>{
// 通过 ES6 对象的解构赋值取出所有属性,其中restProps就是除显示定义的剩下所有的属性。
const {
btnType,
disabled,
size,
children,
className,
href,
...restProps //ES6 rest 语法
} = props;
// 利用 classNames 判断按钮的相应 class 值。
const classes = classNames('btn', className, {
[`btn-${btnType}`]: btnType, // btnType 参数存在时则动态添加 `btn-${btnType}` 类
[`btn-${size}`]: size, // size 参数存在时动态添加 `btn-${size}` 类
'disabled': (btnType === 'link') && disabled // 由于 a 链接原生不带有 disabled 属性,因此需要手动给它添加一个 disabled 类。通过编写类的样式实现disabled效果
})
// 判断如果是 link 类型,则输出 a 链接,否则输出 button。
if(btnType === 'link' && href){
return (
<a
{...restProps}
href={href}
className={classes}
>
{children}
</a>
)
} else {
return (
<button
{...restProps}
className={classes}
disabled={disabled}
>
{children}
</button>
)
}
}
// 属性默认值
Button.defaultProps = {
disabled: false,
btnType: 'default'
}
Button/_styles.scss
.btn {
position: relative;
display: inline-block;
font-weight: $btn-font-weight;
line-height: $btn-line-height;
color: $body-color;
white-space: nowrap;
text-align: center;
vertical-align: middle;
background-image: none;
border: $btn-border-width solid transparent;
@include button-size( $btn-padding-y, $btn-padding-x, $btn-font-size, $border-radius);
box-shadow: $btn-box-shadow;
cursor: pointer;
transition: $btn-transition;
&.disabled,
&[disabled] {
cursor: not-allowed;
opacity: $btn-disabled-opacity;
box-shadow: none;
> * {
pointer-events: none; // 清除鼠标事件
}
}
}
.btn-lg {
@include button-size($btn-padding-y-lg, $btn-padding-x-lg, $btn-font-size-lg, $btn-border-radius-lg);
}
... 省略
Помимо использования переменных в стиле,mixin
Ждатьsass
Другие заклинания высокого порядкаCSS
Без разницы, главное не забытьindex.scss
Представлен в:
// button
@import "../components/Button/styles";
оButton
Бизнес-код компонента почти написан здесь, но у нас еще есть очень важная вещь, то есть модульное тестирование, Поскольку это общий компонент, он очень подходит для проверки того, соответствуют ли его функции ожиданиям посредством модульного тестирования.
тестовый код компонента
в настоящее время более популярныReact
Решение для модульного тестированияJEST
а такжеReact Testing Library
. Поскольку юнит-тестирование — очень обширная тема, в этой статье мы не намерены подробно ее освещать.
JEST
Jest
ЯвляетсяJavaScript
Платформа тестирования, разработанная для того, чтобы гарантировать, что любойJavaScript
правильность кода. Он позволяет использовать доступные, знакомые и многофункциональныеAPI
Приходите и пишите тесты, чтобы быстро получить результаты.
Функции:
- Нулевая конфигурация:
Jest
Цель состоит в том, чтобы быть в большинствеJavaScript
Его можно использовать из коробки на проекте без настройки. - Снимки: сборки могут легко отслеживать большие
Object
Тестовое задание. Снимки могут быть независимыми от тестового кода, а также могут быть интегрированы во внутренние строки кода. - Изолированный: программа тестирования работает параллельно в своем собственном процессе, чтобы максимизировать производительность.
- начальство
api
:отit
прибытьexpect
,Jest
Соберите весь комплект в одном месте, легко писать, легко обслуживать, очень удобно. - Быстро и безопасно: гарантируя, что ваши тесты имеют уникальное глобальное состояние,
Jest
Тесты можно надежно запускать параллельно. Чтобы ускорить процесс тестирования,Jest
Сначала запускает предыдущий неудачный тест и реорганизуйте тест в соответствии с тестовым файлом. -
--coverage
Jest
Весь проект может быть собран из информации о покрытии кода, включая непроверенные документы.
React Testing Library
import React from 'react'
import { render, fireEvent } from '@testing-library/react'
import Button, { ButtonProps } from './Button'
const defaultProps = {
onClick: jest.fn()
}
describe('test Button component', () => {
it('should render the correct default button', () => {
// wrapper 获取到通过render方法解析的React组件实例信息
const wrapper = render(<Button {...defaultProps}>Nice</Button>)
// element 则是通过 text 值获取到的类似“DOM”
const element = wrapper.getByText('Nice') as HTMLButtonElement
// 通过 JEST 框架可以做一系列断言
expect(element).toBeInTheDocument()
expect(element.tagName).toEqual('BUTTON')
expect(element).toHaveClass('btn btn-default')
expect(element.disabled).toBeFalsy()
// 触发元素click事件
fireEvent.click(element)
// 断言模拟事件被触发
expect(defaultProps.onClick).toHaveBeenCalled()
})
})
Из этого простого примера можно увидеть, что он фактически моделирует траекторию использования пользователя: «Получение определенных результатов, делая что-то».
На данный момент, один и функция, стиль, тестButton
Компоненты все готовы.
Просмотрите полный код в этой сводке
Документация библиотеки компонентов и отладка
Хотя мы только разработалиButton
компонент, но есть проблема, которая нас беспокоит, то есть сложность отладки и компонент написан, и нет соответствующего вывода документа.Если вы хотите использоватьButton
Компоненты должны смотреть исходный код. Очевидно, что это очень неразумно, нам нужно использовать инструменты, которые помогут нам реализовать функцию документа.
Storybook
Storybook — это инструмент с открытым исходным кодом для изолированной разработки компонентов пользовательского интерфейса для React, Vue, Angular и т. д.
Функции:
- Предоставляет песочницу для изолированного строительства
UI
компонент; - Предоставляет некоторые расширенные функции плагина для более быстрой сборки
UI
, документирование библиотек компонентов и оптимизация рабочих процессов; -
Storybook
Позволяет нам легко включать техническую документацию в нашу систему проектирования, что еще больше упрощает разработку компонентов.
Установить:
npx -p @storybook/cli sb init
或者
yarn global @storybook/cli && sb init
запускать:
npm run storybook
Добавьте файл:Button/Button.stories.tsx
import React from 'react';
import { Story, Meta } from '@storybook/react/types-6-0';
import Button , { ButtonProps } from './Button';
import "../../styles/index.scss";
export default {
title: 'Button',
component: Button,
} as Meta;
const Template: Story<ButtonProps> = (args) => <Button {...args} />;
export const Primary = Template.bind({});
Primary.args = {
btnType: 'danger',
children: "确定"
};
Конечный результат документа:
Используется только здесьstorybook
Готовая функция, ее функция очень мощная, заинтересованные студенты могут читатьОфициальная документация сборника рассказов.
Просмотрите полный код в этой сводке
Библиотека компонентов упакована и выпущена
Пакет
React App
Процесс упаковки, поindex.tsx
файл является записью,App
Пакет корневого компонента и, наконец, генерировать разделJavaScript
Скрипт, динамически вставляемый на страницуDOM
и стили для формирования полной страницы.
Библиотека компонентов не является приложением, очевидно, что ее нельзя так упаковать, давайте посмотримant design
Как компоненты используются приложением:
import {Button} from "antd";
Это также означает, что входной файл нашей библиотеки компонентов должен использоваться как внешний поставщик всех компонентов.
в стадии трансформацииindex.tsx
export { default as Button } from "./components/Button";
Конечно, здесь мы просто пишемButton
Компоненты, если их несколько, их можно экспортировать с одинаковым синтаксисом.
Как мы все знаем,webpack
является упаковщиком модулей, он проанализирует зависимости в модуле и сделает большойJavaScript
package, однако наша библиотека компонентов может бытьESModule
Путь модуля предоставляется непосредственно пользователю. Так что нам просто нужно положитьTypeScript
ES5
Грамматика может быть. Итак, когда пакет помощиts
создать новыйtsconfig.build.json
Конфигурация в виде файла пакета:
{
"compilerOptions": {
"outDir": "dist", // 打包输出位置
"module": "esnext", // 设置生成代码的模块标准,可以设置为 CommonJS、AMD 和 UMD 等等。
"target": "es5", // 目标语言的版本
"declaration": true, // 生成声明文件,记得 inde.d.ts
"jsx": "react", // 等效 React.createElement调用
"moduleResolution":"Node", // 模块解析策略,这里提供两种解析策略 node 和 classic,ts 默认使用 node 解析策略。
"allowSyntheticDefaultImports": true, // 允许对不包含默认导出的模块使用默认导入。这个选项不会影响生成的代码,只会影响类型检查。
"skipLibCheck": true // 跳过类库检查
},
"include": [
"src" // 编译文件夹
],
"exclude": [ // 排除编译文件
"src/**/*.test.tsx",
"src/**/*.stories.tsx",
"src/setupTests.ts",
"stories/**/*.svg",
"stories/**/*.mdx"
]
}
package.json
Увеличиватьscripts
// 清除文件命令,需要手动安装rimraf包
"clean": "rimraf ./dist",
// 通过 tsconfig.build.json 配置编译 ts 文件
"build-ts": "tsc -p tsconfig.build.json",
// 编译sass文件
"build-css": "node-sass ./src/styles/index.scss ./dist/index.css",
// 总的编译命令,继发执行
"build": "npm run clean && npm run build-css && npm run build-ts"
воплощать в жизньnpm run build
Команда, смотриdist
Файлы, созданные в каталоге:
Задача упаковки завершена,Просмотрите полный код в этой сводке.
выпускать
После того, как упаковка завершена, нам нужно опубликовать пакет в Интернете.npm
по управлению пакетами. Итак, нам нужно сначала войти и зарегистрироватьсяnpm
.
# 查看是否登录
npm whoami
# 查看 npm 配置表
npm config ls
# 注册
npm adduser
Username:shiyou
Email:(xxx@xxx.com)
# 登录
npm login
然后填写注册信息即可
package.json
оптимизировать
{
"name": "lion-design", // 包名,必须要是唯一的
"version": "1.0.1", // 版本号
"author": "Lion", // 作者
"private": false, // 非私有
"main": "dist/index.js", // 项目的入口文件
"module": "dist/index.js", // 指向具有 ES2015 模块语法的模块,但仅指向目标环境支持的语法功能。
"types": "dist/index.d.ts", // 只在 TypeScript 中生效的字段,指向声明文件
"license": "MIT", // 使用许可证
"homepage": "https://github.com/shiyou00/lion-design", // 是包的项目主页或者文档首页。
"repository": { // 代码托管的位置
"type": "git",
"url": "https://github.com/shiyou00/lion-design"
},
"files": [ // 项目上传至npm服务器的文件,可以是单独的文件、整个文件夹,或者通配符匹配到的文件。
"dist"
],
"scripts": {
"clean": "rimraf ./dist",
"build-ts": "tsc -p tsconfig.build.json",
"build-css": "node-sass ./src/styles/index.scss ./dist/index.css",
"build": "npm run clean && npm run build-css && npm run build-ts",
"prepublishOnly": "npm run build" // 执行 npm publish 之前会默认执行的命令
},
// 开发版和发布版需要的依赖
"dependencies": {
"@testing-library/jest-dom": "^5.11.4",
"@testing-library/react": "^11.1.0",
"@testing-library/user-event": "^12.1.10",
"classnames": "^2.2.6",
},
// 开发环境依赖包
"devDependencies": {
"@storybook/addon-actions": "^6.1.10",
"@storybook/addon-essentials": "^6.1.10",
"@storybook/addon-links": "^6.1.10",
"@storybook/node-logger": "^6.1.10",
"@storybook/preset-create-react-app": "^3.1.5",
"@storybook/react": "^6.1.10",
"@types/classnames": "^2.2.11",
"@types/jest": "^26.0.15",
"@types/node": "^12.0.0",
"@types/react": "^16.9.53",
"@types/react-dom": "^16.9.8",
"rimraf": "^3.0.2",
"node-sass": "^4.13.0",
"react-scripts": "4.0.1",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"typescript": "^4.0.3"
},
// 说明还需要依赖的版本,但是用户不进行强行安装,以免造成版本冲突
"peerDependencies": {
"react": ">=16.8.0",
"react-dom": ">=16.8.0"
},
"resolutions": {
"@storybook/react/babel-loader": "8.1.0" // 允许您覆盖特定嵌套依赖项的版本。解决 storybook 与 react-scripts 中 babel 依赖冲突
}
}
воплощать в жизньnpm publish
Отправьте пакет, нажмите, чтобы просмотреть после успешной отправки пакетаLion-Conslance Адрес линии.
发布成功后我们就可以在项目中安装使用自己编写的UI
Button
Компоненты.
использовать
# 新建一个项目
create-react-app lion-design-test
# 安装包
npm install lion-design
# index.js 中引入css文件
import "lion-design/dist/index.css";
# App.js 中使用
import { Button } from "lion-design"
<Button btnType="primary">确定</Button>
Изучив это, каждый получит общее представление о написании и использовании библиотеки компонентов. Как упоминалось выше, нам также нужна онлайн-документация для пользователей.
Автоматическая публикация онлайн-документов
знакомыйgithub
друзья должны знатьGitHub Pages, он может разместитьgithub
Страница проекта.
Теперь, что я хочу сделать, когда я изменяю документ в проекте, просто выполняюgit push
После этого онлайн-документация автоматически обновляется. Как это сделать автоматически? А вот и важная концепция разработки программного обеспечения.CI/CD
.
CI/CD
-
CI
Непрерывная интеграция, частая интеграция кода в транкmain
Ветвление, его цель - позволить продукту быстро повторяться, сохраняя при этом качество. -
CD
Непрерывная доставка, непрерывное развертывание, частая доставка новых версий программного обеспечения группе обеспечения качества или пользователям и автоматическое развертывание в рабочей среде после проверки кода.
После краткого понимания концепции, есть онлайнCI/CD
ПлатформаTravis, что может помочь нам выполнить эту серию задач автоматизации.
Travis
1. Используйтеgithub
Разрешите войти и зарегистрироваться.
2. будет.travis.yml
Добавьте файл в корневой каталог проекта (пожалуйста, удалите закомментированный код)
language: node_js // 部署时使用 node_js 语言
node_js:
- "stable" // node.js 版本
cache:
directories:
- node_modules // node_modules 设置缓存
env:
- CI=true // 设置环境变量
script:
- npm run build-storybook // 执行的脚本命令,这里执行的是构建线上文档
deploy: // 自动部署github pages 的配置
provider: pages
skip_cleanup: true
github_token: $github_token
local_dir: storybook-static // 需部署的文件夹
on:
branch: main // 基于main分支进行部署
первый вgithub
В продаже, затем вtravis
[Уведомление]github_token
то естьtravis
После завершения настройки выполнитеgit push
Эти автоматизированные процессы запускаются, и в конечном итоге документация библиотеки компонентов обновляется.
travis
Анализ рабочего процесса:
- Получать
git
Последний код для соответствующей ветки (Main). - Используйте настроенный
node.js
исполнение версииyarn install
команда (если в проекте естьyarn.lock
) генерироватьnode_modules
. - правильно
node_modules
кешировать. -
npm run build-storybook
Команда генерировать документы библиотеки компонентов. -
deploy
github pages
Об использовании развертыванияtoken
Токенgithub_token
, Развертывание контентаstorybook-static
папка.
Этоtravis
Автоматизируйте процесс развертывания. Конечно, его возможности гораздо больше.
Ассортимент библиотеки компонентов Онлайн адрес документа
Просмотрите полный код в этой сводке
резюме
В этой статье шаг за шагом создается базовая структура библиотеки компонентов и пишетсяButton
Компонент как простой каркас библиотеки компонентов, опубликованный в Интернете.npm
package, а также публикует онлайн-документацию для библиотеки компонентов.
Благодаря изучению и саморазвитию этой статьи я считаю, что вы, по крайней мере, создали и выпустили библиотеку компонентов. Так что просто сделать это точно недостаточно.Ввиду ограниченности места автор напишет его в двух частях.Основой второй части является некоторое обдумывание и написание самого общего компонента.
如果喜欢本文,请点个赞吧! ! !