手把手教你搭建 React UI 组件库 (六) 建立官网

467 阅读3分钟

image.png

image.png

1. 目标一: 添加 menu 切换组件示例:

目标:

image.png

点击Icon链接后:

image.png 点击对话框:

image.png

点击布局:

image.png

开始:

  1. 安装必要的库: 修改:@/package.json:

    {
      "name": "sweetui",
      "version": "1.0.0",
      "description": "",
      "main": "dist/lib/index",
      "types": "dist/lib/index",
      "scripts": {
        "test": "cross-env NODE_ENV=test jest --config=jest.config.js --runInBand",
        "start": "cross-env NODE_ENV=development webpack-dev-server --config webpack.config.dev.js",
        "build": "cross-env NODE_ENV=production webpack --config webpack.config.prod.js"
      },
      "keywords": [],
      "author": "",
      "license": "ISC",
      "devDependencies": {
        "@babel/preset-env": "^7.3.4",
        "@babel/preset-react": "^7.0.0",
        "@types/classnames": "2.2.7",
        "@types/jest": "^24.0.9",
        "@types/react": "^16.8.7",
        "@types/react-dom": "^16.8.2",
        "@types/react-router-dom": "^5.3.3",
        "@types/react-test-renderer": "^16.8.1",
        "babel-jest": "^24.3.1",
        "cross-env": "^5.2.0",
        "css-loader": "^2.1.1",
        "html-webpack-plugin": "^3.2.0",
        "jest": "^24.3.1",
        "node-sass": "npm:sass",
        "react-test-renderer": "^16.8.4",
        "sass-loader": "^7.1.0",
        "style-loader": "^0.23.1",
        "ts-jest": "^24.0.0",
        "ts-loader": "~8.2.0",
        "typescript": "^4.6.2",
        "webpack": "^4.29.6",
        "webpack-cli": "^3.2.3",
        "webpack-dev-server": "^3.2.1"
      },
      "dependencies": {
        "classnames": "2.2.6",
        "react": "^16.8.4",
        "react-dom": "^16.8.4",
        "react-router-dom": "^6.2.2",
        "svg-sprite-loader": "^6.0.11"
      }
    }
    

    运行yarn 安装

    修改webpack配置:@/webpack.config.js

    const path = require('path')
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    module.exports = {
        entry: {
            index: './lib/index.tsx'
        },
        resolve: {
            extensions: ['.ts', '.tsx', '.js', '.jsx'],
        },
        output: {
            path: path.resolve(__dirname, 'dist/lib'),
            library: 'FUI',
            libraryTarget: 'umd',
        },
        module: {
            rules: [
                {
                    test: /.tsx?$/,
                    loader: 'ts-loader',
                    exclude: /node_modules/,
                },
                {
                    test: /.svg$/,
                    loader: 'svg-sprite-loader',
                },
                {
                    test: /.scss$/,
                    use: ['style-loader', 'css-loader', 'sass-loader']
                }
            ]
        },
    }
    
  2. 修改:@/lib/index.tsx

    import React from 'react';
    import ReactDOM from 'react-dom';
    import {HashRouter as Router, Routes,Route, Link} from 'react-router-dom';
    import LayoutExample from './Layout/layout.example';
    import IconExample from './Icon/icon.example';
    import DialogExample from './Dialog/Dialog.example';
    
    
    ReactDOM.render(
        <Router>
            <div>
                <header>
                    <div className="logo">
                        sweet-ui
                    </div>
    
                </header>
                <div>
                    <aside>
                        <h2>组件</h2>
                        <ul>
                            <li>
                                <Link to="/icon">Icon</Link>
                            </li>
                            <li>
                                <Link to="/dialog">对话框</Link>
                            </li>
                            <li>
                                <Link to="/layout">布局</Link>
                            </li>
                        </ul>
                    </aside>
                    <main>
                        <Routes>
                            <Route path="/icon" element={<IconExample/>}/>
                            <Route path="/dialog" element={<DialogExample/>}/>
                            <Route path="/layout" element={<LayoutExample/>}/>
                        </Routes>
                    </main>
                </div>
            </div>
        </Router>
        ,
        document.getElementById('root')
    )
    

    修改:@/lib/Icon/icon.example.tsx:

    import * as React from 'react';
    import Icon from './index';
    
    interface propsType {
    
    }
    
    const IconExample: React.FC<propsType> = () => {
        return (
            <div>
                <Icon icon="alipay"/>
                <Icon icon="wechat"/>
                <Icon icon="close"/>
            </div>
        );
    };
    export default IconExample;
    
  3. 查看结果:

    image.png 成功!

2. 目标二: 使用自己的layout组件建立官网

目标:menu放左边, 自己的logo放在header里, 示例放在content里面

行动:

  1. 修改:@/lib/index.tsx:

    import React from 'react';
    import ReactDOM from 'react-dom';
    import {HashRouter as Router, Routes,Route, Link} from 'react-router-dom';
    import LayoutExample from './Layout/layout.example';
    import IconExample from './Icon/icon.example';
    import DialogExample from './Dialog/Dialog.example';
    import Layout, {Content, Footer, Sider} from './Layout/layout';
    
    
    ReactDOM.render(
        <Router>
            <Layout>
                <Sider>
                    <h2>组件</h2>
                    <ul>
                        <li>
                            <Link to="/icon">Icon</Link>
                        </li>
                        <li>
                            <Link to="/dialog">对话框</Link>
                        </li>
                        <li>
                            <Link to="/layout">布局</Link>
                        </li>
                    </ul>
                </Sider>
                <Layout>
                    <header>
                        <div className="logo">
                            sweet-ui
                        </div>
                    </header>
                    <Content>
                        <main>
                            <Routes>
                                <Route path="/icon" element={<IconExample/>}/>
                                <Route path="/dialog" element={<DialogExample/>}/>
                                <Route path="/layout" element={<LayoutExample/>}/>
                            </Routes>
                        </main>
                    </Content>
                    <Footer>
                        footer
                    </Footer>
                </Layout>
            </Layout>
        </Router>
        ,
        document.getElementById('root')
    )
    
  2. 查看结果:

    image.png 很明显, 组件写的有问题, 没接受children参数, 回去改

  3. 同样修改: @/lib/Layout/components/Header/header.tsx, @/lib/Layout/components/Footer/footer.tsx, @/lib/Layout/components/Content/content.tsx, @/lib/Layout/components/Sider/sider.tsx

    让组件能接受chirldren参数

    image.png 查看浏览器:

    image.png 我们成功了!

3. 目标三: 制作自己的logo

  1. 谷歌搜索: logo maker, 制作, 下载自己的logo

  2. 将logo保存到 @/lib/assets/SweetUILogo.png

  3. 修改代码@/lib/index.tsx

    import logo from './assets/SweetUILogo.png'
    
    //31行添加:
    <img src={logo} alt='pic' />
    
    

    修改@/types/custom.d.ts

    declare module '*.svg' {
        const content: any;
        export default content;
    }
    declare module '*.png' {
        const content: any;
        export default content;
    }
    

    修改webpack配置:

                {
                test: /\.(png|jpe?g|gif)$/i,
                loader: 'file-loader',
            },
    
    

    image.png 安装:png的loader:yarn add --dev file-loader

    重启项目, 查看浏览器

    image.png

    我们成功了

4. 目标四: 改造页面

  1. 修改@/lib/index.tsx:

    import React from 'react';
    import ReactDOM from 'react-dom';
    import {HashRouter as Router, Routes,Route, Link} from 'react-router-dom';
    import LayoutExample from './Layout/layout.example';
    import IconExample from './Icon/icon.example';
    import DialogExample from './Dialog/Dialog.example';
    import Layout, {Content, Footer, Header, Sider} from './Layout/layout';
    import logoCandy from './assets/logo/logoCandy.png'
    import logoText from './assets/logo/logoText.png'
    import './index.scss'
    
    
    ReactDOM.render(
        <Router>
            <Layout className="site-page">
                <Header className="site-header">
                    <div className="logo">
                        <img src={logoCandy} width="48"  alt='pic' />
                        <img src={logoText} width="140"  alt='pic' />
                    </div>
                </Header>
                <Layout>
                    <Sider className="site-aside">
                        <h2>组件</h2>
                        <ul>
                            <li>
                                <Link to="/icon">Icon</Link>
                            </li>
                            <li>
                                <Link to="/dialog">对话框</Link>
                            </li>
                            <li>
                                <Link to="/layout">布局</Link>
                            </li>
                        </ul>
                    </Sider>
                    <Content className="site-main">
                        <Routes>
                            <Route path="/icon" element={<IconExample/>}/>
                            <Route path="/dialog" element={<DialogExample/>}/>
                            <Route path="/layout" element={<LayoutExample/>}/>
                        </Routes>
                    </Content>
                </Layout>
                <Footer className="site-footer">
                    &copy; ttclib/index.scss`
                 
                </Footer>
            </Layout>
        </Router>
        ,
        document.getElementById('root')
    )
    
  2. 修改scss:@/lib/index.scss

    [class^=sweetui-] {
      box-sizing: border-box;
      &::after,
      &::before {
        box-sizing: border-box;
      }
    }
    button {
      box-sizing: border-box;
      height: 32px;
      margin: 0 4px;
      border-radius: 4px;
      &:first-child {
        margin-left: 0;
      }
      &:last-child {
        margin-right: 0;
      }
    }
    
    * {margin: 0;padding: 0; box-sizing: border-box;}
    *::before {box-sizing: border-box;}
    *::after {box-sizing: border-box;}
    ul, ol {list-style: none;}
    h1, h2, h3, h4, h5, h6 {font-weight: normal;}
    a { text-decoration: none; color: inherit; }
    pre {
      font-family: "Fira Code Medium", Consolas, monospace;
    }
    $padding: 16px;
    $main-color: #FF64B2;
    $border-color: #eee;
    body {
      font-size: 16px;
    }
    .site-page {
      background: #ffffff;
      min-height: 100vh;
    }
    .site-main {
      padding-top: 16px;
    }
    .site-header {
      background: $border-color;
      padding: 8px 0;
      .logo {
        padding: 0 $padding;
        display: flex;
        align-items: center;
        > img {margin-right: 8px;}
        > span {
          color: $main-color;
          font-size: 32px;
        }
      }
    }
    .site-aside {
      border-right: 1px solid #EEEEEE;
      margin-right: 16px;
      h2 {
        padding: $padding;
        font-size: 18px;
      }
      ul {
        min-width: 12em;
        > li {
          a {
            color: #333;
            display: block;
            padding: 8px $padding;
            &:hover {
              color: $main-color;
            }
            &.active {
              color: white;
              background: $main-color;
            }
          }
        }
      }
    }
    .site-footer {
      text-align: center;
      font-size: 12px;
      color: #666;
      padding: 16px 0;
      border-top: 1px solid $border-color;
    }
    .example {
      padding: 16px 0;
    }
    

    查看浏览器:

    image.png 成功了!

5. 目标五: 展示代码

目标: 我们期待能在示例下面展示代码:

image.png

行动:

  1. 安装一个loader: yarn add --dev raw-loader@2.0.0

  2. 新增文件: @/lib/Components/Demo/demo.tsx:

    import * as React from 'react';
    
    interface propsType {
        code: string;
    }
    
    const Demo: React.FC<propsType> = (props) => {
        return (
            <div>
                {props.children}
                <pre>
                    {props.code}
                </pre>
            </div>
        );
    };
    export default Demo;
    
  3. 新增文件@/lib/Icon/icon.demo.tsx:

    import * as React from 'react';
    import IconExample from './icon.example';
    import Demo from '../Components/Demo/demo';
    
    interface propsType {
    
    }
    const x = require('!!raw-loader!./icon.example')
    
    const IconDemo: React.FC<propsType> = () => {
        return (
            <Demo code={x.default}>
                <IconExample/>
            </Demo>
        );
    };
    export default IconDemo;
    
  4. 修改引用:@/lib/index.tsx

    
    import IconDemo from './Icon/icon.demo';
    
    <Route path="/icon" element={<IconDemo/>}/>
    

    image.png image.png

  5. 重启项目查看浏览器:

    image.png 成功实现!

6. 目标六: 代码显示高亮:

  1. 安装库: yarn add --dev prism-react-renderer

  2. 修改@/lib/Components/Demo/demo.tsx

    import * as React from 'react';
    import Highlight, {defaultProps} from "prism-react-renderer";
    import {useState} from 'react';
    
    interface Props {
        code: string;
    }
    
    const Demo: React.FunctionComponent<Props> = (props) => {
        const [codeVisible, setCodeVisible] = useState(false);
        const code = (
            <Highlight {...defaultProps} code={props.code} language="jsx">
                {({className, style, tokens, getLineProps, getTokenProps}) => (
                    <pre className={className} style={style}>
                  {tokens.map((line, i) => (
                      <div {...getLineProps({line, key: i})}>
                          {line.map((token, key) => (
                              <span {...getTokenProps({token, key})} />
                          ))}
                      </div>
                  ))}
                </pre>
                )}
            </Highlight>
        );
        return (
            <div>
                <div className="example">
                    {props.children}
                </div>
                <div>
                    <button onClick={() => setCodeVisible(!codeVisible)}>查看代码</button>
                    {codeVisible && code}
                </div>
            </div>
        );
    };
    
    export default Demo;
    
  3. 重启项目, 查看浏览器:

    image.png 我们成功了!

7. 目标七: 编译项目, 准备部署

  1. 增加生存官网文件命令, 修改: @/package.json

        "doc": "cross-env NODE_ENV=production webpack --config webpack.config.doc.js"
    
    

    image.png

  2. 添加生成官网webpack配置, 新增文件:@/webpack.config.doc.js

    const base = require('./webpack.config')
    const path = require('path')
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    module.exports = Object.assign({}, base, {
        mode: 'production',
        output: {
            path: path.resolve(__dirname, 'doc'),
        },
        entry: {
            example: './lib/index.tsx',
        },
        plugins: [
            new HtmlWebpackPlugin({
                template: 'index.html',
                filename: 'example.html'
            })
        ],
    })
    
  3. 运行命令生成官网文件夹: yarn doc

  4. 在文件夹管理器双击打开:@/doc/example.html

    image.png

    image.png 我们成功了!

8.目标八: 将官网部署到github上去

  1. 保存当前代码:

        git add .
        git commit -m 'update'
    

    新建分支: git checkout -b gh-pages

  2. 在新分支下, 删除新建分支不需要的文件: 删除到只剩下下列文件夹: image.png

  3. 将doc文件夹东西移出来: mv -f doc/* ./

  4. git保存, 上传到github:

    git add .
    git commit -m 'update'
    git push --set-upstream origin gh-pages
    
    
  5. 修改github 设置: 切换到 gh-pages 分支 image.png 打开settings

    image.png 打开github pages设置 image.png 成功打开:

    image.png

  6. 打开上图生成的github网址:https://tianzc-zju.github.io/sweet-ui/example.html

    image.png 我们成功了

9. 目标九: 生成自动更新官网的脚本

  1. 切回main分支 git checkout main

  2. 新建文件:@/doc.sh:

    #!/bin/env bash
    
    yarn doc
    git add .
    git commit -m "update"
    git checkout gh-pages
    mv -f doc/* ./
    git add .
    git commit -m "update"
    git push
    git checkout -
    
    
  3. 修改文件: @/lib/index.tsx

    image.png

  4. 执行命令: sh ./doc.sh

  5. 查看https://tianzc-zju.github.io/sweet-ui/example.html

    image.png