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

背景

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

设计

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

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

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

技术方案

React

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

Redux

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

实现

React

在View层中,有两个组件。

  • Message

    <div className="message">
                {this.props.flag === false ? <img src={this.props.photo} alt="头像"/> : ''}
                    <div className="message-content">
                        {this.props.uname} [{this.parseDate(this.props.sendTime)}] {this.props.text}
                        {this.props.flag === true ? this.props.approve === 0 ?
                            <button onClick={this.props.approveMsg}>通过</button> : '已通过' : ''}
                    </div>
                </div>
  • MsgList

    <div>
            <button onClick={(e)=>{this.handleClick(e)}}>获取消息</button>
                <div id="wall">
                    <ul>
                        {this.props.msgs.map((message,index) =>
                            <Message {...message} key={index} flag={this.props.flag}
                                                  approveMsg={()=>{this.props.approveMsg(index,message.id)}}/>
                        )}
                    </ul>
                </div>
            </div>

其中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,但是在开发时间上不是特别充足,因此选择了性能最好的技术方案,但是舍弃了一部分优化的方案。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


react 中的高阶组件主要是对于 hooks 之前的类组件来说的,如果组件之中有复用的代码,需要重新创建一个父类,父类中存储公共代码,返回子类,同时把公用属性...
我们上一节了解了组件的更新机制,但是只是停留在表层上,例如我们的 setState 函数式同步执行的,我们的事件处理直接绑定在了 dom 元素上,这些都跟 re...
我们上一节了解了 react 的虚拟 dom 的格式,如何把虚拟 dom 转为真实 dom 进行挂载。其实函数是组件和类组件也是在这个基础上包裹了一层,一个是调...
react 本身提供了克隆组件的方法,但是平时开发中可能很少使用,可能是不了解。我公司的项目就没有使用,但是在很多三方库中都有使用。本小节我们来学习下如果使用该...
mobx 是一个简单可扩展的状态管理库,中文官网链接。小编在接触 react 就一直使用 mobx 库,上手简单不复杂。
我们在平常的开发中不可避免的会有很多列表渲染逻辑,在 pc 端可以使用分页进行渲染数限制,在移动端可以使用下拉加载更多。但是对于大量的列表渲染,特别像有实时数据...
本小节开始前,我们先答复下一个同学的问题。上一小节发布后,有小伙伴后台来信问到:‘小编你只讲了类组件中怎么使用 ref,那在函数式组件中怎么使用呢?’。确实我们...
上一小节我们了解了固定高度的滚动列表实现,因为是固定高度所以容器总高度和每个元素的 size、offset 很容易得到,这种场景也适合我们常见的大部分场景,例如...
上一小节我们处理了 setState 的批量更新机制,但是我们有两个遗漏点,一个是源码中的 setState 可以传入函数,同时 setState 可以传入第二...
我们知道 react 进行页面渲染或者刷新的时候,会从根节点到子节点全部执行一遍,即使子组件中没有状态的改变,也会执行。这就造成了性能不必要的浪费。之前我们了解...
在平时工作中的某些场景下,你可能想在整个组件树中传递数据,但却不想手动地通过 props 属性在每一层传递属性,contextAPI 应用而生。
楼主最近入职新单位了,恰好新单位使用的技术栈是 react,因为之前一直进行的是 vue2/vue3 和小程序开发,对于这些技术栈实现机制也有一些了解,最少面试...
我们上一节了了解了函数式组件和类组件的处理方式,本质就是处理基于 babel 处理后的 type 类型,最后还是要处理虚拟 dom。本小节我们学习下组件的更新机...
前面几节我们学习了解了 react 的渲染机制和生命周期,本节我们正式进入基本面试必考的核心地带 -- diff 算法,了解如何优化和复用 dom 操作的,还有...
我们在之前已经学习过 react 生命周期,但是在 16 版本中 will 类的生命周期进行了废除,虽然依然可以用,但是需要加上 UNSAFE 开头,表示是不安...
上一小节我们学习了 react 中类组件的优化方式,对于 hooks 为主流的函数式编程,react 也提供了优化方式 memo 方法,本小节我们来了解下它的用...
开源不易,感谢你的支持,❤ star me if you like concent ^_^
hel-micro,模块联邦sdk化,免构建、热更新、工具链无关的微模块方案 ,欢迎关注与了解
本文主题围绕concent的setup和react的五把钩子来展开,既然提到了setup就离不开composition api这个关键词,准确的说setup是由...
ReactsetState的执行是异步还是同步官方文档是这么说的setState()doesnotalwaysimmediatelyupdatethecomponent.Itmaybatchordefertheupdateuntillater.Thismakesreadingthis.staterightaftercallingsetState()apotentialpitfall.Instead,usecom