如何使用 React 和 Stream 构建 Ionic 聊天应用
更新:这里有一篇关于 Ionic 首席执行官Max Lynch和Stream首席执行官Thierry Schellenbach对 2020 年云计算技术趋势的精彩文章。
React Native和Flutter等平台与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
安装完成后,使用新的 CLI 从命令行登录 Ionic:
$ ionic login
目前,我们只需要做这些。接下来,我们将使用Create React App继续进行安装。
2. 安装 Create React App 及其依赖项
与安装 Ionic 的方式类似,接下来我们使用 npm 全局安装 Create React App (CRA):
$ yarn global add create-react-app
接下来,创建一个新目录。我将在我的~/Code目录中工作,但您可以随意选择使用任何目录:
$ cd ~/Code
现在,使用 Create React App (CRA) 安装 React – (ionic-chat是将生成的目录的名称——这也是可选的,您可以随意命名):
$ npx create-react-app ionic-chat
进入该ionic-chat目录,我们将开始安装必要的依赖项。
$ yarn add stream-chat stream-chat-react axios react-router react-router-dom @ionic/react
依赖项安装完毕后,让我们继续进行设置的下一步。
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
完整的使用说明请参见此处: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. 创建文件
一切准备就绪;现在,是时候添加必要的文件来让我们的代码运行起来了!我们需要创建一些文件,所以请仔细阅读:
- 在目录根目录下,创建
ionic.config.json包含以下内容的文件:
{
"name": "Ionic Chat",
"type": "custom",
"integrations": {}
}
- 在 中
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>
- 进入该
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;
}
在 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;
创建一个名为 `<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;
创建一个名为 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;
接下来,创建一个名为 `.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;
请记得将 .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;
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);
};
package.json4. 在您的文件中安装 Ionic 构建脚本:
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"ionic:build": "react-scripts build",
"ionic:serve": "react-scripts start"
}
Capacitor是Ionic提供的一款开源框架,可帮助您构建渐进式原生 Web 应用、移动应用和桌面应用。它针对 Ionic 应用进行了优化,但几乎可以与任何其他框架配合使用。
我们将使用 Capacitor 来提升和准备 iOS 和 Android 版本的构建。不过,首先让我们来安装 Capacitor!
$ ionic capacitor add ios
然后,从根目录使用以下命令启动 React 应用:
$ yarn start
在 iOS 设备上打开:
$ ionic capacitor open ios
或者,在安卓设备上打开:
$ ionic capacitor open android
因为我使用的是 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/Swift和Android/Java/Kotlin的教程。
喜欢演示吗?我们在Stream Chat 网站上也提供互动演示。
祝您编程愉快!✌
文章来源:https://dev.to/nickparsons/how-to-build-an-ionic-chat-app-with-react-and-stream-3c1g











