react&rollup开发自己的ui组件库

352 阅读1分钟

react 组件开发

1. create-react-app 创建项目

npx create-react-app my-react-components-js

等待创建完成用 vscode 打开

code ./my-react-components-js

运行项目看看是否成功,不成功重新创建

npm run start

显示如下成功

Compiled successfully!

You can now view my-react-components-js in the browser.

  http://localhost:3000

Note that the development build is not optimized.
To create a production build, use npm run build.

webpack compiled successfully

2.开发组件

新建组件文件夹

mkdir ./src/components

开发Button组件

mkdir ./src/components/Button

新建如下文件

image-20230208145916927.png

  • index.css

    .my-btn {
      outline: none;
      padding: 0;
      margin: 0;
      border: none;
      color: #fff;
    }
    
    .my-btn-bg-warn{
      background: #f1c40f;
    }
    
    .my-btn-bg-warn:hover{
      background: #ecc00f;
      color: #ebeff1;
    }
    
    .my-btn-bg-warn:active{
      background: #e1b60c;
      color: #d3d9dd;
    }
    
    .my-btn-bg-success{
      background: #2ecc71;
    }
    
    .my-btn-bg-success:hover{
      background: #2dc66d;
      color: #ebeff1;
    }
    
    .my-btn-bg-success:active{
      background: #27ae60;
      color: #d3d9dd;
    }
    
    .my-btn-bg-primary{
      background: #3498db;
    }
    
    .my-btn-bg-primary:hover{
      background: #238acf;
      color: #dce1e4;
    }
    
    .my-btn-bg-primary:active{
      background: #2980b9;
      color: #bdc3c7;
    }
    
    .my-btn-size-default {
      padding: 6px 10px;
      font-size: 16px;
      border-radius: 4px;
    }
    
    .my-btn-size-small {
      padding: 4px 6px;
      font-size: 14px;
      border-radius: 3px;
    }
    
    .my-btn-size-mini {
      padding: 3px 4px;
      font-size: 12px;
      border-radius: 2px;
    }
    
    .my-btn-size-large {
      padding: 8px 12px;
      font-size: 18px;
      border-radius: 5px;
    }
    
  • button.jsx

    import './styles/index.css';
    
    const btnPrefix = 'my-btn';
    const sizePrefix = btnPrefix + '-size';
    const typePrefix = btnPrefix + '-bg';
    
    const sizeEnum = {
      mini: 'mini',
      small: 'small',
      default: 'default',
      large: 'large',
    };
    
    const typeEnum = {
      primary: 'primary',
      success: 'success',
      warn: 'warn',
    };
    
    const getSize = size => sizeEnum[size] || sizeEnum.default;
    
    const getType = type => typeEnum[type] || typeEnum.primary;
    
    const Button = props => {
      const { size, type, onClick } = props;
      return (
        <button
          {...props}
          onClick={onClick}
          className={`${btnPrefix} ${sizePrefix + '-' + getSize(size)} ${
            typePrefix + '-' + getType(type)
          }`}
        >
          {props.children}
        </button>
      );
    };
    
    export default Button;
    
  • index.js

    export {default as Button} from "./src/button"
    

新建组件出口文件并写入内容

echo '' >> ./src/components/index.js
export { Button } from './button';

配置rollup

安装相关插件

npm i -D rollup @rollup/plugin-node-resolve @rollup/plugin-babel @babel/core @babel/cli @babel/preset-env @babel/preset-react rollup-plugin-postcss @babel/plugin-transform-runtime @rollup/plugin-terser 

新建配置文件 根目录

/rollup.config.mjs

import { defineConfig } from 'rollup';
import resolve from '@rollup/plugin-node-resolve';
import babel from '@rollup/plugin-babel';
import postcss from 'rollup-plugin-postcss';
import fs from 'fs';
import terser from '@rollup/plugin-terser';

export default defineConfig({
  input: './src/components/index.js', // 入口
  treeshake: false, // 关闭treeshake
  external: [/node_modules/], // 不打包node_modules模块
  plugins: [
    resolve({
      extensions: ['.js', '.jsx', '.json', '.mjs', '.node'],
    }),
    babel({
      babelHelpers: 'runtime',
      exclude: 'node_modules/**',
      extensions: ['.js', '.jsx', '.mjs', 'json'],
    }),
    postcss({ extract: true }), // 出来css
    // 清理输出目录
    {
      name: 'clean-output',
      outputOptions(options) {
        if (fs.existsSync(options.dir)) {
          clearDir(options.dir);
        }
      },
    },
  ],
  output: [
    //输出
    {
      exports: 'auto',
      format: 'esm', // 打包格式
      dir: 'es', // 输出目录
      preserveModules: true, // 保持文件位置
      preserveModulesRoot: './src/components/', // 打包根目录
    },
    {
      exports: 'auto',
      format: 'cjs',
      dir: 'lib', // 输出目录
      preserveModules: true,
      preserveModulesRoot: './src/components/',
    },
    {
      name: 'asd', // 全局变量名
      format: 'umd',
      dir: 'dist',
      entryFileNames: '[name].js',
      plugins: [terser()],
    },
  ],
});

function emptyDir(path) {
  const files = fs.readdirSync(path);
  files.forEach(file => {
    const filePath = `${path}/${file}`;
    const stats = fs.statSync(filePath);
    if (stats.isDirectory()) {
      emptyDir(filePath);
    } else {
      fs.unlinkSync(filePath);
    }
  });
}

function rmEmptyDir(path, level = 0) {
  const files = fs.readdirSync(path);
  if (files.length > 0) {
    let tempFile = 0;
    files.forEach(file => {
      tempFile++;
      rmEmptyDir(`${path}/${file}`, 1);
    });
    if (tempFile === files.length && level !== 0) {
      fs.rmdirSync(path);
    }
  } else {
    level !== 0 && fs.rmdirSync(path);
  }
}

function clearDir(path) {
  emptyDir(path);
  rmEmptyDir(path);
}

/babel.config.json

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "modules": false
      }
    ],
    "@babel/preset-react"
  ],
  "plugins": [["@babel/plugin-transform-runtime"]],
  "ignore": ["node_modules/**"]
}

打包

npx rollup -c

会生成三个文件夹lib es dist

/package.json 中新增

 "peerDependencies": {
    "react": "^18.2.0",
    "@babel/runtime": "^7.20.13"
  },
  "main": "./lib/index.js",
  "module": "./es/index.js",
  "files": [
    "dist",
    "es",
    "lib",
    "README.md"
   ],

然后将private:true 删掉

npm 发布自己的包

npm publish

测试包

安装你自己发布的包名 最好不要换源安装,其他源有延时

npm i my-react-components-js

App.js 导入自己的包运行

import { Button } from 'my-react-components-js/lib';
import 'my-react-components-js/es/index.css';

function App() {
  return (
    <>
      <Button
        size="large"
        type="success"
        onDoubleClick={e => {
          console.log(e);
        }}
      >
        button
      </Button>
    </>
  );
}

export default App;

测试成功:

image-20230208173512421.png