zoukankan      html  css  js  c++  java
  • 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
    来源:简书
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
    漫思
  • 相关阅读:
    很不错的WebCart控件,分享给大家
    Atitit 功能扩展法细则条例 目录 1. 界面ui扩展 2 1.1. 使用h5做界面 2 1.2. 自制h5 ide。。简化ui自定义配置 2 2. 业务逻辑扩展 2 2.1. Bpm流程引擎还
    Atitit 持久化与数据存储标准化规范 目录 1. 存储的附加功能 2 1.1. 基本存取功能 2 1.2. 全文检索(imap 2 1.3. 属性检索 2 1.4. 查询语言 2 2. 基于内容
    Atitit 常见硬件集成列表 目录 1.1. 小程序设备类 1 1.2. atitit.常见手机的传感器与外设 attilax总结 1 1.3. Pc机外设 1 1.4. 设备管理器 2 1.1
    Atitit 项目wechat微信截屏生成vcf通讯录384 个 384个人 42个节拍,平均每个8个人 技术点 im图像裁剪, ocr Tesseract Vcf格式 /wechatTel
    atitit 音频 项目 系列功能表 音乐 v3 t67.docx Atitit 音频 项目 系列功能表 音频 音乐 语言领域的功能表 听歌识曲功能 酷我功能。 铃声 功能。。 音频切割(按照副歌部
    Atitit spring springboot 集成mybatis法 目录 1.1. 使用spring管理数据源。。需要修改spring、 配置 1 1.2. 直接代码集成,无需修改任何配置 1
    Atitit 艾提拉音频资源列表与统计 t6 六月份战果与7月份规划.docx 目录 1. 第一层次 原始资源类 采集资源类 1 1.1. K歌类采集资源 整理 1 1.2. K歌类资源初步分类
    Atitit 长距离无线通信法 LoRa NBIoT NBCIoT LoRa是Semtech公司的创新发明,该技术向用户提供显著的长距离、低功耗、安全数据传输机制。使用LoRa技术构建的公用网
    Atitit 读取音频音乐文件的bpm 目录 1.1. Librosa是一个用于音频、音乐分析、处理的python工具包, 1 1.2. \bpm.py 1 1.3. Echo 2 1.4. Cod
  • 原文地址:https://www.cnblogs.com/sexintercourse/p/13772785.html
Copyright © 2011-2022 走看看