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

##介紹 最近開發了一個react項目,由於以前都是作原生混合H5開發,對redux用的不怎麼熟練,此次想要鍛鍊下而後花幾天看了一下redux和看了幾個搭建方案,以及看了下公司其餘的H5項目(直接用redux的項目)以爲很複雜繁瑣。用過ant-design-pro 2.0正式版(加入了umi的版本)以爲很不錯,因此決定從UMI入手搭一個項目來作手機端的H5項目,在去掉大部分業務代碼後把這個demo拿來給你們分享一下,但願對新手搭建umi項目有必定價值。html

寫這篇文章是爲了幫助須要的人更好的理解umi來搭建項目,第一次寫文章不足之處望指正 本文的例子的demo在個人 github 地址能夠下載參考 github地址 以爲對本身理解和上手umi項目有幫助的點個starnode

##建立項目react

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

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

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

  • 安裝依賴
yarn
複製代碼
  • 啓動項目
yarn start
複製代碼
  • 編譯打包
umi build
複製代碼

##我對umi的一些理解express

  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 渲染子組件。npm

+ 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.jsjson

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] }redux

request文件

須要根據本身的項目和後臺的接收方法對請求方式進行封裝api

  • 例如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',
          });
        }
      });
    },
  },
};
複製代碼
相關文章
相關標籤/搜索