介绍
最近开发了一个react项目,因为之前都是做原生混合H5开发,对redux用的不怎么熟练,这次想要锻炼下然后花几天看了一下redux和看了几个搭建方案,以及看了下公司其他的H5项目(直接用redux的项目)觉得很复杂繁琐。用过ant-design-pro 2.0正式版(加入了umi的版本)觉得很不错,所以决定从UMI入手搭一个项目来做手机端的H5项目,在去掉大部分业务代码后把这个demo拿来给大家分享一下,希望对新手搭建umi项目有一定价值。
写这篇文章是为了帮助需要的人更好的理解umi来搭建项目,第一次写文章不足之处望指正
本文的例子的demo在我的github
地址可以下载参考 github地址
觉得对自己理解和上手umi项目有帮助的点个star
创建项目
- umi 提供了脚手架,可以参考umi官网通过脚手架创建项目部分
- 包管理器我使用的是yarn
yarn是facebook推出的,在一些较新的react书籍和资料中也是推荐使用yarn。与npm相比,yarn主要的优势在于:速度快,离线模式,版本控制。
- 废话太多上代码干,在空文件夹下
yarn create umi
会出现一个选择框,我选择了antd,dll,hard source。这几个配置在umi官网快速上手里面有配置的解释。
确保 node 版本是 8.10 或以上
- 安装依赖
yarn
- 启动项目
yarn start
- 编译打包
umi build
我对umi的一些理解
- 在dva 项目通常都是要单独写一个
models
,然后所有的models
写在里面。
用了 umi 后,可以在pages同级下写一个models来管理所有的models也可以在每个页面的文件夹下写一个models文件夹来放当前页面需要用的models,好处是结构更加清晰了,删除起来方便不需要去删除好几个地方,且会自动注册 models
- 有约定式路由去掉了router.js,umi 会根据
pages
目录下的页面的js自动生成路由配置。可以参考umi官网路由部分,
- 假设 pages 目录结构如下:
+ pages/
+ users/
- index.js
- index.less
- index.js
- 那么,umi 会自动生成路由配置如下:
[
{ path: '/', component: './pages/index.js' },
{ path: '/users/', component: './pages/users/index.js' },
]
- 好用的动态路由(好用在页面间的传值和取值)
- 比如要实现从列表跳转到订单详情,那一般需要带一个id过去,在详情页拿id获取一些数据,那在umi里面我们怎么做?
+ pages/ + orderdetail/ - $id$.js - index.less
- 那在list跳转上面的orderdetail页面的时候我们要这么写(假设val.id=15),
记得要从import router from 'umi/router'
router.push('/orderdetail/' + val.id)
- 在orderdetail页面取这个id(可以自己打印一下this.props看看)
this.props.match.params.id
- 减少了配置文件,umi的 package.json 里会少很多依赖,再比如创建项目的时候选的antd那就包含了
- antd
- antd-mobile
- babel-plugin-import
上demo
运行后的效果



全局layout组件
约定 src/layouts/index.js 为全局路由,返回一个 React 组件,通过 props.children 渲染子组件。
+ layouts/
+ baseLayout/
- index.js
- index.less
- index.js
- 首先先看下 layouts 下的 index.js
import React, { Component } from 'react' import BaseLayout from './baseLayout'; // 底部导航的组件 const ULR_NO_LAYOUT = ['/', '/home', '/class', '/my']; //判断在哪几个路由下需要出现底部导航 class Index extends Component { componentDidMount() { } renderBody = () => { const {location: {pathname}, children } = this.props; if (ULR_NO_LAYOUT.includes(pathname)) { return (<BaseLayout {...this.props} />); } return ( <React.Fragment> {children} </React.Fragment> ); } render() { return ( <React.Fragment> {this.renderBody()} </React.Fragment> ) } } export default Index;
ULR_NO_LAYOUT
变量是为了判断在哪几个路由下需要出现底部导航BaseLayout
是用antd-mobile
的TabBar
mock数据
约定 mock 目录里所有的 .js 文件会被解析为 mock 文件。我们新建一个home.js
export default { // 支持值为 Object 和 Array 'GET /api/users': { users: [1, 2] }, // GET POST 可省略 '/api/users/1': { id: 1 }, // 支持自定义函数,API 参考 express@4 'POST /api/users/create': (req, res) => { res.end('OK'); }, };
直接请求/api/users
就能拿到 { users: [1, 2] }
request文件
需要根据自己的项目和后台的接收方法对请求方式进行封装
- 例如request的49行和56行,是我对自己项目请求接口时增加的token,可以去掉
// body 添加token if (newOptions.body) { newOptions.body.__token__ = getToken(); } else { newOptions.body = { __token__: getToken(), }; }
- 例如request的86行和89行增加对get传值的转换,
setUrlEncoded
方法,这样处理方便get方法传值的时候也能和post方法一样穿对象,自动转换带在url后面
} else if (newOptions.method === 'GET') { new_url = url + '?' + setUrlEncoded(newOptions.body) delete newOptions.body } //setUrlEncoded方法 export const setUrlEncoded = (obj) => { let urlEncoded = ''; if(obj && obj instanceof Object) { const keys = Object.keys(obj); if(keys && keys.length) { keys.forEach((key, index) => { urlEncoded += `${key}=${obj[key]}`; if(index + 1 < keys.length){ urlEncoded += '&'; } }); } } return urlEncoded; }
- request的110行需要根据自己的项目的返回值来做修改判断是否返回
response
获取和验证表单的值
- 推荐使用
rc-form
import { createForm } from 'rc-form'; @createForm()
- 调用时 需要从
this.props
里拿到form
里的getFieldProps, getFieldError
render() { const {form: {getFieldProps, getFieldError}, regLoading} = this.props; <div> <InputItem {...getFieldProps('name', { initialValue: '', rules: [{ required: true, message: '请输入用户名', }], })} clear error={!!getFieldError('name')} onErrorClick={() => { const err = getFieldError('name').join('、'); Toast.info(err, 1); }} placeholder='用户名' /> </div> }
- 提交验证form输入内容的时候,例如
submit(){ const {form, dispatch} = this.props; form.validateFields((error, fieldsValue) => { if (error) { return; } dispatch({ type: 'login/submit', payload: { // 入参 }, callback: (res) => { //需要实现什么 } }); }); }
models调用和models文件
- 例如在home的index.js要调用
import { connect } from 'dva'; @connect(({ home }) => ({ home }))
- home的models文件夹home.js, 使用了
subscriptions
,subscriptions
的好处应该就是可以监测全局的变化, 即使和当前页面不相关model里也可以进行数据改动或者请求接口.当然你也可以选择在页面中使用dispatch
import { reg } from 'services/home'; import router from 'umi/router'; export default { namespace: 'home', state: { 'list':{ 'productList': '', 'bannerList': '' } }, effects: { *reg({ payload, callback }, { call, put }) { const response = yield call(reg, payload); yield put({ type: 'setData', payload: response.data }); } }, reducers: { setData(state, { payload }) { return { ...state, list: payload, } } }, subscriptions: { setup({ dispatch, history }) { return history.listen(({ pathname, search }) => { if (pathname == '/home'||pathname == '/') { dispatch({ type: 'reg', }); } }); }, }, };
Jack程 写于2018年12月16日凌晨
作者:Jadeite2
链接:https://www.jianshu.com/p/59099cb3e28d
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。