基于umi的React项目结构介绍

本项目是基于umi搭建的,具体可以参考 Ant Design 实战教程(beta 版)
项目搭建 以下命令顺序执行,用来完成umi的初始化

mkdir demo2 cd demo2npm init -ynpm i antd --savenpm i umi --save-dev

然后,我们开始建项目的文件目录
- demo2 - dist // 存放编译后的文件- config - config.js// umi的配置文件 - mock - index.js// 前端模拟请求数据- src// 应用的所有代码 - actions// 处理异步请求 - assets// 静态资源 - components// 公用组件 - pages// 业务逻辑页面 - reducers// reducer 状态处理 - util// 公用方法 - index.html// 项目模板 - index.js// 项目入口 - package.json// npm init 自动生成

// src/pages/index.js import React, { Component } from 'react'class App extends Component { constructor(props) { super(props); this.state = {}; } render() { return (hello world
); } }export default App;

修改package.json
... "scripts": { +"dev": "umi dev", +"build": "umi build" } ...

我们运行 npm run dev 就能看到hello world.
现在我们使用的都是umi的默认配置,我们还需要自己的配置
修改配置文件 添加 umi-plugin-react 插件
npm i umi-plugin-react --save-dev

配置 config/config.js 文件
export default { plugins: [ ['umi-plugin-react', { antd:true, //使用antd dva:false//不使用dva,我们直接用redux }], ], // 使用自定义的router,如果不使用则是用umi的约定式路由(按文件夹路径来) // 这里的path都是相对于pages文件,例如: //routes:[{ //path:'/box', // 指定路由地址(url) //component:'Box/index' //引用的组件路径 // }] // 引用的就是pages文件夹下 Box/index.js文件 routes:[{ path:'/', component:'index' }] }

使用Routes配置 首先我们使用antd的Layout布局,修改src/pages/index.js
import React, { Component } from 'react'; import { Layout } from 'antd'; const { Header, Content, Footer } = Layout; class App extends Component { constructor(props) { super(props); this.state = {}; } render() { return (header content
footer
); } }export default App;

重新运行 npm run dev,页面就会显示出当前的界面
在pages下新建home.js文件
import React, { Component } from 'react'; class Home extends Component { constructor(props) { super(props); this.state = {}; } render() { return (Home 页面); } }export default Home;

修改文件
// src/pages/index.js ... content ==> {this.props.children} ... // config/config.js... routes: [{ path: '/', component: 'index', +routes: [{ +path: '/', +component: './home' +}] }] ...

保存后页面就会发生变化,Content中就会显示 Home的内容
在umi中使用scss 在umi中可以直接使用css,但是并不支持scss,我们需要加两个loader,
直接npm安装 node-sass和sass-loader 即可,剩余的事情umi已经帮我们做好了。
npm i --save-dev node-sass sass-loader

在src/assets下新建文件夹
- assets +- img +- css +-style.scss// 这个样式文件一般来说存放全局的样式

在src/pages/index.js 引用style.scss
import '../assets/css/style.scss';

在home.js同级新建home.scss 文件
.home-container{ .red{ color:red; } }

在home.js引用,并修改render
// 第一种使用scss方法 // 使用这种方法的时候样式名称不能用 "-" ,不然在使用的时候会报错 ... import homeStyle from './home.scss'; ...... render() { return (Home 页面
); } ...//第二种使用scss方法... import './home.scss'; ...... render() { return (Home 页面
); } ...

用第二种方法的情况:
刷新页面发现并没有变化,打开浏览器调试窗口,查看sources
找到引用的css文件,搜索可以看到好像我们的样式确实是存在的,只不过被加上了其他的后缀(为了保证不会出现全局污染)
ps:这个问题当时我找了好久
基于umi的React项目结构介绍
文章图片
1.png 这个是umi自己默认加上,我们并不想要这个东西,在config/config.js文件中添加配置
... cssLoaderOptions:{ localIdentName:'[local]' } ...

完善routes 现在我们的项目只有一个home页面,我们多加几个,来实现跳转的功能
简单页面跳转 在home.js同级添加文件 center.js
import React, { Component } from 'react'; class Center extends Component { constructor(props) { super(props); this.state = {}; }render() { return (这里是个人中心页面
); } }export default Center;

修改home.js
... import Link from 'umi/link'; ...... render() { return (Home 页面
个人中心); } ...

修改config.js
routes: [{ path: '/', component: 'index', routes: [{ path: '/', component: './home' }, { +path: '/center', +component: './center' }] }]

点击页面的"个人中心",即可跳转到个人中心页面
事件跳转 除了点击Link跳转页面外,我们还有其他的跳转需求,比如:返回上一个页面,或者登录后跳转。这些都可以算是事件跳转,
修改center.js
... import { Button } from 'antd'; import router from 'umi/router'; ...... onGoBackHome = () => { router.goBack() // router.push('/') } render() { return (这里是个人中心页面
); } ...

在项目中使用redux 在umi中,redux是封装在dva中的,但是我们想用原始的那种redux (仅仅是个人原因),我们就不去使用dva的模式。
在项目中actions文件主要用于处理请求、异步等,reducers文件则是处理数据以及其他的改变
创建store 在reducers目录下新建文件
因为在umi中会自动导入redux和react-redux包,所以我们不需要在安装,可以直接使用
// src/reducers/home.js//初始化一个homeStore const initHomeStore = { isShowDesc: true }const homeStore = (state = initHomeStore, action) => { switch (action.type) { case 'home-show-desc': return { ...state, isShowDesc: action.value } default: return state; } }export default homeStore; // src/reducers/index.js// 可以把每一个页面/模块都写一个store文件,然后在index中合并成一个store import { combineReducers } from 'redux'; import homeStore from './home'; // 合并store const appStore = combineReducers({ homeStore, }); export default appStore;

使用store store文件创建好了之后,修改pages/index.js
//导入文件 ... import { createStore } from 'redux'; import { Provider } from 'react-redux'; import appStore from '../reducers'; const store = createStore(appStore); ...//修改render内容 render() { return ( +header {this.props.children}
footer
+ ); }

这样我们可以在所有的页面都使用store内容
修改 pages/home.js
//导入 ... import { connect } from 'react-redux'; ...class Home extends Component { ...render() { const { isShowDesc } = this.props; return (Home 页面
个人中心 { isShowDesc ?这里是详情信息
: '' }); } }// mapStateToProps 方法传的state是全局的store,我们只需要homeStore,返回homeStoreconst mapStateToProps = state => { console.log('state:', state); return state.homeStore; }export default connect(mapStateToProps)(Home);

重新启动 npm run dev
此时页面上看不到详情内容,
手动修改一下reducers/home.js 的isShowDesc值为true,保存之后在页面上就能看到详情内容
手动修改只是测试一下
下面我们来利用dispatch修改isShowDesc的值(这里不使用更简单的组件state属性来处理)
给home页面添加一个button,点击button来显示/隐藏详情
... import { Button } from 'antd'; ...class Home extends Component { ... onButtonClick = () => { const { onToggerDesc, isShowDesc } = this.props; onToggerDesc(!isShowDesc); } render() { const { isShowDesc } = this.props; return (... { isShowDesc ?这里是详细信息
: '' }); } }const mapStateToProps ...const mapDispatchToProps = dispatch => ({ onToggerDesc: (value) => { dispatch({ type: 'home-show-desc', value }) } }); export default connect(mapStateToProps, mapDispatchToProps)(Home);

这样我们就能够点击按钮来改变详情的状态
网络请求 网络请求使用fetch,使用mock模拟数据
fetch的使用 安装 fetch、mockjs
npm i --save whatwg-fetch mockjs

封装一下请求方法,并使用mockjs模拟请求
// src/util/index.jsimport 'whatwg-fetch'; export const get = (url, params) => { let newUlr = url; let count = 0; if (params) { newUlr += '?'; for (key in params) { let value = https://www.it610.com/article/params[key]; if (typeof value ==='object') { value = https://www.it610.com/article/JSON.stringify(value); } count++; newUlr += ((count> 1 ? '&' : '') + key + '=' + value); } } return fetch(newUlr); }// src/actions/index.js import { get } from '../util'; export const getList = ({ dispatch, params }) => { get('/getlist') .then((response) => { return response.json() }).then((value) => { dispatch({ type: 'home-get-list', value: value.list }) }).catch((ex) => { ///失败 }); }// mock/index.js import mockjs from 'mockjs'; export default { "/getlist": mockjs.mock({ "list|10": [{ city: "@city", name: "@cname", "sex|0-1": 1, email: "@email", "id|1-10": 5 }] }) }

在home.js中使用
... import { Button, Table } from 'antd'; import { getList } from '../actions'; ...class Home extends Component { ... componentDidMount() { this.props.onGetList(); } ...render() { const { isShowDesc, listData } = this.props; const columns = [{ title: '姓名', dataIndex: 'name' }, { title: '城市', dataIndex: 'city' }, { title: '性别', dataIndex: 'sex', render: (text, record) => (text === 0 ? '女' : '男') }, { title: '邮箱', dataIndex: 'email' }]; return (... ); } }const mapStateToProps = ... const mapDispatchToProps = dispatch => ({ ...onGetList: () => { getList({ dispatch }); } }); export default connect(mapStateToProps, mapDispatchToProps)(Home);
保存,刷新页面后就能看到数据
Antd 主题定制 Antd的主题定制有好几种方法,该项目是基于umi的,所以就是用umi配置的方法来定制。
在config/config.js 文件中添加配置
theme: 'src/assets/css/theme.js'

在src/assets/css 下新建theme.js文件
这里只修改一个属性值 (其他属性可以参考文档)
module.exports = { 'primary-color': '#f39700', };

编译后,home页面的按钮主题就被修改了
【基于umi的React项目结构介绍】至此一个基于umi的react项目结构就讲完了。

    推荐阅读