70
头图

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' : When library loaded, inlet origin return value assigned to a variable

    var MyLibrary = _entry_return_;
    // 在一个单独的 script...
    MyLibrary.doSomething();
  • libraryTarget: 'this' : entrance starting point of the return value assigned to the this a property, this meaning depends on you

    this['MyLibrary'] = _entry_return_;
    // 在一个单独的 script...
    this.MyLibrary.doSomething();
    MyLibrary.doSomething(); // 如果 this 是 window
  • libraryTarget: 'window' : The return value of the assigned to this attribute of the window

    window['MyLibrary'] = _entry_return_;
    window.MyLibrary.doSomething();
  • libraryTarget: 'global' : The return value of the assigned to this attribute of the global

    global['MyLibrary'] = _entry_return_;
    global.MyLibrary.doSomething();
  • libraryTarget: 'commonjs' : The return value assigned to the exports object. This name also means that the module is used in the CommonJS environment

    exports['MyLibrary'] = _entry_return_;
    require('MyLibrary').doSomething();
  • libraryTarget: 'module' : output ES module, it should be noted that this function is not yet fully supported
  • libraryTarget: 'commonjs2' : The return value entry point will be assigned to the module.exports object. This name also means that the module is used in the CommonJS environment

    module.exports = _entry_return_;
    require('MyLibrary').doSomething();
  • libraryTarget: 'amd' : library as a AMD module.
    AMD module requires the entry chunk (for example, the first script loaded with tags) to be defined by specific attributes, such as define and require , which are usually provided by RequireJS or any compatible module loader (such as almond ). Otherwise, directly loading the generated AMD bundle will cause an error, such as define 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 call bundle 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 will CommonJS, AMD environment, or export the module to global under 060dd8ad71324f

    module.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 to css , use postcss to do some processing on the style, and use the autoprefixer plug-in to add the browser prefix to the style to prevent compatibility problems with some styles
  • Use webpack built-in plug-in BannerPlugin to add the version number to the beginning of the built file
  • Engineering using webpack 5.x version to build, using the built- TerserJSPlugin of JS compressed; style compression using OptimizeCSSAssetsPlugin plug. In addition, in order to generate compressed and uncompressed versions, the cross-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
image.png

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
image.png

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 or del-cli , used to delete files and directories, to achieve a function rm -rf
  • cpr , used to copy and copy files and directories, to achieve functions cp -r
  • make-dir-cli , used to create a directory, to achieve a function mkdir -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
image.png

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 the commonjs
  • module : Define the entry file of the es module
  • types : Define the type declaration entry file
  • files : Specify which files install when this package is 060dd8ad713cc0
  • homepage : 060dd8ad713cf7 on the official website of the url

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.
!image.png
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

  1. Docz in your project
yarn add docz --dev 或者 npm install docz --save-dev
  1. docs folder in the root directory to store the mdx file
    image.png
  2. 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>
    
  3. 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解析的文件的选项
    };
  4. docz uses gatsby to build a static site display component documentation, you need to install gatsby-plugin-sass so that the site supports scss . 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:
    image.png
    This is because gatsby-plugin-sass uses Dart implemented by Sass(sass) by default. To use an Node(node-sass) written in 060dd8ad71415e, you can install node-sass instead of sass 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"),
       },
     }
      ],
    }
  5. configuration scripts command
    When docz dev mdx file will be built, and a static site display component description document will be created (this process will load the gatsby-config.js configuration option to make the site support sass )

    "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"
    },

    image.png

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/

image.png

image.png

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:

组件文档部署confluence.png

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


记得要微笑
1.9k 声望4.5k 粉丝

知不足而奋进,望远山而前行,卯足劲,不减热爱。