用React实现小Q聊天机器人

1,156 阅读4分钟

前几篇讲过用Vue实现小Q聊天机器人,既然用了Vue,那React自然也是少不了的,本项目实现效果与vue实现的效果完全一样:

GitHub源码:github.com/baiyuliang/…

在这里插入图片描述

进入正题之前,我们来聊聊前端三剑客 angular,react,vue!

这三种框架的面世时间:angular>react>vue,angular出世这么早,但是为什么我们在国内常见的前端开发基本都是用的react或者vue呢?主要是angular的版本升级出现了断层,也就是新版本不兼容老版本,导致流失大量用户,另外angular框架量级比较重,直白点就是难用!就国内的互联网环境来看,这注定是流行不起来的!所以react成为了最流行的前端开发框架,而新出世的vue凭借着它的易用性也迅速成为了最火的前端开发框架!

据本人了解,就国内目前最火的几种前端或者跨端开发平台,基本都是基于react和vue这两种框架,例如: 1.weex基于vue(现在通过rax也间接支持react) 2.rn不必说了,基于react 3.taro 支持react和vue 4.uni-app 基于vue

可以说学习前端,除了最基础的js+html+css外,react和vue是必学框架,没商量!

回到本项目,接下来,我会基于本项目粗略的对react和vue的实现过程进行一个对比,但不会深入!

1.创建项目 本人较懒,不喜欢用命令行去创建项目再外加一堆配置,而是直接用WebStrom创建,跟前几篇vue创建项目一样: 在这里插入图片描述 创建完成即可运行!

2.项目结构

我这里进行一个完整项目对比:左vue,右react

在这里插入图片描述 在这里插入图片描述 3.页面结构、组件

vue的页面都是以.vue结尾的文件格式,其中包含了 templete,script,style,模板(html布局),js操作,css样式全在一个文件中实现,并且style提供scope属性,来确保css样式不会冲突,而vue创建组件同样也是创建.vue后缀文件,并声明组件名称name:XXX;

react的页面,没有特殊格式,全部为js,在这里大家有必要先了解一下JSX react组件形式有两种:class组件和函数(function)组件, class组件功能大而全,类似于Java的类,可以继承,有构造方法,静态方法,普通方法等,通过render()函数渲染DOM树,通过export default导出组件:

import React from 'react'

class Page2 extends React.Component {
    constructor(props) {
        super(props);
    }

    render() {
        return (
            <div>
                page2
            </div>
        )
    }
}
export default Page2

函数组件功能比较单一,return jsx格式DOM树即可!

function Item() {
    return <div>Item</div>
}

react相当于将js,html糅合到了一块,而css则单独分离了出去,所以使用样式时必须创建对应的css文件,并通过import导入!但是需要注意的是,react并没有像vue一样提供scope,所以直接通过

import ‘xxx.css

使用会导致样式冲突,解决办法有两个:一个是给class名加上前缀来保证类名唯一性,另一个是css模块化:

import Style from ‘xxx.css’

<div className={Style.xxx}></div>

4.数据绑定 vue:通过v-model进行双向数据绑定; react:单项数据绑定,只能通过state进行数据同步! 关于双向数据绑定好,还是单项数据绑定好,vue和react都有自己的解释,开发者也都有自己的理解,不用纠结于此!

5.网络请求 网络请求部分,两者倒没有什么不同,vue版小Q使用axios进行网络请求封装,而react可以直接拿来使用就可以了,没有任何区别!

6.跨域问题 vue在使用代理解决跨域时的方法,配置vue.config.js:

module.exports = {
    devServer: {
        proxy: {
            "/api": {
                target: 'https://api.ai.qq.com',
                changeOrigin: true,
                pathRewrite: {
                    '^/api': '/'
                }
            }
        }
    }
};

react则配置setupProxy.js(注意文件放入位置):

const { createProxyMiddleware } = require('http-proxy-middleware');

module.exports = function(app) {
    app.use(
        createProxyMiddleware('/api', {  //`api`是需要转发的请求
            target: 'https://api.ai.qq.com',  // 这里是接口服务器地址
            changeOrigin: true,
        })
    )
}

当然,两者还有其它很多不同的地方,就不再一一列举了,这里贴出聊天主界面代码,完整代码可以去GitHub(Qrobot_React)下载:

import React from "react";
import {getChatResponse} from "../api/ApiChat";
import './Chat.scss'
import RightItem from "../components/RightItem";
import LeftItem from "../components/LeftItem";

class Chat extends React.Component {

    constructor(props) {
        super(props);
        this.state = {
            text: '',
            msglist: [{
                id: 1,
                type: 1,
                content: '欢迎你!',
                me: false
            }]
        }
    }

    //调用接口
    getResponse = (text) => {
        getChatResponse(text).then(res => {
            this.state.msglist.push({
                id: this.state.msglist[this.state.msglist.length - 1].id + 1,
                type: 1,
                content: res.data.answer,
                me: false
            })
            this.setState({
                msglist: this.state.msglist,
            })
        })
    }

    //输入框数据改变时同步state
    changeText = (e) => {
        this.setState({
            text: e.target.value
        })
    }

    //监听enter键
    onKeyup = (e) => {
        if (e.keyCode === 13) {
            this.send()
        }
    }

    //发送文本
    send = () => {
        if (this.state.text) {
            this.state.msglist.push({
                id: this.state.msglist[this.state.msglist.length - 1].id + 1,
                type: 1,
                content: this.state.text,
                me: true
            })
            this.setState({
                msglist: this.state.msglist,
                text: ''
            })
            this.getResponse(this.state.text)
        }
    }

    //自动滚动到底部
    scrollToBottom = () => {
        this.messagesEnd.scrollIntoView({behavior: "smooth"});
    }

    //数据更新时执行scrollToBottom
    componentDidUpdate() {
        this.scrollToBottom();
    }

    render() {
        return (
            <div className="chat-container">
                <div className="chat-list">
                    <ul>
                        <ListItem list={this.state.msglist}/>
                    </ul>
                    <div style={{float: "left", clear: "both"}}
                         ref={(el) => {
                             this.messagesEnd = el;
                         }}>
                    </div>
                </div>
                <div className="chat-bottom">
                    <div className="chat-line"/>
                    <div className="chat-input-send">
                        <input placeholder="请输入内容..." value={this.state.text} onChange={this.changeText}
                               className="chat-input" onKeyUp={this.onKeyup}/>
                        <button className="chat-send" onClick={this.send}>发送</button>
                    </div>
                </div>

            </div>
        )
    }

}

function ListItem(props) {
    return props.list.map(item => {
        if (item.me) {
            return <li key={item.id}><RightItem msg={item}/></li>
        } else {
            return <li key={item.id}><LeftItem msg={item}/></li>
        }
    })
}


export default Chat

如果看过本人vue实现小Q文章的同学,会知道如何在vue中解决聊天列表自动滚动到底部的问题,而react依然面临着这个问题,解决办法其实大同小异,都是在列表下方放置一个空的div,并在每次聊天结束后,定位到这个div的位置:

第一步:

   <div className="chat-list">
         <ul>
             <ListItem list={this.state.msglist}/>
         </ul>
         <div style={{float: "left", clear: "both"}}
              ref={(el) => {
                  this.messagesEnd = el;
              }}>
         </div>
     </div>

第二步:

    //自动滚动到底部
    scrollToBottom = () => {
        this.messagesEnd.scrollIntoView({behavior: "smooth"});
    }

    //数据更新时执行scrollToBottom
    componentDidUpdate() {
        this.scrollToBottom();
    }

GitHub源码:github.com/baiyuliang/…