Recently, I have been trying to build a React component library. As for why this idea came about, it is mainly because the component library is extremely important to the front-end ecology. Every Internet company that focuses on long-term development and values development efficiency will basically tailor it. Needless to say, the benefits of customizing your own component library. For front-end engineers, understanding and mastering it will allow us to have a special skill in our future work and application process, and it is also very beneficial to our own vertical development. The following is my record of my process of building a component library.
Initialize the project
The construction project does not intend to use create-react-app
scaffolding to build, because the scaffolding encapsulates a lot of things, and some things are not applicable to the component library, which is too bloated to build the component library, so I don't plan to use any scaffolding to build the project.
First, create a project folder pony-react-ui
, and execute the following commands in this folder:
npm init // 生成package.json
tsc --init // 生成tsconfig.json
Then, initialize the project according to the following directory structure:
pony-react-ui
├── src
├── assets
├── components
├── Button
├── Button.tsx
└── index.ts
└── Dialog
├── Dialog.tsx
└── index.ts
├── styles
├── _button.scss
├── _dialog.scss
├── _mixins.scss
├── _variables.scss
└── pony.scss
└── index.ts // 打包的入口文件,引入pony.scss,抛出每一个组件
├── index.js // 主文件入口,package.json中main字段指定的文件
├── package.json
├── tsconfig.json // 指定了用来编译这个项目的根文件和编译选项
├── webpack.config.js
└── README.md
Write a Button component
Button
component should meet the following requirements:
- Different sizes
- Different Types
- Different colors
- Disabled state
- Click event
Button.tsx
import React from 'react';
import classNames from 'classnames';
export interface IButtonProps {
onClick?: React.MouseEventHandler;
// 类型
primary?: boolean;
secondary?: boolean;
outline?: boolean;
dashed?: boolean;
link?: boolean;
text?: boolean;
// 尺寸
xLarge?: boolean;
large?: boolean;
small?: boolean;
xSmall?: boolean;
xxSmall?: boolean;
// 颜色
success?: boolean;
warn?: boolean;
danger?: boolean;
// 禁用状态
disabled?: boolean;
className?: string;
style?: React.CSSProperties;
children?: React.ReactNode;
}
export const Button = (props: IButtonProps) => {
const {
className: tempClassName,
style,
onClick,
children,
primary,
secondary,
outline,
dashed,
link,
text,
xLarge,
large,
small,
xSmall,
xxSmall,
success,
danger,
warn,
disabled,
} = props;
const className = classNames(
{
'pony-button': true,
'pony-button-primary': primary,
'pony-button-secondary': secondary && !text,
'pony-button-outline': outline,
'pony-button-dashed': dashed,
'pony-button-link': link,
'pony-button-text': text && !secondary,
'pony-button-text-secondary': secondary && text,
'pony-button-round': round,
'pony-button-rectangle': noRadius,
'pony-button-fat': fat,
'pony-button-xl': xLarge,
'pony-button-lg': large,
'pony-button-sm': small,
'pony-button-xs': xSmall,
'pony-button-xxs': xxSmall,
'pony-button-long': long,
'pony-button-short': short,
'pony-button-success': success,
'pony-button-warn': warn,
'pony-button-danger': danger,
'pony-button-disabled': disabled,
},
tempClassName
);
return (
<button
type="button"
className={className}
style={style}
onClick={onClick}
disabled={disabled}>
<span className="pony-button__content">{children}</span>
</button>
);
}
Throw Button
components and defined types in the Button/index.ts
export * from './Button';
In this way, an example of assembly basically completed, some students will certainly be such a doubt, why Button.tsx
without introducing its style file _button.scss
, but the introduction of global style when used alone or introducing _button.scss
it?
// 单独引入组件样式
import { Button } from 'pony-react-ui';
import 'pony-react-ui/lib/styles/button.scss';
// 全局引入组件样式,打包时抽离出来的样式
import 'pony-react-ui/lib/styles/index.scss';
This is related to the style weight. The style weight introduced import
JSX
in className
, so the internal style can be modified outside the component.
To give an example:
import { Button } from 'pony-react-ui';
import 'pony-react-ui/lib/styles/button.scss';
import styles from './index.module.scss';
const Demo = () => (
<div className={styles.btnBox}>
<Button onClick={submit}>submit</Button>
</div>
)
The imported Button.scss
and local index.module.scss
in the <style></style>
tag after packaging, and the order is:
<style type="text/css">
// Button.scss的样式
</style>
<style type="text/css">
// index.module.scss的样式
</style>
Thus, index.module.scss
style weights is above Button.scss
in style, can index.module.scss
modification Button.scss
style
Writing style
├── styles
├── _button.scss
├── _dialog.scss
├── _mixins.scss
├── _variables.scss
└── pony.scss
I store all style files under the style
_button.scss
and _dialog.scss
belong to the component style files. _mixins.scss
used to store mixin
instructions to improve the reuse of style logic.
// _mixins.scss
@mixin colors($text, $border, $background) {
color: $text;
background-color: $background;
border-color: $border;
}
// 设置按钮大小
@mixin button-size($padding-x, $height, $font-size) {
height: $height;
padding: 0 $padding-x;
font-size: $font-size;
line-height: ($height - 2);
}
For example, use _button.scss
$values: #ff0000, #00ff00, #0000ff;
.primary {
@include colors($values...);
}
node-sass
will compile it into
.primary {
color: #ff0000;
background-color: #00ff00;
border-color: #0000ff;
}
_variables.scss
used to store some style constants, such as defining the font size of buttons of different sizes:
$button-font-size: 14px !default;
$button-xl-font-size: 16px !default;
$button-lg-font-size: 16px !default;
$button-sm-font-size: 12px !default;
pony.scss
will import all style files. _mixins.scss
and _variables.scss
need to be imported first, because subsequent component style files may depend on them
@import 'variables';
@import 'mixins';
@import 'button';
@import 'dialog';
...
When constructing and processing the style file, I did not use css modules
to avoid the same style name, but used BEM
standard writing style to avoid this problem. Why should I do this?
rules: [
{
test: /\.(sa|sc|c)ss$/,
use: [
loader: 'css-loader',
options: {
modules: false // 禁止css modules
}
]
}
]
css modules
used, the internal style of the component cannot be modified from outside the component. Usually, to modify the component style from the outside is generally written like this:
<Button className="btn">按钮</Button>
// 修改Button内部样式,假如组件内部样式有个样式类名为pony-button-promary
.btn {
:global {
.pony-button-promary {
color: #da2227;
}
}
}
However, css modules
pony-button-promary
will be a string of hash
values after the 060dd8ad712911 class name, and every time the Button
component is modified, the generated hash
will be different, which will cause the class name
.btn {
:global {
// 下次修改Button组件构建后,生成的hash不一定为sadf6756
.pony-button-promary-sadf6756 {
color: #da2227;
}
}
}
Construct
Package entry file
src/index.ts
builds the entry file for webpack
import './styles/pony.scss';
export * from './components/Button';
export * from './components/Dialog';
Global style files will be introduced here, MiniCssExtractPlugin
will extract and compress the styles during construction, and then separate and output JS scripts and CSS scripts
Packaged output UMD specification
Before building, we must clarify the usage scenarios of the component library. Now it is common to es module
and CommonJS
. In some scenes, <script>
will be directly introduced in HTML
. In some very few scenes, AMD(require.js
) and CMD(sea.js)
introduced. As a component library, it should be compatible with these usage scenarios. The component library should remain neutral and should not be limited to a certain way of use.
In order to support multiple usage scenarios, we need to choose a suitable packaging format. webpack
provides a variety of packaging output methods, as follows:
MyLibrary
is the variable name defined output.library
libraryTarget: 'var'
: Whenlibrary
loaded, inlet origin return value assigned to a variablevar MyLibrary = _entry_return_; // 在一个单独的 script... MyLibrary.doSomething();
libraryTarget: 'this'
: entrance starting point of the return value assigned to thethis
a property,this
meaning depends on youthis['MyLibrary'] = _entry_return_; // 在一个单独的 script... this.MyLibrary.doSomething(); MyLibrary.doSomething(); // 如果 this 是 window
libraryTarget: 'window'
: The return value of the assigned to this attribute of thewindow
window['MyLibrary'] = _entry_return_; window.MyLibrary.doSomething();
libraryTarget: 'global'
: The return value of the assigned to this attribute of theglobal
global['MyLibrary'] = _entry_return_; global.MyLibrary.doSomething();
libraryTarget: 'commonjs'
: The return value assigned to theexports
object. This name also means that the module is used in theCommonJS
environmentexports['MyLibrary'] = _entry_return_; require('MyLibrary').doSomething();
libraryTarget: 'module'
: outputES
module, it should be noted that this function is not yet fully supportedlibraryTarget: 'commonjs2'
: The return value entry point will be assigned to themodule.exports
object. This name also means that the module is used in theCommonJS
environmentmodule.exports = _entry_return_; require('MyLibrary').doSomething();
libraryTarget: 'amd'
:library
as aAMD
module.AMD
module requires the entrychunk
(for example, the first script loaded with tags) to be defined by specific attributes, such asdefine
andrequire
, which are usually provided byRequireJS
or any compatible module loader (such asalmond
). Otherwise, directly loading the generatedAMD bundle
will cause an error, such asdefine is not defined
module.exports = { //... output: { library: 'MyLibrary', libraryTarget: 'amd', }, };
The generated
output
name will be defined as"MyLibrary"
:define('MyLibrary', [], function () { return _entry_return_; });
You can import
bundle
script
tag, and you can callbundle
like this:require(['MyLibrary'], function (MyLibrary) { // Do something with the library... });
If
output.library
not defined, the following will be generated.define([], function () { return _entry_return_; });
libraryTarget: 'umd'
:library
as a way to run under all module definitions. It willCommonJS, AMD
environment, or export the module toglobal
under 060dd8ad71324fmodule.exports = { //... output: { library: 'MyLibrary', libraryTarget: 'umd', }, };
The final output is:
(function webpackUniversalModuleDefinition(root, factory) { if (typeof exports === 'object' && typeof module === 'object') module.exports = factory(); else if (typeof define === 'function' && define.amd) define([], factory); else if (typeof exports === 'object') exports['MyLibrary'] = factory(); else root['MyLibrary'] = factory(); })(typeof self !== 'undefined' ? self : this, function () { return _entry_return_; });
According to the above description, the libraryTarget="umd"
provided umd
packed format. webpack
processing script, style and font file is as follows:
const path = require('path');
const webpack = require('webpack');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
// const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const TerserJSPlugin = require('terser-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
// const SpeedMeasurePlugin = require('speed-measure-webpack-plugin')
// const { CleanWebpackPlugin } = require('clean-webpack-plugin');
// const LoadablePlugin = require('@loadable/webpack-plugin')
// const smp = new SpeedMeasurePlugin() // 测量构建速度
const devMode = process.env.NODE_ENV !== 'production';
const pkg = require('./package.json');
module.exports = ({
mode: devMode ? 'development' : 'production',
devtool: devMode ? 'inline-source-map' : 'hidden-source-map',
entry: path.resolve(__dirname, './src/index.ts'),
output: {
path: path.resolve(__dirname, './dist'),
filename: devMode ? 'pony.js' : 'pony.min.js',
library: 'pony',
libraryTarget: 'umd'
},
resolve: {
// Add `.ts` and `.tsx` as a resolvable extension.
extensions: ['.ts', '.tsx', '.js'],
alias: {
}
},
module: {
rules: [
// all files with a `.ts` or `.tsx` extension will be handled by `ts-loader`
{
test: /\.tsx?$/,
use: [
'babel-loader?cacheDirectory',
{
loader: 'ts-loader',
options: {
configFile: 'tsconfig.json'
}
}
]
},
{
test: /\.(sa|sc|c)ss$/,
use: [
{
loader: MiniCssExtractPlugin.loader // 抽取样式文件,将css样式文件用link标签引入,使用此loader就不需要用style-loader,即使用了也不会有效果
},
{
loader: 'css-loader',
options: {
modules: {
auto: true,
localIdentName: '[path][name]__[local]'
},
importLoaders: 2, // 一个css中引入了另一个css,也会执行之前两个loader,即postcss-loader和sass-loader
}
},
{
// 使用 postcss 为 css 加上浏览器前缀
loader: 'postcss-loader',
options: {
// options has an unknown property 'plugins';
postcssOptions: {
// PostCSS plugin autoprefixer requires PostCSS 8.将autoprefixer降到8.0.0版本
plugins: [require('autoprefixer')]
}
}
},
{
loader: 'sass-loader' // 使用 sass-loader 将 scss 转为 css
}
]
},
{
test: /(\.(eot|ttf|woff|woff2)|font)$/,
loader: 'file-loader',
options: { outputPath: 'fonts/' }
},
{
test: /\.(png|jpg|gif|svg|jpeg)$/,
loader: 'file-loader',
options: { outputPath: 'images/' }
}
]
},
plugins: [
// new CleanWebpackPlugin(),
// new LoadablePlugin(),
// 该插件能够使得指定目录被忽略,从而使得打包变快,文件变小;下面忽略了包含’./locale/'该字段路径的文件目录,但是也使得我们使用的时候不能显示中文语言了,所以这个时候可以手动引入中文语言的目录
new webpack.IgnorePlugin(/\.\/locale/, /moment/),
// 主要用于对打包好的js文件的最开始处添加版权声明
new webpack.BannerPlugin(`pony ${pkg.version}`),
// 将CSS提取到单独的文件中
new MiniCssExtractPlugin({
// Options similar to the same options in webpackOptions.output
// both options are optional
filename: devMode ? 'pony.css' : 'pony.min.css',
chunkFilename: '[id].css'
})
// devMode ? new webpack.HotModuleReplacementPlugin() : null
],
optimization: {
minimizer: devMode
? []
: [
// 压缩js代码
// new UglifyJsPlugin({
// cache: true, // 启用文件缓存并设置缓存目录的路径
// parallel: true, // 使用多进程并行运行
// sourceMap: true // set to true if you want JS source maps
// }),
// webpack v5 使用内置的TerserJSPlugin替代UglifyJsPlugin,因为UglifyJsPlugin不支持ES6
new TerserJSPlugin({
// cache: true, // 启用文件缓存并设置缓存目录的路径
parallel: true, // 使用多进程并行运行
// sourceMap: true // set to true if you want JS source maps
}),
// 用于优化或者压缩CSS资源
new OptimizeCSSAssetsPlugin({
assetNameRegExp: /\.css$/g,
cssProcessor: require('cssnano'), // 用于优化\最小化 CSS 的 CSS 处理器,默认为 cssnano
cssProcessorOptions: { safe: true, discardComments: { removeAll: true } }, // 传递给 cssProcesso
canPrint: true // 布尔值,指示插件是否可以将消息打印到控制台,默认为 true
})
],
sideEffects: false
}
});
Here is an explanation of the above configuration:
- After converting
scss
tocss
, usepostcss
to do some processing on the style, and use theautoprefixer
plug-in to add the browser prefix to the style to prevent compatibility problems with some styles - Use
webpack
built-in plug-inBannerPlugin
to add the version number to the beginning of the built file - Engineering using
webpack 5.x
version to build, using the built-TerserJSPlugin
ofJS
compressed; style compression usingOptimizeCSSAssetsPlugin
plug. In addition, in order to generate compressed and uncompressed versions, thecross-env
plug-in is used to inject environment variables when executing the build command to control whether to compress and optimize.
Configure the build command as follows:
"scripts": {
"build:dev": "cross-env NODE_ENV=development webpack",
"build:prod": "cross-env NODE_ENV=production webpack",
"build": "npm run build:prod && npm run build:dev"
},
When yarn build
is executed, the compressed and uncompressed scripts will be generated dist
src/index.js
is the entry program that points to the module. In the development environment, the introduction is dist/pony.js
, and the introduction in the production environment is dist/pony.min.js
if (!process.env.NODE_ENV || process.env.NODE_ENV === 'development') {
module.exports = require('./dist/pony.js');
} else {
module.exports = require('./dist/pony.min.js');
}
Package output es module specification
webpack 5.x
not yet fully supported the es module
packaging method and is in the laboratory trial stage. tsc
compiler can get this done, simply press the compiler options are configured, the main module
property "ES6"
or "ES2015"
// tsconfig.json
{
"compilerOptions": {
"target": "es2015", // 指定ECMAScript目标版本 "ES3"(默认), "ES5", "ES6"/ "ES2015", "ES2016", "ES2017"或 "ESNext"
"lib": [ // 编译过程中需要引入的库文件的列表
"dom",
"esnext"
],
"module": "es2015", // 指定生成哪个模块系统代码:"None", "CommonJS", "AMD", "System", "UMD", "ES6"或 "ES2015"
"allowJs": true, // 指定是否允许编译JS文件,默认false,即不编译JS文件
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": false, // 不生成输出文件
"jsx": "react", // 在 .tsx文件里支持JSX
"newLine": "lf", // 当生成文件时指定行结束符: "crlf"(windows)或 "lf"(unix)
},
"include": [
"src/**/*.ts",
"src/**/*.tsx"
],
"exclude": [
"node_modules"
]
}
In the typescript
project, the type declaration file needs to be generated. I created a new tsconfig.build.json
in the root directory and inherited the configuration in tsconfig.json
// tsconfig.build.json
{
"extends": "./tsconfig",
"compilerOptions": {
"declaration": true, // 指定是否在编译的时候生成相应的d.ts声明文件,如果设为true,编译每个ts文件之后会生成一个js文件和一个声明文件,但是declaration和allowJs不能同时设为true
"declarationMap": false, // 指定编译时是否生成.map文件
"sourceMap": true, // 编译时是否生成.map文件
},
"include": [
"src/**/*.ts",
"src/**/*.tsx"
],
"exclude": [
"node_modules"
]
}
scripts
in 060dd8ad713741 command
"scripts": {
"build:dev": "cross-env NODE_ENV=development webpack",
"build:prod": "cross-env NODE_ENV=production webpack",
// 生成es module编译命令
"build:tsc": "tsc -p tsconfig.build.json --target ES5 --outDir lib",
"build": "npm run build:prod && npm run build:dev"
},
When executing yarn build:tsc
will compile and generate es module
specification script, as shown below
Some students will definitely ask why not directly add compiler options to tsconfig.json
Remember the configuration items for compiling tsx
module: {
rules: [
// all files with a `.ts` or `.tsx` extension will be handled by `ts-loader`
{
test: /\.tsx?$/,
use: [
'babel-loader?cacheDirectory',
{
loader: 'ts-loader',
options: {
configFile: 'tsconfig.json'
}
}
]
},
]
}
When using webpack
build the umd
standard script, there is no need to generate the type declaration file. When tsc
compiles and generates the es module
standard script, it needs to be generated, so the distinction is made.
While generating the es module specification script, it also achieves the on-demand loading of components, because on-demand loading depends on the es module
only generated the es module specification Js script and type declaration file, and did not deal with style files and static resource files. How to deal with this part?
I didn't do any special treatment on it, just put the assets、styles
directory and its subdirectories copy
to lib
folder, first install the following libraries:
yarn add rimraf make-dir-cli cpr --save-dev
npm script
include the creation, deletion, movement, and copying of files and directories. The community also provides cross-platform compatible packages for these basic operations, as listed below:
rimraf
ordel-cli
, used to delete files and directories, to achieve a functionrm -rf
cpr
, used to copy and copy files and directories, to achieve functionscp -r
make-dir-cli
, used to create a directory, to achieve a functionmkdir -p
Configure the scripts
command:
"scripts": {
"build:dev": "cross-env NODE_ENV=development webpack",
"build:prod": "cross-env NODE_ENV=production webpack",
"clean": "rimraf dist && rimraf lib",
"build:tsc": "tsc -p tsconfig.build.json --target ES5 --outDir lib",
"build:es": "cpr src/styles lib/styles -o && cpr src/assets lib/assets -o",
"build": "npm run clean && npm run build:prod && npm run build:dev && npm run build:tsc && npm run build:es"
},
When yarn build
executed, two standardized script directories will be generated
package.json configuration
Finally, complete the configuration of package.json
{
"name": "pony-react-ui",
"version": "1.0.2",
"description": "React组件库",
"main": "index.js", // 配置一个文件名指向模块的入口程序
"module": "lib/index.js",
"types": "lib/index.d.ts",
"author": "zhousheng_zuel@163.com",
"license": "MIT",
"homepage": "",
"keywords": [
"react",
"component"
],
"scripts": {
"build:dev": "cross-env NODE_ENV=development webpack",
"build:prod": "cross-env NODE_ENV=production webpack",
"clean": "rimraf dist && rimraf lib",
"build:tsc": "tsc -p tsconfig.build.json --target ES5 --outDir lib",
"build:es": "cpr src/styles lib/styles -o && cpr src/assets lib/assets -o",
"build": "npm run clean && npm run build:prod && npm run build:dev && npm run build:tsc && npm run build:es"
},
"bugs": {
"url": "https://github.com/Revelation2019/pony-react-ui/issues",
"email": "zhousheng_zuel@163.com"
},
"repository": {
"type": "git",
"url": "https://github.com/Revelation2019/pony-react-ui.git"
},
"files": [
"dist/*",
"lib",
"index.js",
"package.json",
"README.md"
],
...
}
main
: defines the entry file of thecommonjs
module
: Define the entry file of thees module
types
: Define the type declaration entry filefiles
: Specify which filesinstall
when this package is 060dd8ad713cc0homepage
: 060dd8ad713cf7 on the official website of theurl
docz generates component usage documentation
With the team's UI
component library, documentation is indispensable. After all, documentation is much more reliable than word of mouth. Here is a project that can quickly create React UI
component library usage and presentation documents: Docz
. Docz
is characterized by zero configuration, simple and fast. It uses the Markdown
syntax extension MDX
(introducing the React
Markdown
and rendering the component) to write documents. Markdown
can get started directly.
!
On the left is the created MDX
document, and on the right is the component and component code rendered by Docz
Is it convenient? Then let’s briefly introduce the steps to use
-
Docz
in your project
yarn add docz --dev 或者 npm install docz --save-dev
-
docs
folder in the root directory to store themdx
file write
mdx
file
It should be noted that react hooks cannot be used in mdx--- name: Button menu: Components --- import { Playground, Props } from "docz"; import { Button } from "../src/components/Button"; import "../src/styles/_button.scss"; # 按钮 ## Properties <Props of={Button} /> ## 基础用法 <Playground> <Button primary> primary button </Button> </Playground> ## 多种用法 <Playground> <Button secondary> danger button </Button> </Playground>
created in the root directory profile
doczrc.js
, and write the following configuration:export default { title: 'pony-ui', // 网站的标题 typescript: true, // 如果需要在.mdx文件中引入Typescript组件,则使用此选项 dest: 'build-docs', // 指定docz构建的输出目录 files: 'docs/*.mdx', // Glob模式用于查找文件。默认情况下,Docz会在源文件夹中找到所有扩展名为.mdx的文件。 ignore: ['README.md', 'CHANGELOG.md'] // 用于忽略由docz解析的文件的选项 };
docz
usesgatsby
to build a static site display component documentation, you need to installgatsby-plugin-sass
so that the site supportsscss
.gatsby-config.js
in the root directory and add the following configuration:module.exports = { plugins: ['gatsby-plugin-sass'] };
If you execute
docz dev
, the following error is reported:
This is becausegatsby-plugin-sass
usesDart
implemented bySass(sass)
by default. To use anNode(node-sass)
written in 060dd8ad71415e, you can installnode-sass
instead ofsass
and pass it as an implementation to the options.yarn add node-sass --save-dev
Change setting
module.exports = { plugins: [ { resolve: `gatsby-plugin-sass`, options: { implementation: require("node-sass"), }, } ], }
configuration
scripts
command
Whendocz dev
mdx
file will be built, and a static site display component description document will be created (this process will load thegatsby-config.js
configuration option to make the site supportsass
)"scripts": { "docz:dev": "docz dev", "docz:build": "docz build", "docz:serve": "docz build && docz serve", "build:dev": "cross-env NODE_ENV=development webpack", "build:prod": "cross-env NODE_ENV=production webpack", "clean": "rimraf dist && rimraf lib", "build:tsc": "tsc -p tsconfig.build.json --target ES5 --outDir lib", "build:es": "cpr src/styles lib/styles -o && cpr src/assets lib/assets -o", "build": "npm run clean && npm run build:prod && npm run build:dev && npm run build:tsc && npm run build:es" },
Publish to npm repository
First log in to npm
npm login
in the terminal, and follow the prompts to enter the account name, password, and email. If the following error is reported:
npm ERR! 409 Conflict - PUT http://npm.dev.casstime.com/-/user/org.couchdb.user:xxx - user registration disabled
This is because the mirror source is not http://registry.npmjs.org/
. The mirror source of the company I use here needs to be changed to http://registry.npmjs.org/
, execute the following command, then log in again, and execute yarn publish
after the build ( npm publish
somewhat different from 060dd8ad714314, so I won’t talk about it here. )
npm config set registry=http://registry.npmjs.org/
deploy
Our company did not deploy the built directory to the server, but used the nginx
proxy to pull gitlab
. The detailed process is as follows:
Nginx configuration:
server {
listen 83;
server_name 10.118.71.232;
location / {
root /opt/web/gitweb/inquiry-bre-component/build-docs;
index index.html index.htm;
if ( $request_uri !~* \. ) {
rewrite ^/([\w/]+).*/? /index.html break;
}
}
}
server {
listen 82;
server_name 10.118.71.232;
location / {
root /opt/web/gitweb/bre-components/build-docs;
index index.html index.htm;
if ( $request_uri !~* \. ) {
rewrite ^/([\w/]+).*/? /index.html break;
}
}
}
server {
listen 81;
server_name 10.118.71.232;
location ~ ^/v {
root /opt/web/gitweb/bricks-docs;
index index.html index.htm;
}
location / {
root /opt/web/gitweb/bricks-docs;
index index.html index.htm;
if ( $request_uri !~* \. ) {
rewrite ^/([\w/]+).*/? /index.html break;
}
}
}
Finally, attach the warehouse address: https://github.com/Revelation2019/pony-react-ui
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。