百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

React 项目实践——创建一个聊天机器人

zhezhongyun 2025-06-15 20:37 3 浏览

作者: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/

相关推荐

JavaScript中常用数据类型,你知道几个?

本文首发自「慕课网」,想了解更多IT干货内容,程序员圈内热闻,欢迎关注!作者|慕课网精英讲师Lison这篇文章我们了解一下JavaScript中现有的八个数据类型,当然这并不是JavaScr...

踩坑:前端的z-index 之bug一二(zh1es前端)

IE6下浮动元素bug给IE6下的一个div设置元素样式,无论z-index设置多高都不起作用。这种情况发生的条件有三个:1.父标签position属性为relative;2.问题标签无posi...

两栏布局、左边定宽200px、右边自适应如何实现?

一、两栏布局(左定宽,右自动)1.float+margin即固定宽度元素设置float属性为left,自适应元素设置margin属性,margin-left应>=定宽元素宽度。举例:HTM...

前端代码需要这样优化才是一个标准的网站

  网站由前端和后端组成,前端呈现给用户。本文将告诉您前端页面代码的优化,当然仍然是基于seo优化的。  就前端而言,如果做伪静态处理,基本上是普通的html代码,正常情况下,这些页面内容是通过页面模...

网页设计如何自学(初学网页设计)

1在Dreamweaver中搭建不同的页面,需要掌握HTML的语句了,通过调整各项数值就可以制作出排版漂亮的页面,跟着就可以学习一些可视化设计软件。下面介绍网页设计如何自学,希望可以帮助到各位。Dre...

1、数值类型(数值类型有)

1.1数据类型概览MySQL的数据类型可划分为三大类别:数值类型:旨在存储数字(涵盖整型、浮点型、DECIMAL等)。字符串类型:主要用于存储文本(诸如CHAR、VARCHAR之类)。日期/...

网页设计的布局属性(网页设计的布局属性是什么)

布局属性是网站设计中必不可少的一个重要的环节,主要用来设置网页的元素的布局,主要有以下属性。1、float:该属性设置元素的浮动方式,可以取none,left和right等3个值,分别表示不浮动,浮在...

Grid网格布局一种更灵活、更强大的二维布局模型!

当涉及到网页布局时,display:flex;和display:grid;是两个常用的CSS属性,它们都允许创建不同类型的布局,但有着不同的用法和适用场景。使用flex布局的痛点当我们使...

React 项目实践——创建一个聊天机器人

作者:FredrikStrandOseberg转发链接:https://www.freecodecamp.org/news/how-to-build-a-chatbot-with-react/前言...

有趣的 CSS 数学函数(css公式)

前言之前一直在玩three.js,接触了很多数学函数,用它们创造过很多特效。于是我思考:能否在CSS中也用上这些数学函数,但发现CSS目前还没有,据说以后的新规范会纳入,估计也要等很久。然...

web开发之-前端css(5)(css前端设计)

显示控制一个元素的显示方式,我们可以使用display:block;display:inline-block;display:none;其中布局相关的还有两个很重要的属性:display:flex;和...

2024最新升级–前端内功修炼 5大主流布局系统进阶(分享)

获课:keyouit.xyz/14642/1.前端布局的重要性及发展历程前端布局是网页设计和开发的核心技能之一,它决定了页面元素如何组织和呈现。从早期的静态布局到现代的响应式布局,前端布局技术经历了...

教你轻松制作自动换行的CSS布局,轻松应对不同设备!

在网页设计中,自动换行的CSS布局是非常常见的需求,特别是在响应式设计中。它可以让网页内容自动适应不同屏幕尺寸,保证用户在不同设备上都能够获得良好的浏览体验。本文将介绍几种制作自动换行的CSS布局的方...

晨光微语!一道 CSS 面试题,伴你静享知识治愈时光

当第一缕阳光温柔地爬上窗台,窗外的鸟鸣声清脆悦耳,空气中弥漫着清新的气息。在这宁静美好的清晨与上午时光,泡一杯热气腾腾的咖啡,找一个舒适的角落坐下。前端的小伙伴们,先把工作的疲惫和面试的焦虑放在一边,...

2023 年的响应式设计指南(什么是响应式设计优缺点)

大家好,我是Echa。如今,当大家考虑构建流畅的布局时,没有再写固定宽度和高度数值了。相反,小编今天构建的布局需要适用于几乎任何尺寸的设备。是不是不可思议,小编仍然看到网站遵循自适应设计模式,其中它有...