发布于 2026-01-06 10 阅读
0

如何使用 React 和 Stream 构建 Ionic 聊天应用

如何使用 React 和 Stream 构建 Ionic 聊天应用

更新这里有一篇关于 Ionic 首席执行官Max LynchStream首席执行官Thierry Schellenbach对 2020 年云计算技术趋势的精彩文章

React NativeFlutter等平台与Ionic之间存在着巨大的差异。Ionic 坚信,驱动当今 Web 的开放技术代表着未来,也应该用于构建移动应用。正因如此,Ionic 成为为数不多的允许你将同一套代码库复用于 Web 和移动设备的流行平台之一,从而确保你的代码遵循 DRY(避免重复)原则。

另一方面,Flutter 和 React Native 则取代了 Web 技术栈。Flutter 通过其渲染引擎实现这一点,而 React Native则接入iOS和 Android 的原生渲染引擎

Ionic 的优势在于它采用基于 Web 的技术,可以复用同一套代码库。而 Flutter 和 React Native 则不具备如此高的代码复用率;不过,它们的性能更接近原生应用。尽管如此,对于任何应用开发而言,保持代码的 DRY(Don't Repeat Yourself,不要重复自己)原则始终是首要目标。

在本教程中,我将带您了解如何使用 Ionic、React(是的,就是您在 Web 上使用的相同版本)和Stream 的实时聊天 API构建实时聊天应用程序。

如果您想直接体验,Appetize上提供了一个演示,您可以自行评估性能(但请注意,Appetize 会显著降低运行速度)。您也可以下载已签名的 Android APK以获得更流畅的体验。这是 React / Ionic 的 GitHub 代码库这是 API 的 GitHub 代码库

这需要一些要求,主要是 Node.js 的版本(我更喜欢使用 nvm 进行 Node 版本管理),如果您在 macOS 上,则需要Xcode用于 iOS;如果您在 macOS 或 Windows 上并且想要构建 Android 应用,则需要 Android Studio;以及用于依赖管理的yarn

开始编程吧!🤓

1. 安装Ionic

要开始使用Ionic请使用 yarn下载Ionic CLI :

$ yarn global add ionic
Enter fullscreen mode Exit fullscreen mode

安装完成后,使用新的 CLI 从命令行登录 Ionic:

$ ionic login
Enter fullscreen mode Exit fullscreen mode

目前,我们只需要做这些。接下来,我们将使用Create React App继续进行安装。

2. 安装 Create React App 及其依赖项

与安装 Ionic 的方式类似,接下来我们使用 npm 全局安装 Create React App (CRA):

$ yarn global add create-react-app
Enter fullscreen mode Exit fullscreen mode

接下来,创建一个新目录。我将在我的~/Code目录中工作,但您可以随意选择使用任何目录:

$ cd ~/Code
Enter fullscreen mode Exit fullscreen mode

现在,使用 Create React App (CRA) 安装 React – (ionic-chat是将生成的目录的名称——这也是可选的,您可以随意命名):

$ npx create-react-app ionic-chat
Enter fullscreen mode Exit fullscreen mode

进入该ionic-chat目录,我们将开始安装必要的依赖项。

$ yarn add stream-chat stream-chat-react axios react-router react-router-dom @ionic/react
Enter fullscreen mode Exit fullscreen mode

依赖项安装完毕后,让我们继续进行设置的下一步。

3. 使用Heroku设置 API

虽然API体积小巧,但它在聊天系统中扮演着至关重要的角色。该API接受用户在登录界面输入的凭据,并生成一个JWT供聊天应用程序使用。它还会将用户添加到聊天频道中。

为了快速启动 API,我添加了一个简单的Heroku 一键按钮。点击后,它会在 Heroku 上创建一个新的应用程序,然后为您创建一个 Stream Chat 试用版。

部署

点击 Heroku 按钮后,系统会提示您添加应用程序名称——请确保名称唯一。然后点击“部署”按钮,启动 Heroku 部署流程。

安装完成后,从 Heroku 获取环境变量(Heroku 创建应用时会自动生成这些变量),并将其添加到 React 应用的 .env 文件中。环境变量位于 Heroku 控制面板的“设置”部分,如Heroku 这篇博客文章所示。请注意,只有一个名为“STREAM_URL”的环境变量。API 密钥和密钥之间用逗号分隔,:第一个是密钥,第二个是密钥。

或者,如果您想跳过 Heroku,您可以克隆此 GitHub 存储库并使用 yarn start 命令运行 API – 请务必在开始之前运行 yarn install,并且还请务必使用在 Stream 控制面板上找到的凭据填写您的 .env 文件(您需要启用免费聊天试用)。

4. 安装 iOS 模拟器(可选)

如果您已经安装了 Xcode,那就基本万事俱备了。如果没有,并且想要下载 Xcode,可以点击这里下载。Xcode 默认自带 iOS 模拟器。

如果您不想安装 Xcode,您可以选择安装此 npm 包,它将为您安装一个独立的 iOS 模拟器。

$ yarn global add ios-sim
Enter fullscreen mode Exit fullscreen mode

完整的使用说明请参见此处:https://www.npmjs.com/package/ios-sim

5. 安装 Android Studio(可选)

在 macOS 系统上运行 iOS 似乎是测试代码最快的方法;但是,如果您使用的是 Windows 系统或者只是想使用 Android 系统,我将在下面进行介绍。

前往Android Studio 下载页面,选择您需要的下载版本。Android Studio 适用于 iOS、Windows 和 macOS 系统。文件较大,下载可能需要一些时间。

下载完成后,请按照安装说明操作并打开 Android Studio。我们将下载必要的 SDK 并创建一个 Android 虚拟设备 (AVD)。

打开 Android Studio,点击“配置”,然后点击“SDK 管理器”。

现在,打开 SDK 管理器,选择“Android 9.0 (Pie)”,然后点击“应用”。

下载即将开始。下载完成后,返回主屏幕,点击“配置”按钮,然后点击“AVD 管理器”。在 AVD 管理器屏幕上,点击“+ 创建虚拟设备”。

接下来,选择“Pixel 3 XL”设备,然后点击“下一步”。选择“Pie (28)”作为 API 级别,然后点击“下一步”按钮。

最后,点击“完成”,您的AVD就配置完成了。完成后,您可以安全地退出AVD界面,并在AVD管理器中看到您新创建的AVD。

点击“绿色播放按钮”,您的AVD就会启动!

恭喜!您已成功在 Android Studio 中生成 AVD!我们暂时还不会使用它,但 AVD 将在后续教程的测试中派上用场。

6. 创建文件

一切准备就绪;现在,是时候添加必要的文件来让我们的代码运行起来了!我们需要创建一些文件,所以请仔细阅读:

  1. 在目录根目录下,创建ionic.config.json包含以下内容的文件:
{
  "name": "Ionic Chat",
  "type": "custom",
  "integrations": {}
}
Enter fullscreen mode Exit fullscreen mode
  1. 在 中public/index.html,将当前的 HTML 代码替换为以下内容:
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1.0,
    minimum-scale=1.0, maximum-scale=1.0, viewport-fit=cover user-scalable=no"
    />

    <meta name="apple-mobile-web-app-capable" content="yes" />
    <meta
      name="apple-mobile-web-app-status-bar-style"
      content="black-translucent"
    />
    <meta name="theme-color" content="#ffffff" />
    <meta name="apple-mobile-web-app-title" content="Ionic Chat" />

    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />

    <title>Ionic Chat</title>
  </head>
  <body ontouchstart="">
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode
  1. 进入该src/目录;我们将创建并修改一些文件:

在 app.css 文件中,将所有现有的 CSS 代码替换为以下代码:

@import url("https://fonts.googleapis.com/css?family=Open+Sans");

html,
body {
  background: #ffffff;
  padding: env(safe-area-inset-top) env(safe-area-inset-right) env(
      safe-area-inset-bottom
    ) env(safe-area-inset-left);
  font-family: "Open Sans", sans-serif;
}

.no-scroll .scroll-content {
  overflow: hidden;
}

::placeholder {
  color: #3f3844;
}

.login-root {
  text-align: center;
  margin-top: 25%;
}

.login-card > h4 {
  margin-bottom: 22px;
}

.login-card > input {
  padding: 4px 6px;
  margin-bottom: 20px;
  border: 1px solid #d3d3d3;
  background: hsla(0, 0%, 100%, 0.2);
  border-radius: 4px !important;
  font-size: 16px;
  color: #24282e;
  -webkit-box-shadow: none;
  box-shadow: none;
  outline: 0;
  padding: 0 16px 1px;
  -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
  height: 50px;
  width: 300px;
}

.login-card button {
  font-size: 16px;
  background-color: #3880ff;
  border-radius: 4px;
  line-height: 1.4em;
  padding: 14px 33px 14px;
  margin-right: 10px;
  border: 0 solid rgba(0, 0, 0, 0);
  color: #ffffff;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08), 0 2px 4px rgba(0, 0, 0, 0.12);
  border-radius: 6px;
  text-transform: none;
  outline: none;
}

.str-chat__loading-indicator {
  text-align: center;
  margin-top: 15%;
}

.str-chat-channel {
  background-color: #ffffff !important;
}

.str-chat__header-livestream {
  box-shadow: none !important;
  background: transparent;
}

.str-chat__square-button {
  display: none !important;
}

.str-chat__input {
  box-shadow: none !important;
}

.rta__textarea {
  padding: 4px 6px;
  margin-bottom: 20px;
  border: 1px solid #d3d3d3 !important;
  background: hsla(0, 0%, 100%, 0.2);
  border-radius: 4px !important;
  font-size: 14px !important;
  color: #24282e !important;
  -webkit-box-shadow: none !important;
  -webkit-appearance: none !important;
  box-shadow: none !important;
  outline: none !important;
  padding: 0 16px 1px;
  -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
  height: 50px;
}

.str-chat__textarea {
  height: 45px !important;
}

.str-chat__input-footer--count {
  margin-top: 4px;
  margin-left: 4px;
}

.footer {
  margin-bottom: 50px;
}
Enter fullscreen mode Exit fullscreen mode

在 App.js 文件中,将现有代码替换为以下 JavaScript 代码(此逻辑将处理文件之间的路由):

import React from "react";
import { BrowserRouter as Router, Switch } from "react-router-dom";

import Chat from "./Chat";
import Login from "./Login";

import UnauthedRoute from "./UnauthedRoute";
import AuthedRoute from "./AuthedRoute";

const App = () => (
  <Router>
    <Switch>
      <UnauthedRoute path="/auth/login" component={Login} />
      <AuthedRoute path="/" component={Chat} />
    </Switch>
  </Router>
);

export default App;
Enter fullscreen mode Exit fullscreen mode

创建一个名为 `<filename>` 的文件AuthedRoute.js,并将以下内容放入该文件中:

import React from "react";
import { Redirect, Route } from "react-router-dom";

const AuthedRoute = ({ component: Component, loading, ...rest }) => {
  const isAuthed = Boolean(localStorage.getItem("token"));
  return (
    <Route
      {...rest}
      render={props =>
        loading ? (
          <p>Loading...</p>
        ) : isAuthed ? (
          <Component history={props.history} {...rest} />
        ) : (
          <Redirect
            to={{
              pathname: "/auth/login",
              state: { next: props.location }
            }}
          />
        )
      }
    />
  );
};

export default AuthedRoute;
Enter fullscreen mode Exit fullscreen mode

创建一个名为 Chat.js 的文件,并使用以下代码(这是聊天功能的全部逻辑):

import React, { Component } from "react";
import { IonApp, IonContent } from "@ionic/react";
import {
  Chat,
  Channel,
  ChannelHeader,
  Thread,
  Window,
  MessageList,
  MessageInput
} from "stream-chat-react";
import { StreamChat } from "stream-chat";

import "./App.css";
import "@ionic/core/css/core.css";
import "@ionic/core/css/ionic.bundle.css";
import "stream-chat-react/dist/css/index.css";
import "stream-chat-react/dist/css/index.css";

class App extends Component {
  constructor(props) {
    super(props);

    const { id, name, email, image } = JSON.parse(localStorage.getItem("user"));

    this.client = new StreamChat(localStorage.getItem("apiKey"));
    this.client.setUser(
      {
        id,
        name,
        email,
        image
      },
      localStorage.getItem("token")
    );

    this.channel = this.client.channel("messaging", "ionic-chat", {
      image: "https://i.imgur.com/gwaMDJZ.png",
      name: "Ionic Chat"
    });
  }

  render() {
    return (
      <IonApp style={{ paddingTop: "2px" }}>
        <IonContent>
          <Chat client={this.client} theme={"messaging light"}>
            <Channel channel={this.channel}>
              <Window>
                <ChannelHeader />
                <MessageList />
                <div className="footer">
                  <MessageInput />
                </div>
              </Window>
              <Thread />
            </Channel>
          </Chat>
        </IonContent>
      </IonApp>
    );
  }
}

export default App;
Enter fullscreen mode Exit fullscreen mode

接下来,创建一个名为 `.app.js` 的文件Login.js,并使用以下代码(这将为您的应用添加身份验证):

import React, { Component } from "react";
import axios from "axios";

import "./App.css";

class Login extends Component {
  constructor(props) {
    super(props);

    this.state = {
      loading: false,
      name: "",
      email: ""
    };

    this.initStream = this.initStream.bind(this);
  }

  async initStream() {
    await this.setState({
      loading: true
    });

    const auth = await axios.post(process.env.REACT_APP_TOKEN_ENDPOINT, {
      name: this.state.name,
      email: this.state.email
    });

    localStorage.setItem("user", JSON.stringify(auth.data.user));
    localStorage.setItem("token", auth.data.token);
    localStorage.setItem("apiKey", auth.data.apiKey);

    await this.setState({
      loading: false
    });

    this.props.history.push("/");
  }

  handleChange = e => {
    this.setState({
      [e.target.name]: e.target.value
    });
  };

  render() {
    return (
      <div className="login-root">
        <div className="login-card">
          <h4>Ionic Chat</h4>
          <input
            type="text"
            placeholder="Name"
            name="name"
            onChange={e => this.handleChange(e)}
          />
          <br />
          <input
            type="email"
            placeholder="Email"
            name="email"
            onChange={e => this.handleChange(e)}
          />
          <br />
          <button onClick={this.initStream}>Submit</button>
        </div>
      </div>
    );
  }
}

export default Login;
Enter fullscreen mode Exit fullscreen mode

请记得将 .env 文件中的环境变量替换REACT_APP_TOKEN_ENDPOINT为您的 Heroku 端点凭据。

现在,创建一个名为“UnauthedRoute.js用于容纳未经身份验证的用户”的文件:

import React from "react";
import { Redirect, Route } from "react-router-dom";

const UnauthedRoute = ({ component: Component, loading, ...rest }) => {
  const isAuthed = Boolean(localStorage.getItem("token"));
  return (
    <Route
      {...rest}
      render={props =>
        loading ? (
          <p>Loading...</p>
        ) : !isAuthed ? (
          <Component history={props.history} {...rest} />
        ) : (
          <Redirect
            to={{
              pathname: "/"
            }}
          />
        )
      }
    />
  );
};

export default UnauthedRoute;
Enter fullscreen mode Exit fullscreen mode

withSession.js创建一个名为:的文件

import React from "react";
import { withRouter } from "react-router";

export default (Component, unAuthed = false) => {
  const WithSession = ({ user = {}, streamToken, ...props }) =>
    user.id || unAuthed ? (
      <Component
        userId={user.id}
        user={user}
        session={window.streamSession}
        {...props}
      />
    ) : (
      <Component {...props} />
    );

  return withRouter(WithSession);
};
Enter fullscreen mode Exit fullscreen mode

package.json4. 在您​​的文件中安装 Ionic 构建脚本:

"scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "ionic:build": "react-scripts build",
    "ionic:serve": "react-scripts start"
}
Enter fullscreen mode Exit fullscreen mode

CapacitorIonic提供的一款开源框架,可帮助您构建渐进式原生 Web 应用、移动应用和桌面应用。它针对 Ionic 应用进行了优化,但几乎可以与任何其他框架配合使用。

我们将使用 Capacitor 来提升和准备 iOS 和 Android 版本的构建。不过,首先让我们来安装 Capacitor!

$ ionic capacitor add ios
Enter fullscreen mode Exit fullscreen mode

然后,从根目录使用以下命令启动 React 应用:

$ yarn start
Enter fullscreen mode Exit fullscreen mode

在 iOS 设备上打开:

$ ionic capacitor open ios
Enter fullscreen mode Exit fullscreen mode

或者,在安卓设备上打开:

$ ionic capacitor open android
Enter fullscreen mode Exit fullscreen mode

因为我使用的是 macOS 系统,所以我将使用 iOS 模拟器。运行命令后ionic capacitor open ios,Xcode 将会启动。您需要等待大约一分钟,让它索引项目,然后就可以点击运行按钮了。

您的 iOS 模拟器应该会启动并安装该应用程序,您应该会看到类似这样的登录屏幕:

请用您的姓名和邮箱地址登录。别担心,您的信息仅存储在本地,不会传输到任何第三方平台。聊天窗口加载完毕后,您就可以开始聊天了!

接下来是什么?

我建议您继续基于您创建的代码库进行开发。如果您遇到任何问题,可以随时从 GitHub 克隆仓库,重新开始。

在将应用程序部署到 iOS 或 Android 等独立设备方面,Ionic 提供了一系列优秀的教程。iOS 和 Android 的教程都可以在 Ionic 文档中找到

想了解更多关于Stream Chat 的信息?请查看我们的交互式API 导览,它将引导您完成使用 Stream 从零开始创建聊天功能的各个步骤。

Stream 还拥有出色的API 文档和精美的UI 工具包,可让您构建任何类型的实时消息平台。

最后,别忘了查看我们为 Stream Chat 提供的各种 SDK,包括iOS/SwiftAndroid/Java/Kotlin的教程。

喜欢演示吗?我们在Stream Chat 网站上也提供互动演示

祝您编程愉快!✌

文章来源:https://dev.to/nickparsons/how-to-build-an-ionic-chat-app-with-react-and-stream-3c1g