Skip to content

Latest commit

 

History

History
288 lines (233 loc) · 8.54 KB

开发文档.md

File metadata and controls

288 lines (233 loc) · 8.54 KB
title date tags
开发文档
2018-03-28 04:44:25 -0700
开发文档
开发规约

[TOC]

开发文档

项目结构

.
├── dist/                          // 构建产物目录
└── src/                           // 源码目录,可选,把里面的内容直接移到外面即可
    ├── constants/                 // 常量文件
        ├── Images.js              // 图片资源管理
        └── TopicName.js           // 服务相关配置
    ├── layouts/
    │   └── index.js               // 全局布局
    ├── pages/                     // 页面目录,里面的文件即路由
        ├── user/                  // 用户中心页面
            ├── components/        // 组件分化
                ├── Item.js        // 组件 Item
                └── Item.less      // 组件 Item 样式
            ├── models/            // redux 相关
            ├── services/          // 服务层
            ├── page.js                
            └── page.less          
        ├── document.ejs           // HTML 模板
        ├── 404.js                 // 404 页面
        └── page1.js               // 页面 1,任意命名
    ├── global.less                // 约定的全局样式文件,自动引入
├── test/                          // 测试用例放这里
├── .umirc.js                      // umi 配置
├── .webpackrc                     // webpack 配置
└── package.json

页面(page)开发

每个单页都需要在 pages 文件夹下新建一个对应的文件夹,该文件夹名会自动作为路由名,如上 pages/user 文件夹,文件夹下新建一个 page.js(强制要求以 page.js 命名) 作为页面入口,

该页中用到的组件需要放到 components 文件夹内,如 pages/user/components

redux 等状态管理放到 models 文件夹中,如 pages/user/models

服务请求放到 services 文件夹中,如 pages/user/services

页面(组件)的样式文件需与页面(组件)文件同目录且同名,样式文件的后缀名统一采用 .less

组件

组件不涉及视图刷新操作的统一采用 React.PureComponent ,不了解 PureComponent 的暂时使用 Component 代替,避免一些不必要的麻烦,

组件文件名和组件命名统一采用大写字母开头的驼峰式命名法

静态资源

图片资源统一在 constants/Images.js 中管理,在引用时引入,如下:

// 引入
import images from '../constants/Images';
...
// 使用
<img src={ images.DEFAULT_AVATAR } />

使用 Socket

1. 结合 redux

在页面文件中引用 connect,如下:

import { connect } from 'dva';

class UserPage extends React.Component {
    
}

const mapStateToProps = (state) => ({
    
})

export default connect(mapStateToProps)(UserPage);

在 page 对应的目录下新建 models 文件夹,在 models 文件夹内新建一文件,如 pages/user/models/user.js,内容如下:

export default {
    namespace: 'user',  // 针对页面取名,会作为全局 state 中的 key 值
    state: {
        // 页面所需 state
    },
    reducers: {
        // 改变的 state 的函数
    },
    effects: {
        // 网络请求及数据处理函数
    },
    subscriptions: {
        
    }
}

然后在页面通过 connect 的特性能获取到对应的 state

// 此处 state 为整个应用的全局 state(仅针对 models 中的 state,页面内的 state 不在计算内)
const mapStateToProps = (state) => ({
    user: state.user,  // 获取 user 模块的 state
})

2. 发送 Action(发送网络请求)

在页面目录下新建 services 目录,新建一个文件,如:pages/user/services/user.js

/**
 * 网络请求
 * @param {Object} socket Socket 连接(必需)
 * @param {Object} params 服务所需参数
 */
export function reqUserInfo(socket, params) {
    // 固定写法
    socket.sendToServer('MessageType', 'MessageName', { ...params })
}

在需要改变 state 的时候需要发送一个 Action,需要获取 socket,如:pages/user/page.js

class UserPage extends React.Component {
    
    // 需要 socket 时必需声明
    static contextTypes = {
        socket: PropTypes.object.isRequired,
    }
    
    componentDidMount() {
        const { dispatch } = this.props;  // connect 之后默认带有 dispatch
        const { socket } = this.context;  // 使用 socket 做网络请求
        dispatch({
            type: 'user/reqUserInfo',  // 格式为: models.namespace + reducers/effects 中函数名
            payload: {  // 传递数据
                socket,  // 网络请求必传 socket
                params: {
                    userId: 123
                }
            }
        })
    }
}

在 models 中定义 reqUserInfo 函数,函数名与 page 中发送的 type 函数同名,如 pages/user/models/user.js

// 引入对应的 service 服务文件
import * as services from '../services/user.js';

export default {
    namespace: 'user',  // 针对页面取名,会作为全局 state 中的 key 值
    state: {
        // 页面所需 state
    },
    reducers: {
        // 改变的 state 的函数
    },
    effects: {
        // 网络请求及数据处理函数,此函数名与页面中发的 Action 的 type 函数名一致
        *reqUserInfo({ payload }, { call, put }) {
            // 调用 service 服务并传递参数
            yield call(services.reqUserInfo, payload.socket, { userId: payload.params.userId })
        }
    },
    subscriptions: {
        
    }
}

3. 接收服务返回

在页面中接收服务返回需要引用已封装好的 Event 组件,如 pages/user/page.js

import Event from '../../socket/Event';

class UserPage extends React.Component {
    
    /**
     * 接收服务返回
     * @param {Object} response 服务返回结果
     */
    handleResponse = (response) => {
        // 处理服务返回结果
        // 我们统一将服务返回发送到 models 中处理
        const { dispatch } = this.props;
        dispatch({
            type: 'user/resUserInfo',
            payload: { response }  // 处理数据不需要 socket
        })
    }
    
    render() {
        return (
        	<div>
            	{/* event 属性为固定值 'svevent',handler 为接收服务返回的回调 */}
            	<Event event="svevent" handler={this.handleResponse} />
            </div>
        )
    }
}

在 models 中处理服务返回数据,如 pages/user/models/user.js

import { Toast } from 'antd-mobile';

export default {
    namespace: 'user',  // 针对页面取名,会作为全局 state 中的 key 值
    state: {
        // 页面所需 state
        userInfo: {},
    },
    reducers: {
        // 改变的 state 的函数
        userInfoSuccessed(state, {payload}) {
            // 返回新的数据
            return {...state, userInfo: payload.userInfo}
        }
    },
    effects: {
        // 网络请求及数据处理函数
        *resUserInfo({ payload }, { call, put }) {
            // 判断是否成功调用服务,可能存在不同接口返回的状态不一样
            if(payload.response.state === 1) {
                // 成功返回,改变 state 值,渲染视图,改变 state 统一在 reducer 中处理
                yield put({
                    type: 'userInfoSuccessed',  // 在models 文件中发送 Action 不需要 models.namespace
                    payload: { userInfo: payload.data }
                })
            } else {
                // 错误返回,提示错误信息
                Toast.info(response.message)
            }
        }
    },
    subscriptions: {
        
    }
}

得到服务返回的数据后在页面中显示,如 pages/user/page.js

class UserPage extends React.Component {
    render() {
        // 获取信息
        const { userInfo } = this.props;
        return (
        	<div>
            	{/* 将信息显示在前端页面 */}
            	<div>用户名:{ userInfo.username }</div>
            </div>
        )
    }
}

/**
 * 将 redux 中的 state 取出作为 page 的 props
 * @param {Object} state 应用整体 state
 */
const mapStateToProps = (state) => ({
    userInfo: state.user.userInfo,  // 得到对应 models 中的 state,格式为 state[models.namespace][key]
})

export default connect(mapStateToProps)(UserPage);