轉發連結:https://www.freecodecamp.org/news/how-to-build-a-chatbot-with-react/
前言我的理念很簡單:如果你想要在某個方面精通,那麼你需要持續實踐,實踐一次不會有多少效果,你必須反覆實踐。
我在程式設計這件事上就是這麼做的。
在這個過程中,我特別感受到:建立一些有意思的好東西是非常有趣的。你可以向朋友展示自己引以為傲的作品,坐下來敲程式碼實現它的過程會讓你感覺歡喜。
比如說我建立了一個聊天機器人(程式碼)。
我們一起來建立吧!如果你想自己獨立完成這個挑戰,可以直接參考這份文件(其實是一個聊天機器人成品)。
好啦,我們開始吧。我就假設你已經安裝了 Node,可以執行 npx 命令。如果沒有的話,訪問這裡。
初始設定// 執行以下程式碼npx create-react-app chatbotcd chatbotyarn add react-chatbot-kityarn 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 屬性值,將資訊渲染到螢幕。
我們進一步看看 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.jsximport 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,以操作 statescrollIntoView - 滑動到聊天框底部,在需要調整檢視時使用這個函式props - 給 widget 定義的 props 將通過 configProps 傳遞給 widgetstate - 通過 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 顯示每個主題對應的資源列表連結。我們看看怎麼實現。
首先,建立一個連結列表元件:
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 元件 handlerimport 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”,機器人會回覆同樣的清單。完成啦!
結語建立專案很有趣,也是一個幫助你拓展技能的很棒的方式。你完全可以動動腦筋,在這個專案基礎上再開發別的,比如一個機器人通過一些簡單的問題找到網店裡最適合的產品,或者是一個幫公司回覆顧客常見問題的機器人。
我覺得持續建立專案真的是開發者提升自己的唯一方式,建議你現在就動起來!
轉發連結:https://www.freecodecamp.org/news/how-to-build-a-chatbot-with-react/