资讯专栏INFORMATION COLUMN

基于React与Redux的留言墙的实现

MockingBird / 251人阅读

摘要:设计本次留言墙分为两部分。活动展示部分为匿名留言墙形式后改为实名制,需要根据收到的留言墙进行向上平滑滚动,如果没有消息接收则停止在最后一条消息上。主要为处理数据的数据层。

背景

由于某事业群需要留言墙用于年会,同时需要调用大象公众号服务器接口,所以在今年年初开发了留言墙用于活动现场交流。

设计

本次留言墙分为两部分。一部分为活动展示部分,另一部分为后台审批部分。

活动展示部分为匿名留言墙形式(后改为实名制),需要根据收到的留言墙进行向上平滑滚动,如果没有消息接收则停止在最后一条消息上。

后台审批部分为管理人员对用户像某个特定公众号发送的特定格式消息进行审核,符合上墙要求的消息则通过审核,通过活动展示页面进行展示。

技术方案 React

该项目采用了React作为View层的渲染框架。关于React的简单介绍,大家可以戳阮一峰的博客React 入门实例教程, 需要系统学习的同学可以戳React的官方网站React英文版,React中文版。建议大家阅读React英文版网站,中文版网站相对于英文版网站来说缺少了一部分内容,例如React的children部分,可能是由于英文文档更新导致的翻译不太及时的原因。

Redux

Redux的学习可以通过Redux中文文档来进行。里面有很多的示例能够辅助进行学习。具体使用方法会通过后面的步骤进行介绍。

实现 React

在View层中,有两个组件。

Message

    
{this.props.flag === false ? : ""}
{this.props.uname} [{this.parseDate(this.props.sendTime)}] {this.props.text} {this.props.flag === true ? this.props.approve === 0 ? : "已通过" : ""}

MsgList

    
    {this.props.msgs.map((message, index) => {this.props.approveMsg(index, message.id)}}/> )}

其中Message为每条消息的组件,MsgList为整个消息列表的组件。

Redux Action

Action主要为处理数据的数据层。大部分的数据操作都放在Action中,通过dispatch(Action)的方法来通知readucer进行数据更新,从而通过react-redux来通知组件更新。
首先,会定义一些Action常量,用于操作命名。

export const WALL_REQUEST = "WALL_REQUEST";
export const WALL_REQUEST_SUCCESS = "WALL_REQUEST_SUCCESS";
export const WALL_REQUEST_FAIL = "WALL_REQUEST_FAIL";

export const CHECK_MSGS = "CHECK_MSGS";
export const CHECK_MSGS_SUCCESS = "CHECK_MSGS_SUCCESS";
export const CHECK_MSGS_FAIL = "CHECK_MSGS_FAIL";
export const MSG_REQUEST = "MSG_REQUEST";
export const MSG_REFUSE = "MSG_REFUSE";
export const MSG_PASS = "MSG_PASS";

同时,会定义一些函数,用于View层中与Action部分进行通信,从而触发某些操作。

export function fetchMsgs(number) {
    const path = "/nh/show/msg";
    return dispatch=> {
        dispatch(requestMsgs(number));
        return window.fetch(URL + path).then(response=>response.json()).then(json=>dispatch(receiveMsgs(json.data)));
    }
}

在reducer中使用了window变量中的fetch接口用于数据获取,有关于此接口的使用我后面会写另一篇文章来进行介绍,大家如果需要资料可以先戳此处,需要中文版的童鞋可以戳此处。

Reducer

在Reducer中,会对当前state中的所有数据进行处理,改变state中的全局数据从而驱动组件重新渲染。

function msgsReducer(state = [], action) {
    switch(action.type) {
        case WALL_REQUEST:
        {
            return state.slice(action.number)
        }
        case WALL_REQUEST_SUCCESS:
        {
            return [
                ...state, ...action.data
            ]
        }
        default:
        {
            return state;
        }
    }
}

在reducer中一般通过Action中声明的操作和action所带来的参数对state进行操作。每次都需要返回一个新的对象或者数组,而不能再原有数据上进行修改,从而避免数据更新后组件不更新的问题。

Server

server端返回的数据为一次性数据,即数据取过后就不会再返回,因此需要在前端Reducer里面对数据进行存储。由于数据为滚动显示,因此需要一个队列来进行控制。

难点 滚动问题 scrollTop+setInterval

最开始的滚动方案选择此方案。此方案在实现上也最为简单。但是,当消息数目到达1K量级时,能够明显的感觉到有卡顿的现象发生,滚动很不流畅,因此抛弃了此方法。

transform+setInterval

由于上一个方案中scrollTop在节点数过多的情况下会导致卡顿的问题,因此在滚动上采用了transform的方法,但是由于setInterval粒度不够小,因此对其进行了替换。

transform+window.requestAnimationFrame

window.requestAnimationFrame是浏览器接口,被调用的频率是每秒60次,但是一般会遵循W3C标准规定的频率。使用此接口可以保证调用频率,同时能够配合transform的变化数字来进行速度控制。因此采用了此方法。

节点删除功能

由于在留言墙的使用过程中,会有不断的新的节点产生并且滚动出视口,因此为了节省内存,需要将滚动出视口的节点删除,从而避免整个网页消耗的内存越来越大。
由于滚动方式确定为transform的滚动方式,因此选择了在请求调用返回数据后同时触发删除代码,对当前消息节点进行判断,对已经滚动到视口外的数据节点进行删除,并重置transform值,从而达到删除节点的目的。

componentWillReceiveProps(nextProps) {
    if(this.props.flag === false && this.props.msgs.length !== nextProps.msgs.length) {
        let number = Math.floor((-this.y) / this.msgHeight),
            element = document.getElementById("wall").getElementsByTagName("ul")[0];
        if(number > 0) {
            this.y = this.y + number * this.msgHeight;
            element.style.transform = "translateY(" + (this.y) + "px)";
            return;
        }
        window.cancelAnimationFrame(this.animationId);
        this.animationId = window.requestAnimationFrame(this.scroll.bind(this));
    }
}

通过组件接收新数据渲染时来重置transform值,完成节点删除工作。

不足

如果消息并发数量较多,会导致消息堆积在视口下方等待向上滚动,由此可能消耗大量的内存,后续仍然需要优化,避免所有接受到的未展示的数据都渲染出来堆积在下方。

总结

在刚开始设计时至少考虑到了滚动的情况,并没有考虑到消息越来越多导致页面占用内存越来越大的问题。当完成最初版本的消息滚动时,在自己测试的过程中因为消息数量过大导致卡顿,所以考虑到了滚动方面的优化与节点删除的问题。transform的效率优于scrollTop,而window.requestAnimationFrame的性能又优于setInterval,但是在开发时间上不是特别充足,因此选择了性能最好的技术方案,但是舍弃了一部分优化的方案。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/78908.html

相关文章

  • 基于 react, redux 最佳实践构建 2048

    摘要:最佳实践一个文件一个组件。,这是包含的是无副作用的纯函数式计算状态操作的函数。,的启动脚本,启动开发模式,项目打包,运行单元测试等等。每次代码推送到之前也会执行所有单元测试用例,全部通过才可以继续推送。,首次安装依赖包之后生成的文件。 前段时间 React license 的问题闹的沸沸扬扬,搞得 React 社区人心惶惶,好在最终 React 团队听取了社区意见把 license 换...

    Eidesen 评论0 收藏0
  • React.js 最佳实践(2016)_链接修正版

    摘要:译者按最近依旧如火如荼相信大家都跃跃欲试我们团队也开始在领域有所尝试年应该是逐渐走向成熟的一年让我们一起来看看国外的开发者们都总结了哪些最佳实践年在全世界都有很多关于新的更新和开发者大会的讨论关于去年的重要事件请参考那么年最有趣的问题来了我 译者按:最近React(web/native)依旧如火如荼,相信大家都跃跃欲试,我们团队也开始在React领域有所尝试. 2016年应该是Reac...

    syoya 评论0 收藏0
  • 前端最实用书签(持续更新)

    摘要:前言一直混迹社区突然发现自己收藏了不少好文但是管理起来有点混乱所以将前端主流技术做了一个书签整理不求最多最全但求最实用。 前言 一直混迹社区,突然发现自己收藏了不少好文但是管理起来有点混乱; 所以将前端主流技术做了一个书签整理,不求最多最全,但求最实用。 书签源码 书签导入浏览器效果截图showImg(https://segmentfault.com/img/bVbg41b?w=107...

    sshe 评论0 收藏0
  • 企业级React项目个人构建总结

    摘要:隔壁老王今日行程不同的内容使用这种方法就可以将我们得所有路由写在一起了,可能有人觉得每次都要写引入很麻烦,有没有其他更好用的办法,在讲的时候会说到这里就先跳过。 前言 距离上篇文章已经好长一段时间了,这两个星期公司派驻到京东方这边出差负责入驻项目团队的前端工作。这段时间从零搭建一下前端项目,这次给的时间比较充裕,思考的也比较多。以前也常有搭过前端项目,但是给的时间都比较紧,因此很多问题...

    simpleapples 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<