create-react-app+typescript+concent+react-router-dom+hooks搭建后台管理系统

464 阅读3分钟

解决一些新手搭建项目可能会遇到的一些坑。

创建你的新项目

按照官网推荐

$ npx create-react-app my-app --typescript  
$ # 或者 
$ yarn create react-app my-app --typescript

目录结构如下:

                             

引入sass(根据自己的使用习惯也可使用less)

$ npm install node-sass --save
$ # or
$ yarn add node-sass

安装成功后

可将App.css文件改为App.scss,发现文件生效;当我们希望样式不要全局生效,

#需要修改代码
将 import './App.scss';改为 import styles from './App.scss'

发现样式都不生效了,此时我们遇到第一个坑点,研究webpack配置,我们发现:


scssModule模块检测是将 name.module.scss这种名称类型的scss文件进行模块化编译了,

如果希望对所有.scss文件都可模块化引入,可正则匹配的.module去掉,具体如下:

#执行  yarn eject 暴露出所有的配置文件

修改config/webpack.config.js文件,将

const sassModuleRegex = /\.module\.(scss|sass)$/;
#修改为
const sassModuleRegex = /\.(scss|sass)$/;

配置antd的按需加载

#添加依赖antd
yarn add antd
#安装babel-plugin-import
yarn add babel-plugin-import

添加配置文件,在项目最外层目录添加.babelrc文件,加入如下配置即可:

{    
    "presets":["react-app"],    
    "plugins": [      [        
        "import", {          
                    "libraryName": "antd",          
                    "libraryDirectory": "es",          
                    "style": "css"        
                    }      
                ]    
     ]
}

添加路由

#安装依赖react-router-dom
yarn add react-router-dom

添加route文件夹,文件夹下新建index.tsx文件,我们可以新建两个页面 index和welcome

则index.tsx内容如下:

import React from 'react';
import { HashRouter, Route, Switch } from 'react-router-dom';
import Home from '../pages/Home';
import Welcome from '../pages/Welcome';

export default () => {    
    return (        
            <HashRouter>            
                <Switch>                
                    <Route exact path="/" component={Home} />                
                    <Route exact path="/index" component={Home} />                
                    <Route exact path="/welcome" component={Welcome} />            
                </Switch>        
            </HashRouter>    
    )
}

添加状态管理工具concent

concent是一款集vuex,redux,mobx等众多前端数据仓库优点于一身的可预测、0入侵、高性能的增强型状态管理工具,天生支持hooks,power your react!

#安装依赖
yarn add concent 

我们要用这个工具实现点击按钮,侧边栏菜单相应展开和收缩;

在最外层创建runConcent.ts文件,内容如下:

import { run } from 'concent';
run({
  "$$global":{
     state:{ collapsed: false},
     reducer:{
        changeCollapse(_: any,moduleState:{collapsed:boolean}){         
            const {collapsed} = moduleState;         
            return{           collapsed: !collapsed         }        
        }     
      }
   }
})#在最外层的index.tsx中引用执行即可import './runConcent';

添加layout组件,实现整体布局

整体文件结构如下:

            

BasicHeader组件:

import React from 'react';
import { Icon } from 'antd';
import { registerHookComp } from 'concent';
import styles from './index.scss';

type TypeDescriptor = {    type: string,    module?: string,    reducerModule?: string,  };
type ICtxBase = {    
    dispatch(        
        type: string | TypeDescriptor,        
        payload?:any,         
        renderKey?:string,        
        delay?:number, ): Promise<object | undefined>}

const setup = (ctx:ICtxBase):{[key: string]:any} => {    
    return {        
            changeCollapse: () =>{            
                ctx.dispatch('changeCollapse')        
            }    
     }
}

export default registerHookComp({    
            module: "$$global",    
            setup,    
            render: (ctx:any) =>{        
                return (           
                         <>                
                            <Icon  className={styles.trigger} type={ctx.state.collapsed ? 'menu-unfold' : 'menu-fold'}  onClick={ctx.settings.changeCollapse}  />            
                         </>        
                        )    
                    }
            });

BasicMenu组件:

import React, { memo } from 'react';
import { Menu, Icon } from 'antd';
import { HashRouter, Link, useLocation } from 'react-router-dom';
import { useConcent,ICtxBase } from 'concent';

const setup = (ctx:ICtxBase)=>{    
        return {        
                getSelectedKeys(pathname:string){           
                     return pathname        
                }    
    }}

export default memo(() => {    
        const { settings } = useConcent({ setup })    
        return (        
                    <HashRouter>            
                        <Menu theme="dark" mode="inline" selectedKeys={settings.getSelectedKeys(useLocation().pathname)}>                
                            <Menu.Item key="/index">                    
                                <Link to="/index"> 
                                    <Icon type="user" />                        
                                    <span>index</span>                    
                                </Link>                
                            </Menu.Item>                
                            <Menu.Item key="/welcome">                    
                                <Link to="/welcome">                        
                                    <Icon type="video-camera" />                        
                                    <span>welcome</span>                    
                                </Link>                
                            </Menu.Item>                
                            <Menu.Item key="3">                    
                                <Icon type="upload" />                    
                                <span>nav 3</span>                
                            </Menu.Item>            
                         </Menu>        
                        </HashRouter>    
                    )
})

index.tsx结构:

import React from 'react';
import { Layout } from 'antd';
import BasicMenu from './BasicMenu';
import BasicHeader from './BasicHeader';
import styles from './index.scss';
import { useConcent } from 'concent';
import { HashRouter } from 'react-router-dom';

const { Header, Sider, Content } = Layout;

export default function BasicLayout(props: { children?: React.ReactChild }): JSX.Element {  
    const { state } = useConcent({ module: "$$global" });
    //用HashRouter包裹BasicMenu组件,在组件内部才能用useLocation方法  
    return (    
                <Layout className={styles.myLayout}>      
                    <Sider trigger={null} collapsible collapsed={state.collapsed}>
                        <div className={styles.logo} />        
                        <HashRouter><BasicMenu /></HashRouter>      
                    </Sider>      
                    <Layout>        
                        <Header style={{ background: '#fff', padding: 0, textAlign: "left" }}>          
                            <BasicHeader />        
                        </Header>        
                        <Content style={{ margin: '24px 16px',  padding: 24,  background: '#fff',  minHeight: 280, }}> 
                            {props.children}        
                        </Content>      
                    </Layout>    
                </Layout>  
    )
}

整体效果如下:


项目地址

github.com/hxyvipno1/m…