React 项目实践——创建一个聊天机器人
zhezhongyun 2025-06-15 20:37 56 浏览
作者:Fredrik Strand Oseberg
转发链接:
https://www.freecodecamp.org/news/how-to-build-a-chatbot-with-react/
前言
我的理念很简单:如果你想要在某个方面精通,那么你需要持续实践,实践一次不会有多少效果,你必须反复实践。
我在编程这件事上就是这么做的。
在这个过程中,我特别感受到:创建一些有意思的好东西是非常有趣的。你可以向朋友展示自己引以为傲的作品,坐下来敲代码实现它的过程会让你感觉欢喜。
比如说我创建了一个聊天机器人(代码)。
我们一起来创建吧!如果你想自己独立完成这个挑战,可以直接参考这份文档(其实是一个聊天机器人成品)。
好啦,我们开始吧。我就假设你已经安装了 Node,可以运行 npx 命令。如果没有的话,访问这里。
初始设置
// 运行以下代码
npx create-react-app chatbot
cd chatbot
yarn add react-chatbot-kit
yarn start
安装 npm 包,访问 localhost:3000。
然后打开 App.js,修改如下:
import Chatbot from 'react-chatbot-kit'
function App() {
return (
<div className="App">
<header className="App-header">
<Chatbot />
</header>
</div>
);
}
现在你的界面是这样的:
聊天机器人要正常工作,需要接收三个 props。首先,需要具有 initialMessages 属性,包含聊天信息对象。然后,需要有 MessageParser 用于解析,还有 ActionProvider 基于解析结果执行我们需要它执行的动作。
稍后我们进一步讲解这个。现在,在这里获取代码。
- 把 MessageParser 代码放到 MessageParser.js 文件
- 把 ActionProvider 代码放到 ActionProvider.js 文件
- 把 config 代码放到 config.js 文件
现在返回到 App.js 文件,添加以下代码:
import React from 'react';
import Chatbot from 'react-chatbot-kit'
import './App.css';
import ActionProvider from './ActionProvider';
import MessageParser from './MessageParser';
import config from './config';
function App() {
return (
<div className="App">
<header className="App-header">
<Chatbot config={config} actionProvider={ActionProvider} messageParser={MessageParser} />
</header>
</div>
);
}
localhost:3000 现在应该是这样显示:
很棒!我们已经初始化了聊天机器人,可以输入和提交一些信息了。试试看!
理解聊天机器人是怎么工作的
我们暂停一下,看看 MessageParser 和 ActionProvider 是怎么配合让聊天机器人执行动作的。
机器人初始化的时候,内部 state 的 messages 属性获取 initialMessages 属性值,将信息渲染到屏幕。
接着,当我们在聊天框输入信息,点击 submit 提交时,MessageParser(作为 props 传递给机器人调用 parse 方法。
我们进一步看看 MessageParser 的代码:
class MessageParser {
constructor(actionProvider) {
this.actionProvider = actionProvider;
}
parse(message) {
... parse logic
}
}
代码中包含 actionProvider,这跟我们传递给聊天机器人的 props ActionProvider 是一样的。我们通过这个代码解析信息,并告诉机器人执行什么动作。
比如,我们创建一个简单的响应。首先,将 MessageParser 改为:
class MessageParser {
constructor(actionProvider) {
this.actionProvider = actionProvider;
}
parse(message) {
const lowerCaseMessage = message.toLowerCase()
if (lowerCaseMessage.includes("hello")) {
this.actionProvider.greet()
}
}
}
export default MessageParser
MessageParser 接收到用户的信息,检查是否包含 “hello”。如果包含,则调用 actionProvider 的 greet 方法。
不过现在还行不通,因为我们还没有执行 greet 方法。稍后再处理这个。先处理 ActionProvider.js 文件如下:
class ActionProvider {
constructor(createChatBotMessage, setStateFunc) {
this.createChatBotMessage = createChatBotMessage;
this.setState = setStateFunc;
}
greet() {
const greetingMessage = this.createChatBotMessage("Hi, friend.")
this.updateChatbotState(greetingMessage)
}
updateChatbotState(message) {
// NOTE: This function is set in the constructor, and is passed in // from the top level Chatbot component. The setState function here // actually manipulates the top level state of the Chatbot, so it's // important that we make sure that we preserve the previous state.
this.setState(prevState => ({
...prevState, messages: [...prevState.messages, message]
}))
}
}
export default ActionProvider
现在我们在聊天框输入 “hello”,可以看到:
很好!解析信息和响应都没有问题了。我们再做一些更复杂的东西,让机器人提供我们想要的编程语言学习资料。
创建一个学习机器人
首先,回到 config.js 文件,稍作修改:
import { createChatBotMessage } from 'react-chatbot-kit';
const config = {
botName: "LearningBot",
initialMessages: [createChatBotMessage("Hi, I'm here to help. What do you want to learn?")],
customStyles: {
botMessageBox: {
backgroundColor: "#376B7E",
},
chatButton: {
backgroundColor: "#376B7E",
},
},
}
export default config
我们增加了一些属性,修改了初始信息,特别是给机器人取了个名字,更改了 messagebox 和 chatbutton 组件的颜色。
好玩的部分来了。
我们不仅可以渲染信息和回复给用户,还可以根据想要的信息来自定义 React 组件。比如,我们创建一个选择组件,引导用户做不同选择。
首先,定义学习选项组件:
// in src/components/LearningOptions/LearningOptions.jsx
import React from "react";
import "./LearningOptions.css";
const LearningOptions = (props) => {
const options = [
{ text: "Javascript", handler: () => {}, id: 1 },
{ text: "Data visualization", handler: () => {}, id: 2 },
{ text: "APIs", handler: () => {}, id: 3 },
{ text: "Security", handler: () => {}, id: 4 },
{ text: "Interview prep", handler: () => {}, id: 5 },
];
const optionsMarkup = options.map((option) => (
<button
className="learning-option-button"
key={option.id}
onClick={option.handler}
>
{option.text}
</button>
));
return <div className="learning-options-container">{optionsMarkup}</div>;
};
export default LearningOptions;
// in src/components/LearningOptions/LearningOptions.css
.learning-options-container {
display: flex;
align-items: flex-start;
flex-wrap: wrap;
}
.learning-option-button {
padding: 0.5rem;
border-radius: 25px;
background: transparent;
border: 1px solid green;
margin: 3px;
}
然后在机器人代码中使用组件。对 config.js 文件作如下操作:
import React from "react";
import { createChatBotMessage } from "react-chatbot-kit";
import LearningOptions from "./components/LearningOptions/LearningOptions";
const config = {
initialMessages: [
createChatBotMessage("Hi, I'm here to help. What do you want to learn?", {
widget: "learningOptions",
}),
],
...,
widgets: [
{
widgetName: "learningOptions",
widgetFunc: (props) => <LearningOptions {...props} />,
},
],
}
理解 Widgets
小结一下:
- 我们创建了 LearningOptions 组件
- 在 config 的 widgets 下使用组件
- 给 createChatbotMessage 函数一个选项对象,说明需要渲染哪个 widget 和信息
结果:
很棒!但是,为什么要在 config 中以 widget 的形式引入组件呢?
通过将其设置为函数,我们可以在调用时以聊天机器人的重要属性来装饰 widget。
我们定义的 widget 会接收到机器人的各种属性:
- actionProvider - 将 actionProvider 添加到 widget,以执行动作
- setState - 将 setState 添加到 widget,以操作 state
- scrollIntoView - 滑动到聊天框底部,在需要调整视图时使用这个函数
- props - 给 widget 定义的 props 将通过 configProps 传递给 widget
- state - 通过 mapStateToProps 属性将自定义 state 传递给 widget
回头想想,我们给 LearningOptions 组件设置了一些选项:
const options = [
{ text: "Javascript", handler: () => {}, id: 1 },
{ text: "Data visualization", handler: () => {}, id: 2 },
{ text: "APIs", handler: () => {}, id: 3 },
{ text: "Security", handler: () => {}, id: 4 },
{ text: "Interview prep", handler: () => {}, id: 5 },
];
暂时这些选项有一个空的 handler,我们想调用 actionProvider 替换 handler。
那么,我们想在执行这些函数的时候发生什么呢?理想状况下,机器人已经具有一些回复信息以及一个 widget 显示每个主题对应的资源列表链接。我们看看怎么实现。
首先,创建一个链接列表组件:
// in src/components/LinkList/LinkList.jsx
import React from "react";
import "./LinkList.css";
const LinkList = (props) => {
const linkMarkup = props.options.map((link) => (
<li key={link.id} className="link-list-item">
<a
href={link.url}
target="_blank"
rel="noopener noreferrer"
className="link-list-item-url"
>
{link.text}
</a>
</li>
));
return <ul className="link-list">{linkMarkup}</ul>;
};
export default LinkList;
// in src/components/LinkList/LinkList.css
.link-list {
padding: 0;
}
.link-list-item {
text-align: left;
font-size: 0.9rem;
}
.link-list-item-url {
text-decoration: none;
margin: 6px;
display: block;
color: #1d1d1d;
background-color: #f1f1f1;
padding: 8px;
border-radius: 3px;
box-shadow: 2px 2px 4px rgba(150, 149, 149, 0.4);
}
将这个组件添加到 widget 中:
import React from "react";
import { createChatBotMessage } from "react-chatbot-kit";
import LearningOptions from "./components/LearningOptions/LearningOptions";
import LinkList from "./components/LinkList/LinkList";
const config = {
...
widgets: [
{
widgetName: "learningOptions",
widgetFunc: (props) => <LearningOptions {...props} />,
},
{
widgetName: "javascriptLinks",
widgetFunc: (props) => <LinkList {...props} />,
},
],
};
export default config;
如果我们想动态给这个组件传递参数,以便对其他选项复用,那就需要给 widget 添加另一个属性:
import React from "react";
import { createChatBotMessage } from "react-chatbot-kit";
import LearningOptions from "./components/LearningOptions/LearningOptions";
import LinkList from "./components/LinkList/LinkList";
const config = {
...,
widgets: [
...,
{
widgetName: "javascriptLinks",
widgetFunc: (props) => <LinkList {...props} />,
props: {
options: [
{
text: "Introduction to JS",
url:
"https://www.freecodecamp.org/learn/javascript-algorithms-and-data-structures/basic-javascript/",
id: 1,
},
{
text: "Mozilla JS Guide",
url:
"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide",
id: 2,
},
{
text: "Frontend Masters",
url: "https://frontendmasters.com",
id: 3,
},
],
},
},
],
};
export default config;
现在,这些 props 会作为参数传递给 LinkList 组件。
我们再做两件事情。
- 给 actionProvider 添加一个方法
class ActionProvider {
constructor(createChatBotMessage, setStateFunc) {
this.createChatBotMessage = createChatBotMessage;
this.setState = setStateFunc;
}
handleJavascriptList = () => {
const message = this.createChatBotMessage(
"Fantastic, I've got the following resources for you on Javascript:",
{
widget: "javascriptLinks",
}
);
this.updateChatbotState(message);
};
updateChatbotState(message) {
// NOTICE: This function is set in the constructor, and is passed in from the top level Chatbot component. The setState function here actually manipulates the top level state of the Chatbot, so it's important that we make sure that we preserve the previous state.
this.setState((prevState) => ({
...prevState,
messages: [...prevState.messages, message],
}));
}
}
export default ActionProvider;
- 把这个方法作为 LearningOptions 组件 handler
import React from "react";
import "./LearningOptions.css";
const LearningOptions = (props) => {
const options = [
{
text: "Javascript",
handler: props.actionProvider.handleJavascriptList,
id: 1,
},
{ text: "Data visualization", handler: () => {}, id: 2 },
{ text: "APIs", handler: () => {}, id: 3 },
{ text: "Security", handler: () => {}, id: 4 },
{ text: "Interview prep", handler: () => {}, id: 5 },
];
const optionsMarkup = options.map((option) => (
<button
className="learning-option-button"
key={option.id}
onClick={option.handler}
>
{option.text}
</button>
));
return <div className="learning-options-container">{optionsMarkup}</div>;
};
export default LearningOptions;
好啦,信息量比较大。现在如果我们点击聊天机器人的 JavaScript 选项,会出现:
完美!再进一步,如果用户输入信息,机器人也应该响应。所以我们需要给 MessageParser 创建新规则。
更新 MessageParser.js 文件:
class MessageParser {
constructor(actionProvider) {
this.actionProvider = actionProvider;
}
parse(message) {
const lowerCaseMessage = message.toLowerCase();
if (lowerCaseMessage.includes("hello")) {
this.actionProvider.greet();
}
if (lowerCaseMessage.includes("javascript")) {
this.actionProvider.handleJavascriptList();
}
}
}
export default MessageParser;
在输入框键入 “javaScript”,机器人会回复同样的清单。完成啦!
欢迎在 GitHub 访问代码和文档。
结语
创建项目很有趣,也是一个帮助你拓展技能的很棒的方式。你完全可以动动脑筋,在这个项目基础上再开发别的,比如一个机器人通过一些简单的问题找到网店里最适合的产品,或者是一个帮公司回复顾客常见问题的机器人。
你可以尽量实践你的新想法。也欢迎你 pull request,帮我完善这个项目。
我觉得持续创建项目真的是开发者提升自己的唯一方式,建议你现在就动起来!
作者:Fredrik Strand Oseberg
转发链接:
https://www.freecodecamp.org/news/how-to-build-a-chatbot-with-react/
相关推荐
- 3 分钟!AI 从零开发五子棋全过程曝光,网友:这效率我服了
-
<!DOCTYPEhtml><htmllang="zh-CN"><head><metacharset="UTF-8...
- 一行代码实现display"过渡动画"原理
-
作者:Peter谭老师转发链接:https://mp.weixin.qq.com/s/XhwPOv62gypzq5MhhP-5vg写本文的起因上篇文章,提到如何让display出现过渡动画,却没有仔...
- 脑洞:琼恩·雪诺、蝙蝠侠和魔形女的灵魂宠物了解一下
-
AlekseiVinogradovisaRussianfreelancedigitalartistwhoshareshisskillsandtalentwith120k...
- 浏览器的渲染机制、重绘、重排
-
1、什么是重排和重绘网页生成过程:HTML被HTML解析器解析成DOM树css则被css解析器解析成CSSOM树结合DOM树和CSSOM树,生成一棵渲染树(RenderTree)生成布局(flo...
- 托福写作高频考题写作思路&词汇丨考虫独家
-
科技话题与媒体话题是托福写作的常考话题很多考生对这两类话题里的专有词汇表达也许很不了解所以今天就跟随考虫托福写作老师刘云龙老师一起来学习在这些话题的写作里你可以使用哪些有用的表达。希望大家有收获!记得...
- 在优麒麟上使用 Electron 开发桌面应用
-
使用Web标准来创建桌面GUI,上手快、成本低、跨平台、自适应分辨率,这些都是Electron的优势。作者/来源:优麒麟Electron是由Github开发,用HTML、CSS和...
- php手把手教你做网站(三十八)jquery 转轮盘抽奖,开盲盒
-
抽奖和开盲盒性质一样的都是通过ajax读取后台的随机数据。1、转轮盘本来是想直接绘图实现轮盘,但是没有找到怎么填充文字,只好把轮盘弄成了背景图,通常用于游戏抽道具,商城积分抽奖,公司年末员工抽奖点击抽...
- 用 CSS 整活!3D 轮播图手把手教学,快乐代码敲出来
-
兄弟们,今天咱来搞点好玩的——用CSS整一个3D轮播图!咱野生程序员就是要在代码里找乐子,技术和快乐咱都得要!代码是写不完的,但咱能自己敲出快乐来,走起!一、先整个容器,搭个舞台咋先写一个...
- 实现一个超酷的 3D 立体卡片效 #前端开发
-
今天我们来实现一个超酷的3D立体卡片效果。正常情况下就是一个普通的图片展示卡片,鼠标悬停的时候图片会跳出卡片,并将影子投射到背景卡片上,在视觉上有一个3D立体感。html主要分成3个部分:容器→背景层...
- Vue 3 Teleport与Suspense:解决UI难题的两个"隐藏大招"
-
模态框的"层级噩梦"与Teleport的救赎"这个模态框怎么又被父容器截断了?"团队协作开发后台系统时,小张第N次遇到这个问题。多层嵌套的组件结构里,弹窗被overfl...
- 让交互更加生动!有意思的鼠标跟随 3D 旋转动效
-
今天,群友问了这样一个问题,如下所示的鼠标跟随交互效果,如何实现:简单分析一下,这个交互效果主要有两个核心:借助了CSS3D的能力元素的旋转需要和鼠标的移动相结合本文,就将讲述如何使用纯CSS...
- 填坑:transform元素导致zindex失效终极方法
-
今天遇到了使用css3动画的元素层级被放大置顶的问题,ios浏览器上没问题,安卓原生浏览器和安卓微信上有问题。使用了css3动画的元素z-index失效,兄弟元素设置多高的z-index都盖不住解决办...
- 诡异的层级错乱:一个被transform隐藏的CSS陷阱
-
周五下午三点十七分,设计部突然发来紧急截图——原本应该悬浮在顶部的导航菜单,此刻正诡异地被下方的轮播图遮挡。我盯着屏幕上错乱的层级关系,手指下意识地敲下z-index:9999,心里清楚这不过是程序...
- 动画篇--碎片动画
-
本文授权转载,作者:Sindri的小巢(简书)前言从最开始动笔动画篇的博客,至今已经过去了四个多月。这段时间回头看了看自己之前的动画文章,发现用来讲解动画的例子确实不那么的赏心悦目。于是这段时间总是想...
- Nature:大洋转换断层处的拉张构造与两阶段地壳增生
-
Nature:大洋转换断层处的拉张构造与两阶段地壳增生转换断层是三种基本的板块边界之一,全球总长度超过48000km(Bird,2003),它们的发现为板块构造理论的建立奠定了重要的基础(Wil...
- 一周热门
- 最近发表
- 标签列表
-
- HTML 教程 (33)
- HTML 简介 (35)
- HTML 实例/测验 (32)
- HTML 测验 (32)
- JavaScript 和 HTML DOM 参考手册 (32)
- HTML 拓展阅读 (30)
- HTML文本框样式 (31)
- HTML滚动条样式 (34)
- HTML5 浏览器支持 (33)
- HTML5 新元素 (33)
- HTML5 WebSocket (30)
- HTML5 代码规范 (32)
- HTML5 标签 (717)
- HTML5 标签 (已废弃) (75)
- HTML5电子书 (32)
- HTML5开发工具 (34)
- HTML5小游戏源码 (34)
- HTML5模板下载 (30)
- HTTP 状态消息 (33)
- HTTP 方法:GET 对比 POST (33)
- 键盘快捷键 (35)
- 标签 (226)
- HTML button formtarget 属性 (30)
- CSS 水平对齐 (Horizontal Align) (30)
- opacity 属性 (32)