如何从socket.io高效地实时呈现具有动态数据的React组件



我的前端页面是由React+Flux创建的,它将script data发送到后端nodejs服务器。

script data是一个包含linux shell参数(超过100000)的Array。当后端收到时,它将执行linux shell命令。

举个例子:

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天。有什么解决方案吗?感谢~

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

筛选/搜索

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

<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>
    );
  }
});

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

最新更新