如何从socket.io高效实时渲染带有动态数据的React组件

How to render the React component with dynamic data realtime from socket.io high efficiency

我的前端页面是用React + Flux制作的,将script data发送到后端nodejs服务器

script data 是一个 Array,其中包含 linux shell 个参数(超过 100000 个)。当后台收到后,就会执行linuxshell命令。

举个例子:

cat ~/testfile1
cat ~/testfile2
          .
          .
          .
(100000 times ...etc)

当后端完成linux shell命令之一时,我可以将读取的内容保存到result data。因此,socket.io 将向前端发出 result data

我想从我的网页实时获取result data,所以我在下面的项目中做了一些东西。

我的React component代码:

import React from 'react';

import AppActions from '../../../actions/app-actions';
import SocketStore from '../../../stores/socket-store';
import ResultStore from '../../../stores/result-store';

function getSocket () {
    return SocketStore.getSocket();
}

function getResult () {
    return ResultStore.getResultItem();
}

class ListResultItem extends React.Component {
    constructor () {
        super();
    }
    render () {
        return <li>
                {this.props.result.get('name')} {this.props.result.get('txt')}
            </li>;
    }
}

class ShowResult extends React.Component {
    constructor () {
        super();
        this.state = {
            socket: getSocket(),
            result: getResult()
        };
    }
    componentWillMount () {
        ResultStore.addChangeListener(this._onChange.bind(this));
    }
    _onChange () {
        this.setState({
            result: getResult()
        });
    }
    render () {
        return <div>
                <ol>
                    {this.state.result.map(function(item, index) {
                        return <ListResultItem key={index} result={item} />;
                    })}
                </ol>
            </div>;
    }
    componentDidMount () {
        this.state.socket.on('result', function (data) {
            AppActions.addResult(data);
        });
    }
}

我的Flux store (ResultStore)代码:

import AppConstants from '../constants/app-constants.js';
import { dispatch, register } from '../dispatchers/app-dispatcher.js';
import { EventEmitter } from 'events';
import Immutable from 'immutable';

const CHANGE_EVENT = 'changeResult';

let _resultItem = Immutable.List();

const _addResult = (result) => {
    let immObj = Immutable.fromJS(result);

    _resultItem = _resultItem.push(immObj);
}

const _clearResult = () => {
    _resultItem = _resultItem.clear();
}

const ResultStore = Object.assign(EventEmitter.prototype, {
    emitChange (){
        this.emit( CHANGE_EVENT );
    },
    addChangeListener (callback) {
        this.on(CHANGE_EVENT, callback);
    },
    removeChangeListener (callback) {
        this.removeListener(CHANGE_EVENT, callback);
    },
    getResultItem () {
        return _resultItem;
    },
    dispatcherIndex: register(function (action) {
        switch (action.actionType) {
            case AppConstants.ADD_RESULT:
                _addResult(action.result);
            break;
            case AppConstants.CLEAR_RESULT:
                _clearResult();
            break;
        }

        ResultStore.emitChange();
    })
});

但是渲染超过1000条数据后页面会变得很慢。如何提高渲染性能?我需要持续执行 linux 脚本 3 天以上。任何解决方案?谢谢~

是否需要在屏幕上渲染所有数据?如果没有,那么有几种方法可以减少可见数据量。

过滤/搜索

您可以提供一个 search/filter 组件来补充列表并创建一个谓词函数,该函数可用于确定是否应该呈现每个项目。

<PredicateList>
  <Search />
  <Filter />
  {this.state.result
     .filter(predicate)
     .map(function(item, index) {
       return <ListResultItem key={index} result={item} />;
     })
  }
</PredicateList>

延迟加载

仅在需要时加载项目。您可以通过跟踪项目是否在屏幕上或鼠标是否在其上来确定是否需要该项目。

var Lazy = React.createClass({
  getInitialState: function() {
    return { loaded: false };
  },
  load: function() {
    this.setState({ loaded: true });
  },
  render: function() {
    var loaded = this.state.loaded,
        component = this.props.children,
        lazyContainer = <div onMouseEnter={this.load} />;

    return loaded ?
      component
      lazyContainer;
  }
});

然后只需将您的数据项包装在这些 Lazy 包装器中,以便在请求时呈现它们。

<Lazy>
  <ListResultItem key={index} result={item} />
</Lazy>

这样可以确保只显示用户需要的数据。您还可以改进加载触发器以适用于更复杂的场景,例如当组件在屏幕上显示超过 2 秒时。

分页

最后,最后也是最受考验的方法是分页。为可以一次显示的数据项数量选择限制,然后允许用户分块浏览数据集。

var Paginate = React.createClass({
  getDefaultProps: function() {
    return { items: [], perPage: 100 }; 
  },
  getInitialState: function() {
    return { page: 0 };
  },
  next: function() {
    this.setState({ page: this.state.page + 1});
  },
  prev: function() {
    this.setState({ page: this.state.page - 1});
  },
  render: function() {
    var perPage = this.props.perPage,
        currentPage = this.state.page,
        itemCount = this.props.items.length;

    var start = currentPage * perPage,
        end = Math.min(itemCount, start + perPage);

    var selectedItems = this.props.items.slice(start, end);

    return (
      <div className='pagination'>
        {selectedItems.map(function(item, index) {
          <ListResultItem key={index} result={item} />
        })}
        <a onClick={this.prev}>Previous {{this.state.perPage}} items</a>
        <a onClick={this.next}>Next {{this.state.perPage}} items</a>
      </div>
    );
  }
});

这些只是以高效方式管理大量数据呈现的非常粗略的实施示例,但希望它们对您实施自己的解决方案有足够的意义。