React+Flux

###React+Flux

React 不使用 HTML,而使用 JSX 。它打算抛弃 DOM,要求开发者不要使用任何 DOM 方法,因为Dom操作会引起浏览器的重绘,非常影响性能,所以react做了一件事,发明出了虚拟DOM,这里推荐一篇react原理。它甚至还抛弃了 SQL ,自己发明了一套查询语言 GraphQL 。当然,这些你都可以不用,React 照样运行,但是就发挥不出它的最大威力。

在了解Flux前我们先需要了解下React的生命周期

一.理论

  • 组件本质上是状态机,输入确定,输出一定确定
  • 生命周期的三个阶段,三者时间是不固定的,只是在逻辑上的分类

二. 这里是列表文本. 初始化阶段:

  • getDefaultProps:获取实例的默认属性(即使没有生成实例,组件的第一个实例被初始化CreateClass的时候调用,只调用一次,)
  • getInitialState:获取每个实例的初始化状态(每个实例自己维护)
  • componentWillMount:组件即将被装载、渲染到页面上(render之前最好一次修改状态的机会)
  • render:组件在这里生成虚拟的DOM节点(只能访问this.props和this.state;只有一个顶层组件,也就是说render返回值值职能是一个组件;不允许修改状态和DOM输出)
  • componentDidMount:组件真正在被装载之后,可以修改DOM

三、运行中状态:

  • componentWillReceiveProps:组件将要接收到属性的时候调用(赶在父组件修改真正发生之前,可以修改属性和状态)
  • shouldComponentUpdate:组件接受到新属性或者新状态的时候(可以返回false,接收数据后不更新,阻止render调用,后面的函数不会被继续执行了)
  • componentWillUpdate:不能修改属性和状态
  • render:只能访问this.props和this.state;只有一个顶层组件,也就是说render返回值只能是一个组件;不允许修改状态和DOM输出
  • componentDidUpdate:可以修改DOM

四、销毁阶段:

  • componentWillUnmount:开发者需要来销毁(组件真正删除之前调用,比如计时器和事件监听器)

五、demo查看:

我这里有一个关于react生命周期的例子,大家打开后,打开控制台会看到打印出的生命周期的详细信息。

https://github.com/jdbi336534/React-lifeCycle.git

六,Flux

同时我这里提供一个Demo,大家可以下载安装下,那么我就用这个来讲解下。建议大家下载运行下,才能真正清晰明了的了解Flux的工作流程。

这个Demo主要功能,当我们输入文字后,点击“New item”按钮后,就会逐条添加到下面,当我们点击每条后面的删除按钮就会删除当前的文字。

$ git clone https://github.com/jdbi336534/React-TodoList.git

$ cd React-TodoList && npm install

$ npm run start

然后,访问 http://localhost

输入图片说明

那么,我们先来看看flux个整个流程图。 输入图片说明

Flux将一个应用分成了四个部分。

  • View: 视图层
  • Action(动作):视图层发出的消息(比如mouseClick)
  • Dispatcher(派发器):用来接收Actions、执行回调函数
  • Store(数据层):用来存放应用的状态,一旦发生变动,就提醒Views要更新页面

Flux的最大特点,就是数据的“单向流动”。

  • 用户访问 View
  • View 发出用户的 Action
  • Dispatcher 收到 Action,要求 Store 进行相应的更新
  • Store 更新后,发出一个"change"事件
  • View 收到"change"事件后,更新页面

上面过程中,数据总是"单向流动",任何相邻的部分都不会发生数据的"双向流动"。这保证了流程的清晰。

我整个项目的结构目录如下:

输入图片说明

1. view部分

打开Demo首页app.js,只加载了一个组件

//app.js
'use strict';
import '../styles/usage/page/app.scss';

import React from 'react';
import ReactDOM from 'react-dom';
import MyButtonController from './flux/components/MyButtonController.jsx';

let app = document.getElementById('app');


ReactDOM.render(
<MyButtonController />
  , app);

if (module.hot) {
  module.hot.accept();
}

上面的代码中,我们的组件采用的是 React 的 controller view 模式。"controller view"组件只用来保存状态,然后将其转发给子组件。MyButtonController的源码很简单。

// components/MyButtonController.jsx
import React from 'react';
import MyButton from './MyButton.jsx';
import ButtonActions from '../actions/ButtonActions.js';
import ListStore from '../stores/ListStore.js';
export default React.createClass({
  getInitialState(){
    return{
      items:ListStore.getAll()
    }
  },
  createNewItem(val){
     ButtonActions.addNewItem(val);
    // console.log(val);
  },
  deleteli(index){
    ButtonActions.delLi(index);
  },
  componentDidMount(){
    ListStore.addChangeListener(this._onChange);
  },
  componentWillUnmount(){
    ListStore.removeChangeListener(this._onChange);
  },
  _onChange(){
    this.setState({
      items:ListStore.getAll()
    })
  },
    render() {
        return (
            <MyButton onClick={this.createNewItem} items={this.state.items} onDel={this.deleteli}/>

        )
    }
})

上面代码中,MyButtonController将参数传给子组件MyButton。

// components/MyButton.jsx
import React from 'react';
export default React.createClass({
  handleVal(event){
    let val=this.refs.listtodo.value;
    this.refs.listtodo.value="";
    // console.log(this.refs);
    this.props.onClick(val);

  },
  delwli(index){

    this.props.onDel(index);

  },
    render() {
      let items=this.props.items;
      let itemHTML=items.map((listItem,i)=>{
        return <li key={i}>{listItem}<button onClick={this.delwli.bind(this,i)}>X</button></li>
      });
        return (
            <div>
            <input type="text" ref="listtodo" placeholder="请输入文字。"/>
            <button onClick={this.handleVal}>New item</button>
                <ul>
                   {itemHTML}
                </ul>
            </div>
        )
    }
})

这里有点小知识,就是map方法,大家要了解, 输入图片说明 需要注意的是这里需要通过bind来传递参数,即this.delwli.bind(this,i),不能这样this.delwli(i),这样会直接执行这个函数的。 你可以看到MyButton是不一个纯组件(即不含有任何状态),这里因为我要子组件传值给父组件。那么有一个更好的方法就是,在父组件中通过event.target方法来取到子组件里的值。 如下图: 输入图片说明

MyButton只有一个逻辑,就是一旦用户点击,就调用this.createNewItem 方法,向Dispatcher发出一个Action。

  createNewItem(val){
     ButtonActions.addNewItem(val);
    // console.log(val);
  }

上面代码中,调用createNewItem方法,会触发名为addNewItem的Action。

##2.Action

每个Action都是一个对象,包含一个actionType属性(说明动作的类型)和一些其他属性(用来传递数据)。 在这个Demo里面,ButtonActions 对象用于存放所有的Action。

// actions/ButtonActions.js
import AppDispatcher from '../dispatchers/AppDispatcher.js';
export default {
  addNewItem(text){
    AppDispatcher.dispatch({
      actionType:'ADD_NEW_ITEM',
      text:text
    })
  },
  delLi(index){
    AppDispatcher.dispatch({
    actionType:'DELETE_LI',
    index:index
      })
  }
}

上面代码中,ButtonActions.addNewItem方法使用AppDispatcher,把动作ADD_NEW_ITEM派发到Store。

3.Dispatcher

Dispatcher 的作用是将 Action 派发到 Store、。你可以把它看作一个路由器,负责在 View 和 Store 之间,建立 Action 的正确传递路线。注意,Dispatcher 只能有一个,而且是全局的。 Facebook官方的 Dispatcher 实现输出一个类,你要写一个AppDispatcher.js,生成 Dispatcher 实例。

// dispatchers/AppDispatcher.js
import  {Dispatcher} from 'flux';
import ListStore from '../stores/ListStore.js';
let AppDispatcher=new Dispatcher();
 AppDispatcher.register(action => {
  switch(action.actionType){
    case 'ADD_NEW_ITEM':
      ListStore.addNewItemHandle(action.text);
      ListStore.emitChange();
      break;
  }
});
AppDispatcher.register(action => {
 switch(action.actionType){
   case 'DELETE_LI':
     ListStore.delItemHandle(action.index);
     ListStore.emitChange();
     break;
 }
});
export default AppDispatcher;

AppDispatcher.register()方法用来登记各种Action的回调函数。 上面代码中,Dispatcher收到ADD_NEW_ITEM动作,就会执行回调函数,对ListStore进行操作。 记住,Dispatcher 只用来派发 Action,不应该有其他逻辑。

4.store

Store 保存整个应用的状态。它的角色有点像 MVC 架构之中的Model 。 在我们的 Demo 中,有一个ListStore,所有数据都存放在那里。

import { EventEmitter } from 'events';
export default Object.assign({}, EventEmitter.prototype ,{
  items:[],
  getAll(){
    return this.items;
  },
  addNewItemHandle(text){
    // this.items.push(text);
    this.items.unshift(text);
  },
  delItemHandle(index){
    this.items.splice(index,1);
  },
  emitChange(){
    this.emit('change');
  },
  addChangeListener(callback){
    this.on('change',callback);
  },
  removeChangeListener(callback){
    this.removeListener('change',callback);
  }

})

ListStore.items用来保存条目,ListStore.getAll()用来读取所有条目,ListStore.emitChange()用来发出一个"change"事件。 由于 Store 需要在变动后向 View 发送"change"事件,因此它必须实现事件接口。 ListStore继承了EventEmitter.prototype,因此就能使用ListStore.on()和ListStore.emit(),来监听和触发事件了。 Store 更新后(this.addNewItemHandler())发出事件(this.emitChange()),表明状态已经改变。 View 监听到这个事件,就可以查询新的状态,更新页面了。

##5.View 现在,我们再回过头来看 View ,让它监听 Store 的 change 事件。

// components/MyButtonController.jsx
import React from 'react';
import MyButton from './MyButton.jsx';
import ButtonActions from '../actions/ButtonActions.js';
import ListStore from '../stores/ListStore.js';
export default React.createClass({
  getInitialState(){
    return{
      items:ListStore.getAll()
    }
  },
  createNewItem(val){
     ButtonActions.addNewItem(val);
    // console.log(val);
  },
  deleteli(index){
    ButtonActions.delLi(index);
  },
  componentDidMount(){
    ListStore.addChangeListener(this._onChange);
  },
  componentWillUnmount(){
    ListStore.removeChangeListener(this._onChange);
  },
  _onChange(){
    this.setState({
      items:ListStore.getAll()
    })
  },
    render() {
        return (
            <MyButton onClick={this.createNewItem} items={this.state.items} onDel={this.deleteli}/>

        )
    }
})

上面代码中,你可以看到当MyButtonController 发现 Store 发出 change 事件,就会调用 this._onChange 更新组件状态,从而触发重新渲染。

转载于:https://my.oschina.net/u/1271438/blog/785817

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值