umi + dva + ant-design-mobile快速搭建H5項(xiàng)目

介紹

最近開(kāi)發(fā)了一個(gè)react項(xiàng)目,因?yàn)橹岸际亲鲈旌螲5開(kāi)發(fā),對(duì)redux用的不怎么熟練,這次想要鍛煉下然后花幾天看了一下redux和看了幾個(gè)搭建方案,以及看了下公司其他的H5項(xiàng)目(直接用redux的項(xiàng)目)覺(jué)得很復(fù)雜繁瑣。用過(guò)ant-design-pro 2.0正式版(加入了umi的版本)覺(jué)得很不錯(cuò),所以決定從UMI入手搭一個(gè)項(xiàng)目來(lái)做手機(jī)端的H5項(xiàng)目,在去掉大部分業(yè)務(wù)代碼后把這個(gè)demo拿來(lái)給大家分享一下,希望對(duì)新手搭建umi項(xiàng)目有一定價(jià)值。

寫這篇文章是為了幫助需要的人更好的理解umi來(lái)搭建項(xiàng)目,第一次寫文章不足之處望指正
本文的例子的demo在我的 github 地址可以下載參考 github地址
覺(jué)得對(duì)自己理解和上手umi項(xiàng)目有幫助的點(diǎn)個(gè)star

創(chuàng)建項(xiàng)目

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

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

會(huì)出現(xiàn)一個(gè)選擇框,我選擇了antd,dll,hard source。這幾個(gè)配置在umi官網(wǎng)快速上手里面有配置的解釋。
確保 node 版本是 8.10 或以上

  • 安裝依賴
yarn
  • 啟動(dòng)項(xiàng)目
yarn start
  • 編譯打包
umi build

我對(duì)umi的一些理解

  1. 在dva 項(xiàng)目通常都是要單獨(dú)寫一個(gè) models,然后所有的models寫在里面。
    用了 umi 后,可以在pages同級(jí)下寫一個(gè)models來(lái)管理所有的models也可以在每個(gè)頁(yè)面的文件夾下寫一個(gè)models文件夾來(lái)放當(dāng)前頁(yè)面需要用的models,好處是結(jié)構(gòu)更加清晰了,刪除起來(lái)方便不需要去刪除好幾個(gè)地方,且會(huì)自動(dòng)注冊(cè) models
  2. 有約定式路由去掉了router.js,umi 會(huì)根據(jù) pages 目錄下的頁(yè)面的js自動(dòng)生成路由配置??梢詤⒖?a target="_blank" rel="nofollow">umi官網(wǎng)路由部分,
  • 假設(shè) pages 目錄結(jié)構(gòu)如下:
+ pages/
  + users/
    - index.js
    - index.less
  - index.js
  • 那么,umi 會(huì)自動(dòng)生成路由配置如下:
[
  { path: '/', component: './pages/index.js' },
  { path: '/users/', component: './pages/users/index.js' },
]
  1. 好用的動(dòng)態(tài)路由(好用在頁(yè)面間的傳值和取值)
  • 比如要實(shí)現(xiàn)從列表跳轉(zhuǎn)到訂單詳情,那一般需要帶一個(gè)id過(guò)去,在詳情頁(yè)拿id獲取一些數(shù)據(jù),那在umi里面我們?cè)趺醋觯?/li>
+ pages/
  + orderdetail/
    - $id$.js
    - index.less
  • 那在list跳轉(zhuǎn)上面的orderdetail頁(yè)面的時(shí)候我們要這么寫(假設(shè)val.id=15),
    記得要從import router from 'umi/router'
router.push('/orderdetail/' + val.id)     
  • 在orderdetail頁(yè)面取這個(gè)id(可以自己打印一下this.props看看)
this.props.match.params.id  
  1. 減少了配置文件,umi的 package.json 里會(huì)少很多依賴,再比如創(chuàng)建項(xiàng)目的時(shí)候選的antd那就包含了
  • antd
  • antd-mobile
  • babel-plugin-import

上demo

運(yùn)行后的效果

首頁(yè)
登陸
個(gè)人中心
全局layout組件

約定 src/layouts/index.js 為全局路由,返回一個(gè) React 組件,通過(guò) props.children 渲染子組件。

+ layouts/
  + baseLayout/
    - index.js
    - index.less
  - index.js
  • 首先先看下 layouts 下的 index.js
import React, { Component } from 'react'
import BaseLayout from './baseLayout';  // 底部導(dǎo)航的組件

const ULR_NO_LAYOUT = ['/', '/home', '/class', '/my'];  //判斷在哪幾個(gè)路由下需要出現(xiàn)底部導(dǎo)航
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變量是為了判斷在哪幾個(gè)路由下需要出現(xiàn)底部導(dǎo)航
  • BaseLayout是用 antd-mobileTabBar
mock數(shù)據(jù)

約定 mock 目錄里所有的 .js 文件會(huì)被解析為 mock 文件。我們新建一個(gè)home.js

export default {
  // 支持值為 Object 和 Array
  'GET /api/users': { users: [1, 2] },

  // GET POST 可省略
  '/api/users/1': { id: 1 },

  // 支持自定義函數(shù),API 參考 express@4
  'POST /api/users/create': (req, res) => { res.end('OK'); },
};

直接請(qǐng)求/api/users就能拿到 { users: [1, 2] }

request文件

需要根據(jù)自己的項(xiàng)目和后臺(tái)的接收方法對(duì)請(qǐng)求方式進(jìn)行封裝

  • 例如request的49行和56行,是我對(duì)自己項(xiàng)目請(qǐng)求接口時(shí)增加的token,可以去掉
// body 添加token
  if (newOptions.body) {
    newOptions.body.__token__ = getToken();
  } else {
    newOptions.body = {
      __token__: getToken(),
    };
  }
  • 例如request的86行和89行增加對(duì)get傳值的轉(zhuǎn)換,setUrlEncoded方法,這樣處理方便get方法傳值的時(shí)候也能和post方法一樣穿對(duì)象,自動(dòng)轉(zhuǎn)換帶在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行需要根據(jù)自己的項(xiàng)目的返回值來(lái)做修改判斷是否返回response
獲取和驗(yàn)證表單的值
  • 推薦使用 rc-form
import { createForm } from 'rc-form';
@createForm()
  • 調(diào)用時(shí) 需要從this.props里拿到form里的getFieldProps, getFieldError

render() {
    const {form: {getFieldProps, getFieldError}, regLoading} = this.props;
    <div>
      <InputItem
            {...getFieldProps('name', {
              initialValue: '',
              rules: [{
                  required: true,
                  message: '請(qǐng)輸入用戶名',
                }],
            })}
            clear
            error={!!getFieldError('name')}
            onErrorClick={() => {
              const err = getFieldError('name').join('、');
              Toast.info(err, 1);
            }}
            placeholder='用戶名'
          />
    </div>
}
  • 提交驗(yàn)證form輸入內(nèi)容的時(shí)候,例如
  submit(){
    const {form, dispatch} = this.props;
    form.validateFields((error, fieldsValue) => {
      if (error) {
        return;
      }
      dispatch({
        type: 'login/submit',
        payload: {
          // 入?yún)?        },
        callback: (res) => {
          //需要實(shí)現(xiàn)什么
        }
      });
    });
  }
models調(diào)用和models文件
  • 例如在home的index.js要調(diào)用
import { connect } from 'dva';
@connect(({ home }) => ({ home }))
  • home的models文件夾home.js, 使用了subscriptions,subscriptions的好處應(yīng)該就是可以監(jiān)測(cè)全局的變化, 即使和當(dāng)前頁(yè)面不相關(guān)model里也可以進(jìn)行數(shù)據(jù)改動(dòng)或者請(qǐng)求接口.當(dāng)然你也可以選擇在頁(yè)面中使用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日凌晨

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容