2018年2月25日星期日

React 导读(三)

前言

React 导读(一)
React 导读(二)

在之前 2 篇文章中中学习到了写第一个 Web 组件以及常用的生命周期函数的使用,这篇文章将继续之前的目录,开始新的知识点补充:

  1. React 如何编写 Hello World!
  2. React 中三个最基础、最重要的东西
  3. React 中的 JSX
  4. 你的第一个 Web 组件
  5. React 中最开始需要关注的生命周期
  6. React 一个组件集合的简单交互
  7. React 开始一个项目的一点建议
  8. React 简单的项目结构组织

这篇文章主要会介绍第6、7的知识点。

六 & 七、React 一个组件集合的简单交互以及开始一个项目的一点建议

为什么要将6、7合在一起写呢?不是因为想偷懒...其实是脱离一个场景和合适的开始去规划组件等设计都是不合理的,多多少少都有点交集,所以将这 2 点融合在一起是更利于学习和理解的,到这里就已经不是太基础的内容了,基本上代码量会有所提高,但是分析依然会很细致。

这里用一个简单的表格的添加删除编辑搜索四个功能来作为实例吧。
因为这应该是日常开发过程中遇到过程最多的,我将参考 bootstrap-table 的方式来开发一个简单的表格组件和约定配置来做,感觉比较自由,如果你动手能力好且业务稍大和复杂可以参考 antd 设计规范来实现,目前市面上应该蚂蚁这套用的比较多,但是这并不意味着我们就一定是按照他来做,实际项目不复杂的情况是可以使用更简单的方式。

做这个开始之前,首先要假设一点场景和基本需求,这样才能带着去思考如何实现以及更接近需求目标。

(1) 场景

为了更清晰的安排年前年后的工作和值班,现在要对过年期间人员请假的情况进行统计,并且进行一个简单的管理。

(2) 功能性需求

  • 添加员工的请假信息
  • 展示添加的员工请假的列表
  • 能够对信息进行修改
  • 能够删除添加的信息,由于不可恢复,所以需要一个提示
  • 能够根据员工的名字进行搜索

简单描述了一下,其实就之前说的几个功能。

最后做出来的效果如下(=.=没有设计,对齐就行哈):

看之前可以下载源代码对照着看,不过代码可能会不断修改 BUG,哈哈~有 BUG 不要虚,没有 BUG 我们可能就失业了。
源码-GitHub

(3) 准备工作

  1. 整理需要用到的技术
  2. 开发要用的基础 UI 组件
  3. 看下 bootstrap-table 的基本设计
  4. 搭建项目目录

1. 需要用到的技术
需要用到的技术:React/ES6, CSS 即可

2. 基础 UI 组件
根据我们这里的功能来看,我们只需要下面这几个基础组件即可:
Button, Dialog, Input, Table, Radio

在这个例子项目里面,组件的划分结构如下:

为什么要这样划分呢?

  • 基础组件:其实这个是每一个项目都需要的,如果说太小的项目不需要其实大多数是考虑掉了项目的迭代周期的考量以及以后代码的可复用性,顾名思义,基础组件就是你要在以后的组件编写过程中需要依赖的最基础的组件,基本是只负责 UI 层面的职责,当然你还能够再剥离,这里就不太展开了,知道这一层是为了以后写组件能够有自己的基础组件即可。

  • 业务组件、模块组件:在我们开发好基础组件过后,其实这些基础组件是不具备任何业务价值的,比如有了业务设计稿后,我们需要针对业务然后编写业务中公用的组件或者封装使用操作2次的组件代码,形成一个可复用的业务组件或者业务模块类型的组件。比如我这里会将每个模块用到的模块标题封装成一个 ModTitle 组件,这样以后修改这里样式的时候全部就在一个地方修改,或者在业务系统上会有 Layout 相关的布局组件需求,再比如系统中表格整个一块的需求,包含搜索、头部操作按钮、数据展示表格,这三者能够进行一个通用性的封装来形成业务模块上的表格使用组件,增加编写模块的效率,当然这里我并没有封装,因为封装和重构并不是软件初始开发更应该注重的,而是遇到第二次的时候再反过来思考如何避免重复或者让组件内部封装。

  • 展示组件、容器组件:这一层就是网上流行的展示型组件、容器组件的一层,我这里划分主要是跟具体业务功能有关系的一层。由于我这里没有 react-router,所以复杂度会低一些,后面有时间也可以介绍。

3. bootstrap-table 生成表格的方式

可以查看 github-bootstrap-table 的使用例子来看下使用方式,这里我用它做例子并不是说此库完全好或者不好,而是以前项目用了 bootstrap-table 然后就模仿了 columns 配置的方式,对于它 API 设计的其他部分暂时没采用。
表格组件其实是管理类系统很核心的部分,一是用的多,二是本身也比较复杂,封装太死缺少灵活性,封装太简单缺少效率,种类也比较多。大体上我会采用字段进行配置的方式,具体看后面的代码和分析。

4. 项目的目录规划
上面介绍了一些概念性的东西,那么项目主要的目录单独提一下,这里的项目目录不适合大型项目,但是需要一个这个过程,来理解每一项的意思以及为什么我们还需要其他技术来解决你遇到的问题,堆技术的做法是不可取的,至少在不疯狂 KPI 模式的情况下。

├── public
│   ├── favicon.ico
│   ├── index.html
│   └── manifest.json
├── server // 网站后端的目录,这里我们不需要关系
├── src // 前端的源代码目录
│   ├── App.js // App 的入口组件
│   ├── apis // API 请求层的相关文件,Ajax 的方法也是需要适配的,比如常见的拦截器做法
│   ├── app.css
│   ├── assets // 一些静态资源
│   ├── components // 包含了业务组件、模块组件、展示组件,这里项目较小的时候不需要划分太细,但是要有这样的分层来组织代码
│   ├── containers // 容器组件,主要的副作用等逻辑组件,基本上是数据初始化、维护一个较顶层的数据入口
│   ├── index.css
│   ├── index.js // 网站的入口 JS 文件,主要是负责组件挂载到 DOM,或者你也可以做一些全局注入的一些操作
│   ├── normalize.css
│   ├── registerServiceWorker.js
│   ├── smarty // 基础组件的目录,这里我叫它 smarty,命名空间使用 st-,这个随你高兴
│   ├── stores // 数据操作的主要聚焦地方,每一个 Store 都能是一个事件订阅者,用于连接 React View 组件
│   └── utils // 一些工具辅助函数,目前我这里没有使用,真实项目肯定会用上的

(4) 开始思考要如何开始写代码

1. 需要一个 React 的容器组件来渲染我想要的一个功能模块;
2. 功能模块的数据需要一个地方进行管理。

要解决第一个问题,假设我们的容器组件叫 EmployeeManage,那么在最外层的 App 组件中应该声明要渲染它,代码就会是这样:

class App extends Component {
    render() {
        return (
            <div className="App">
                <EmployeeManage />
            </div>
        );
    }
}

好了,假设这样会出现最初的那个效果图的样子,那么数据并不想写的太过于零散,所以我定义了一个 Store 类进行管理,为什么是类呢?现在不是流行 Redux 之类函数式的么?一是在最开始学习的时候增加太多技术栈心累,二是不一定要用 Redux 我们才能写好 React,三是感觉也不太必要就我们目前的需求来看,四是我就想最初简简单单的。
但是现在我们是数据驱动方式的编程,数据变了来通知 React 的 state 变了然后 React 去帮我们做视图的更新,所以,我们的 Store 得是一个基于事件的类,要有事件应该有的特征:监听。所以最后我需要一个 EmployeeStore

// 用下自带的,你也可以自己实现一个简单的
import EventEmitter from 'events';
import assign from 'object-assign';

const state = {};

const EmployeeStore = assign({}, EventEmitter.prototype, {
    // 把容器组件的 this.state 在这里管理
    getState() {
        return state;
    }
});

原始是原始了一点,但是应该很好理解,那就是我的 EmployeeStore 拥有了 EventEmitter.prototype 的东西,比如常用的 on(), off(), emit() 等方法来实现事件特性。
然后我们需要把 EmployeeManageEmployeeStore 连接起来,最简单的连接像这样子:

class EmployeeManage extends React.Component {
    constructor() {
        super();
        // 看这里
        this.state = EmployeeStore.getState();
    }
}

连接了这个基础的东西,我们的 EmployeeStore 不是还可以订阅事件么?然后数据修改了我们就触发一下订阅的事件去告诉 EmployeeManage 然后通过 this.setState 去更新视图即可,整个关系如下:

看图可能就更直观的知道数据和组件之间的关系了,用过 Flux 可能可以发现还比较像,但是这是两个不同的理念,我这里只是一个最基础的事件系统,所以会特别简单。

我们现在来订阅一个名为 updateList 的事件,用来表示表格中需要展示每条数据。我们需要在 EmployeeManage 中加入下面的代码:

componentDidMount() {
    EmployeeStore.on('updateList', this.handleUpdateList);
}

componentWillUnMount() {
    EmployeeStore.off('updateList', this.handleUpdateList);
}
    
handleUpdateList(list) {
    this.setState(prevState => {
        return {
            list: list,
        };
    });
}

这三个方法能跟上面的图对应一下,就对应上了 EmployeeManageComponent,那么我们的 Store 需要怎么做呢?

getList() {
    // API 请求列表数据的方法,返回一个 Promise
    EmployeeApi.get().then(result => {
        if(result.status === 200) {
            // 刚好,就通知了 EmployeeManage 说我数据获取成功了,可以更新视图了
            this.emit('updateList', result.data);
        }
    });
},

以上就完成了连接工作了,基本上剩下的就是码代码,往上累积功能。
先写到这里吧,太长看着也累,分一下章节吧~其实架子已经差不多了,剩下的就是写功能点了。如果觉得看文章太慢可以直接看源码可能会更快更直接一点,没有数据层,其实并不是太好,先理解视图和关系吧。

没有评论:

发表评论