介紹
最近開發了一個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
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。