umi + dva + ant-design-mobile快速搭建H5項目


介紹

最近開發了一個react項目,因為之前都是做原生混合H5開發,對redux用的不怎么熟練,這次想要鍛煉下然后花幾天看了一下redux和看了幾個搭建方案,以及看了下公司其他的H5項目(直接用redux的項目)覺得很復雜繁瑣。用過ant-design-pro 2.0正式版(加入了umi的版本)覺得很不錯,所以決定從UMI入手搭一個項目來做手機端的H5項目,在去掉大部分業務代碼后把這個demo拿來給大家分享一下,希望對新手搭建umi項目有一定價值。

寫這篇文章是為了幫助需要的人更好的理解umi來搭建項目,第一次寫文章不足之處望指正
本文的例子的demo在我的 github 地址可以下載參考 github地址
覺得對自己理解和上手umi項目有幫助的點個star

創建項目

yarn是facebook推出的,在一些較新的react書籍和資料中也是推薦使用yarn。與npm相比,yarn主要的優勢在於:速度快,離線模式,版本控制。

  • 廢話太多上代碼干,在空文件夾下
yarn create umi

會出現一個選擇框,我選擇了antd,dll,hard source。這幾個配置在umi官網快速上手里面有配置的解釋。
確保 node 版本是 8.10 或以上

  • 安裝依賴
yarn
  • 啟動項目
yarn start
  • 編譯打包
umi build

我對umi的一些理解

  1. 在dva 項目通常都是要單獨寫一個 models,然后所有的models寫在里面。
    用了 umi 后,可以在pages同級下寫一個models來管理所有的models也可以在每個頁面的文件夾下寫一個models文件夾來放當前頁面需要用的models,好處是結構更加清晰了,刪除起來方便不需要去刪除好幾個地方,且會自動注冊 models
  2. 有約定式路由去掉了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' },
]
  1. 好用的動態路由(好用在頁面間的傳值和取值)
  • 比如要實現從列表跳轉到訂單詳情,那一般需要帶一個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  
  1. 減少了配置文件,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-mobileTabBar
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, 使用了subscriptionssubscriptions的好處應該就是可以監測全局的變化, 即使和當前頁面不相關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日凌晨

 
 
48人點贊
 
 


作者:Jadeite2
鏈接:https://www.jianshu.com/p/59099cb3e28d
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM