解决一些新手搭建项目可能会遇到的一些坑。
创建你的新项目
按照官网推荐
$ 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>
)
}