关闭 x
IT技术网
    技 采 号
    ITJS.cn - 技术改变世界
    • 实用工具
    • 菜鸟教程
    IT采购网 中国存储网 科技号 CIO智库

    IT技术网

    IT采购网
    • 首页
    • 行业资讯
    • 系统运维
      • 操作系统
        • Windows
        • Linux
        • Mac OS
      • 数据库
        • MySQL
        • Oracle
        • SQL Server
      • 网站建设
    • 人工智能
    • 半导体芯片
    • 笔记本电脑
    • 智能手机
    • 智能汽车
    • 编程语言
    IT技术网 - ITJS.CN
    首页 » UI前端 »Flux 架构入门教程

    Flux 架构入门教程

    2016-01-15 00:00:00 出处:ITJS
    分享

    过去一年中,前端技术大发展,最耀眼的明星就是React。

    React 本身只涉及UI层,假如搭建大型应用,必须搭配一个前端框架。也就是说,你至少要学两样东西,才能基本满足需要:React + 前端框架。

    Facebook官方使用的是 Flux 框架。本文就介绍如何在 React 的基础上,使用 Flux 组织代码和安排内部逻辑,使得你的应用更易于开发和维护。

    阅读本文之前,我假设你已经掌握了 React 。假如还没有,可以先看我写的《React入门教程》。与以前一样,本文的目标是使用最简单的语言、最好懂的例子,让你一看就会。

    一、Flux 是什么?

    简单说,Flux 是一种架构思想,专门解决软件的结构问题。它跟MVC 架构是同一类东西,但是更加简单和清晰。

    Flux存在多种实现(至少15种),本文采用的是Facebook官方实现。

    二、安装 Demo

    为了便于讲解,我写了一个Demo。

    请先安装一下。

    $ git clone https://github.com/ruanyf/extremely-simple-flux-demo.git
    $ cd extremely-simple-flux-demo && npm install
    $ npm start

    然后,访问 http://127.0.0.1:8080 。

    你会看到一个按钮。这就是我们的Demo。

    三、基本概念

    讲解代码之前,你需要知道一些 Flux 的基本概念。

    首先,Flux将一个应用分成四个部分。

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

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

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

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

    读到这里,你可能感到一头雾水,OK,这是正常的。接下来,我会详细讲解每一步。

    四、View(第一部分)

    请打开 Demo 的首页index.jsx ,你会看到只加载了一个组件。

    // index.jsx
    var React = require('react');
    var ReactDOM = require('react-dom');
    var MyButtonController = require('./components/MyButtonController');
    
    ReactDOM.render(
      <MyButtonController/>,
      document.querySelector('#example')
    );

    上面代码中,你可能注意到了,组件的名字不是 MyButton,而是 MyButtonController。这是为什么?

    这里,我采用的是 React 的 controller view 模式。”controller view”组件只用来保存状态,然后将其转发给子组件。MyButtonController的源码很简单。

    // components/MyButtonController.jsx
    var React = require('react');
    var ButtonActions = require('../actions/ButtonActions');
    var MyButton = require('./MyButton');
    
    var MyButtonController = React.createClass({
      createNewItem: function (event) {
        ButtonActions.addNewItem('new item');
      },
    
      render: function() {
        return <MyButton
          onClick={this.createNewItem}
        />;
      }
    });
    
    module.exports = MyButtonController;

    上面代码中,MyButtonController将参数传给子组件MyButton。后者的源码甚至更简单。

    // components/MyButton.jsx
    var React = require('react');
    
    var MyButton = function(props) {
      return <div>
        <button onClick={props.onClick}>New Item</button>
      </div>;
    };
    
    module.exports = MyButton;

    上面代码中,你可以看到MyButton是一个纯组件(即不含有任何状态),从而方便了测试和复用。这就是”controll view”模式的最大优点。

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

    // components/MyButtonController.jsx
    
      // ...
      createNewItem: function (event) {
        ButtonActions.addNewItem('new item');
      }

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

    五、Action

    每个Action都是一个对象,包含一个actionType属性(说明动作的类型)和一些其他属性(用来传递数据)。

    在这个Demo里面,ButtonActions 对象用于存放所有的Action。

    // actions/ButtonActions.js
    var AppDispatcher = require('../dispatcher/AppDispatcher');
    
    var ButtonActions = {
      addNewItem: function (text) {
        AppDispatcher.dispatch({
          actionType: 'ADD_NEW_ITEM',
          text: text
        });
      },
    };

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

    六、Dispatcher

    Dispatcher 的作用是将 Action 派发到 Store、。你可以把它看作一个路由器,负责在 View 和 Store 之间,建立 Action 的正确传递路线。注意,Dispatcher 只能有一个,而且是全局的。

    Facebook官方的 Dispatcher 实现输出一个类,你要写一个AppDispatcher.js,生成 Dispatcher 实例。

    // dispatcher/AppDispatcher.js
    var Dispatcher = require('flux').Dispatcher;
    module.exports = new Dispatcher();

    AppDispatcher.register()方法用来登记各种Action的回调函数。

    // dispatcher/AppDispatcher.js
    var ListStore = require('../stores/ListStore');
    
    AppDispatcher.register(function (action) {
      switch(action.actionType) {
        case 'ADD_NEW_ITEM':
          ListStore.addNewItemHandler(action.text);
          ListStore.emitChange();
          break;
        default:
          // no op
      }
    })

    上面代码中,Dispatcher收到ADD_NEW_ITEM动作,就会执行回调函数,对ListStore进行操作。

    记住,Dispatcher 只用来派发 Action,不应该有其他逻辑。

    七、Store

    Store 保存整个应用的状态。它的角色有点像 MVC 架构之中的Model 。

    在我们的 Demo 中,有一个ListStore,所有数据都存放在那里。

    // stores/ListStore.js
    var ListStore = {
      items: [],
    
      getAll: function() {
        return this.items;
      },
    
      addNewItemHandler: function (text) {
        this.items.push(text);
      },
    
      emitChange: function () {
        this.emit('change');
      }
    };
    
    module.exports = ListStore;

    上面代码中,ListStore.items用来保存条目,ListStore.getAll()用来读取所有条目,ListStore.emitChange()用来发出一个”change”事件。

    由于 Store 需要在变动后向 View 发送”change”事件,因此它必须实现事件接口。

    // stores/ListStore.js
    var EventEmitter = require('events').EventEmitter;
    var assign = require('object-assign');
    
    var ListStore = assign({}, EventEmitter.prototype, {
      items: [],
    
      getAll: function () {
        return this.items;
      },
    
      addNewItemHandler: function (text) {
        this.items.push(text);
      },
    
      emitChange: function () {
        this.emit('change');
      },
    
      addChangeListener: function(callback) {
        this.on('change', callback);
      },
    
      removeChangeListener: function(callback) {
        this.removeListener('change', callback);
      }
    });

    上面代码中,ListStore继承了EventEmitter.prototype,因此就能使用ListStore.on()和ListStore.emit(),来监听和触发事件了。

    Store 更新后(this.addNewItemHandler())发出事件(this.emitChange()),表明状态已经改变。 View 监听到这个事件,就可以查询新的状态,更新页面了。

    八、View (第二部分)

    现在,我们再回过头来修改 View ,让它监听 Store 的 change 事件。

    // components/MyButtonController.jsx
    var React = require('react');
    var ListStore = require('../stores/ListStore');
    var ButtonActions = require('../actions/ButtonActions');
    var MyButton = require('./MyButton');
    
    var MyButtonController = React.createClass({
      getInitialState: function () {
        return {
          items: ListStore.getAll()
        };
      },
    
      componentDidMount: function() {
        ListStore.addChangeListener(this._onChange);
      },
    
      componentWillUnmount: function() {
        ListStore.removeChangeListener(this._onChange);
      },
    
      _onChange: function () {
        this.setState({
          items: ListStore.getAll()
        });
      },
    
      createNewItem: function (event) {
        ButtonActions.addNewItem('new item');
      },
    
      render: function() {
        return <MyButton
          items={this.state.items}
          onClick={this.createNewItem}
        />;
      }
    });

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

    // components/MyButton.jsx
    var React = require('react');
    
    var MyButton = function(props) {
      var items = props.items;
      var itemHtml = items.map(function (listItem, i) {
        return <li key={i}>{listItem}</li>;
      });
    
      return <div>
        <ul>{itemHtml}</ul>
        <button onClick={props.onClick}>New Item</button>
      </div>;
    };
    
    module.exports = MyButton;

    九、致谢

    本文受到了Andrew Ray 的文章《Flux For Stupid People》的启发。

    上一篇返回首页 下一篇

    声明: 此文观点不代表本站立场;转载务必保留本文链接;版权疑问请联系我们。

    别人在看

    正版 Windows 11产品密钥怎么查找/查看?

    还有3个月,微软将停止 Windows 10 的更新

    Windows 10 终止支持后,企业为何要立即升级?

    Windows 10 将于 2025年10 月终止技术支持,建议迁移到 Windows 11

    Windows 12 发布推迟,微软正全力筹备Windows 11 25H2更新

    Linux 退出 mail的命令是什么

    Linux 提醒 No space left on device,但我的空间看起来还有不少空余呢

    hiberfil.sys文件可以删除吗?了解该文件并手把手教你删除C盘的hiberfil.sys文件

    Window 10和 Windows 11哪个好?答案是:看你自己的需求

    盗版软件成公司里的“隐形炸弹”?老板们的“法务噩梦” 有救了!

    IT头条

    公安部:我国在售汽车搭载的“智驾”系统都不具备“自动驾驶”功能

    02:03

    液冷服务器概念股走强,博汇、润泽等液冷概念股票大涨

    01:17

    亚太地区的 AI 驱动型医疗保健:2025 年及以后的下一步是什么?

    16:30

    智能手机市场风云:iPhone领跑销量榜,华为缺席引争议

    15:43

    大数据算法和“老师傅”经验叠加 智慧化收储粮食尽显“科技范”

    15:17

    技术热点

    商业智能成CIO优先关注点 技术落地方显成效(1)

    用linux安装MySQL时产生问题破解

    JAVA中关于Map的九大问题

    windows 7旗舰版无法使用远程登录如何开启telnet服务

    Android View 事件分发机制详解

    MySQL用户变量的用法

      友情链接:
    • IT采购网
    • 科技号
    • 中国存储网
    • 存储网
    • 半导体联盟
    • 医疗软件网
    • 软件中国
    • ITbrand
    • 采购中国
    • CIO智库
    • 考研题库
    • 法务网
    • AI工具网
    • 电子芯片网
    • 安全库
    • 隐私保护
    • 版权申明
    • 联系我们
    IT技术网 版权所有 © 2020-2025,京ICP备14047533号-20,Power by OK设计网

    在上方输入关键词后,回车键 开始搜索。Esc键 取消该搜索窗口。