MERN 项目初学者指南(一)

原文:MERN Projects for Beginners

协议:CC BY-NC-SA 4.0

一、MERN 部署设置

欢迎来到面向初学者的 MERN 项目,在这里你将学习使用 MERN (MongoDB,Express,React,Node.js)框架构建令人敬畏的 web 应用。这种堆栈在创业领域有很高的需求,因为你可以用它来制作一个全功能的 web 应用。一个懂 HTML、CSS、React 的前端工程师,可以很快学会 Node.js 和 MongoDB,构建一个完全量产就绪的 web app。

在本书中,您将学习如何在 Heroku 中使用 Node.js 代码托管后端。前端站点使用 React 代码和 Firebase 托管。它还通过一个名为 MongoDB Atlas 的云数据库托管。在接下来的五章中,大多数主机设置都是相同的,所以在大多数章节中不会重复。

MERN 堆栈一览

在安装 Firebase 之前,让我们讨论一下 MERN 堆栈中涉及的基础技术。

  • MongoDB 是一个基于 NoSQL 数据库的开源文档。它不同于将数据存储在表中的传统关系数据库。它将数据存储在类似 JSON 的文档中。它具有高度的可扩展性和性能导向性,因此适合现代网络应用。

  • React 是最流行的开源 JavaScript 库,用于构建网站或 web 应用的前端或用户界面。它由脸书开发和维护。

  • Node.js 允许开发者使用 JavaScript 编写服务器端代码。它与前端的 React 或 Angular 以及数据库的 MongoDB 集成得非常好。

  • Express 是 Node.js 的一个框架,通过它可以创建 API 端点,这些端点是任何后端服务器端代码的基础。

Firebase 托管初始设置

你需要一个谷歌账户才能使用 Firebase。进入 https://firebase.google.com ,点击进入右上角的控制台。你必须登录你的谷歌账户,如图 1-1 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-1

Firebase 控制台标题

点击页面中的添加项目链接,如图 1-2 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-2

添加项目

在此页面中,将项目命名为 dating-app-mern ,然后点击继续按钮,如图 1-3 所示。请注意,这只是一个安装说明。你将在下一章开始构建应用。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-3

应用名称

在下一页面中,点击创建项目按钮,如图 1-4 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-4

创建项目

创建项目需要一些时间,如图 1-5 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-5

项目已创建

MongoDB 设置

MongoDB 是您在云上使用的数据库。它也被称为 MongoDB Atlas。这比在本地机器上设置更容易操作。进入 www.mongodb.com 并登录或创建新账户。

创建新项目

登录后,您会看到类似于图 1-6 所示的屏幕。点击新建项目按钮。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-6

MongoDB 新项目

将你的项目命名为 dating-app-mern ,然后点击下一步按钮,如图 1-7 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-7

项目名

在下一个屏幕上,点击创建项目按钮,如图 1-8 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-8

MongoDB 创建项目

在下一个屏幕上,点击建立集群按钮,如图 1-9 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-9

构建集群

在下一个屏幕上,选择自由层,如图 1-10 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-10

自由层

在下一个屏幕上,您需要选择要在其中创建数据库的 AWS 区域。(我选择孟买是因为我住在印度,这给了我低延迟。)之后,点击创建集群按钮,如图 1-11 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-11

选择区域

下一个屏幕显示集群已经创建,这需要时间。您可以返回并创建您的第一个 API 端点,如图 1-12 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-12

集群已创建

数据库用户和网络访问

在 MongoDB 中创建用户,点击数据库访问页签,然后点击添加新数据库用户按钮,如图 1-13 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-13

创建数据库用户

在下一个屏幕上,您需要输入用户名和密码,如图 1-14 所示。你必须记住这两点。接下来,向下滚动并点击添加用户按钮。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-14

添加用户

接下来,进入网络访问选项卡,点击添加 IP 地址按钮,如图 1-15 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-15

网络存取

在弹出的窗口中,点击允许从任何地方访问按钮,然后点击确认按钮,如图 1-16 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-16

允许访问

接下来,返回到集群选项卡,点击连接按钮,弹出如图 1-17 所示的窗口。单击连接您的应用选项卡。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-17

连接应用

点击复制按钮复制连接 URL,如图 1-18 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-18

连接字符串

将后端部署到 Heroku

完成后端代码后,进入 www.heroku.com 部署后端。登录你的 Heroku 账号,点击新建下拉菜单,然后点击新建 app 按钮,如图 1-19 所示。您也可以从命令行使用 Heroku CLI 来实现这一点,但这里不做介绍。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-19

英雄库登录

接下来命名 app,点击创建 app 按钮,如图 1-20 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-20

Heroku app name

下一个屏幕显示了部署您的应用的所有命令,但是您需要 Heroku CLI。点击链接,按照说明将其安装到您的操作系统上,如图 1-21 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-21

希律王的指示

运行backend文件夹中的heroku login命令。系统会询问您是否有权限打开浏览器。此命令要求您按任意键在浏览器中打开。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-22。

在这里,您可以使用您的凭证登录,如图 1-23 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-23

登录凭据

成功登录后,您会看到如图 1-24 所示的页面,您需要关闭该页面。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-24

关闭弹出窗口

您需要将代码从本地机器推送到 Heroku 存储库。现在您已经登录到您的帐户,您可以运行以下命令来连接 Heroku Git。

heroku git:remote -a dating-mern-backend

接下来,让我们运行熟悉的git命令来提交代码。Git 是一个跟踪文件变化的软件。这是软件开发中必须的。以下命令将代码添加到临时区域,然后提交代码。push命令将其推送到远程 Heroku 服务器。

git add .
git commit -m "backend code complete"
git push heroku master

安装完成后,点击打开 app 按钮,进入部署站点,如图 1-25 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-25

打开后端应用

将前端部署到 Firebase

在前端项目完成之后(在下一章中),您可以在 Firebase 中部署它。转到frontend文件夹,在终端中运行firebase login命令。如果是第一次运行,将会打开一个弹出窗口。接下来,运行firebase init命令。键入 Y 继续。

firebase login
firebase init

使用向下箭头键进入托管,如图 1-26 所示。按空格键选择它,然后按 Enter 键。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-26

安装ˌ使成形

选择使用已有项目,如图 1-27 所示,按回车键。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-27

现有项目

接下来选择正确的项目,在我这里是 dating-app-mern-453b1 ,如图 1-28 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-28

正确的项目

接下来选择公共目录,也就是build。下面这个问题问的是一个单页 app 回答。下一个问题是关于 GitHub 部署的;回答,如图 1-29 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-29

建设

接下来,运行frontend文件夹中的npm run build以获得最佳的生产版本。最后一个命令,firebase deploy,将项目部署到 Firebase。如果成功,该网站现在是活的,这将在接下来的章节中显示。

安装 Node.js 和 npm

如果您的系统上还没有安装 Node.js 和 npm(Node 包管理器),我们来看一下它们的安装。本书中的大部分代码都需要 Node.js 和 npm。React 前端代码也需要 Node.js,通过 npm,可以安装很多小型开源程序,为 React 和 Node.js 都增加了功能。

当您安装 Node.js 时,npm 也会自动安装在您的系统上。尽管 macOS 用户可以在互联网上找到类似的指南,但以下说明适用于基于 Windows 的系统。

在你的网页浏览器中,输入 https://nodejs.org/en/download/ ,点击 Windows Installer,如图 1-30 所示。同样,它还会安装 npm。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-30

Node.js installer(Node. js 安装程序)

默认情况下,下载的文件安装在您的下载文件夹中。点击它,然后点击运行按钮,如图 1-31 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-31

快动按钮

在 Node.js 安装弹出窗口中,点击下一步按钮,如图 1-32 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-32

Node.js 欢迎

点击接受最终用户许可协议,然后点击下一步按钮,如图 1-33 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-33

协议

接下来,我建议您使用图 1-34 所示的安装位置。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-34

安装位置

向导要求您选择一个包。保持默认设置,如图 1-35 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-35

默认包

接下来点击复选框,然后点击下一个按钮,如图 1-36 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-36

属国

然后点击安装按钮,如图 1-37 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-37

安装

安装完成后,运行以下命令检查版本并验证一切正常。

node –v
npm -v

摘要

在这一章中,我们学习了创建 MERN(MongoDB,Express,ReactJS,NodeJS)项目的所有不同技术。我们还学习了如何在不同的环境中部署它们,我们将在接下来的章节中使用它们。

二、使用 MERN 开发约会应用

欢迎来到第二章,在这里你将使用 MERN (MongoDB,Express,React,Node.js)框架构建一个约会应用。后端托管在 Heroku,前端站点使用 Firebase 托管。项目中的图标来自 Material-UI。

该 web 应用功能简单,是第一个 MERN 堆栈项目。部署在 Firebase 中的成品 app 的截图如图 2-1 所示。所有数据都来自 MongoDB 数据库,API 端点设置在 Node.js 中。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-1

完成的应用

让我们先回顾一下 React 前端,然后转到后端。打开您的终端并创建一个dating-app-mern文件夹。在里面,使用 create-react-app 创建一个新的 app,名为 dating-app-frontend 。以下是完成此操作的命令。

mkdir dating-app-mern
cd dating-app-mern
npx create-react-app dating-app-frontend

Firebase 托管初始设置

由于前端站点是通过 Firebase 托管的,所以让我们在 create-react-app 创建 React app 的同时创建基本设置。按照第一章中相同的设置说明,我在 Firebase 控制台中创建了 dating-app-mern。

React 基本设置

返回 React 项目,将cd返回到dating-app-frontend目录。用npm start启动 React 应用。

cd dating-app-frontend
npm start

接下来,让我们删除一些你不需要的文件。图 2-2 显示了该应用在 localhost 上的外观。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-2

删除文件

让我们删除所有不必要的样板代码。index.js文件应该如下所示。

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);

App.js只包含文字交友 App MERN 。来自App.css文件的所有内容都已被删除。

import './App.css';
function App() {
  return (
    <div className="app">
      <h1>Dating App MERN </h1>
    </div>
  );
}

export default App;

index.css中,更新 CSS,使margin: 0位于顶部。

* {
       margin: 0;
  }

图 2-3 显示了该应用在 localhost 上的外观。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-3

初始应用

创建标题组件

让我们创建一个标题组件。首先,你必须安装 Material-UI ( https://material-ui.com ),它提供了图标。根据 Material-UI 文档,您需要进行两次 npm 安装。通过dating-app-frontend文件夹中的集成端子安装铁芯。

npm i @material-ui/core @material-ui/icons

接下来,在src文件夹中创建一个components文件夹。在components文件夹中创建两个文件——Header.jsHeader.css—Header.js有三样东西:一个人物图标、一个徽标和一个论坛图标。该徽标来自项目的公共目录,默认情况下包含 React 徽标。

以下是Header.js文件的内容。

import React from 'react'
import './Header.css'
import PersonIcon from '@material-ui/icons/Person'
import IconButton from '@material-ui/core/IconButton'
import ForumIcon from '@material-ui/icons/Forum'
const Header = () => {
    return (
        <div className="header">
            <IconButton>
                <PersonIcon fontSize="large" className="header__icon" />
            </IconButton>
            <img className="header__logo" src="logo192.png" alt="header" />
            <IconButton>
                <ForumIcon fontSize="large" className="header__icon" />
            </IconButton>
        </div>
    )
}

export default Header

在本地主机上的App.js文件中包含Header组件。更新后的代码用粗体标记。

import './App.css';
import Header from './components/Header';

function App() {
  return (
    <div className="app">
      <Header  />
    </div>
  );
}

export default App;

Header.css文件包含以下内容,包括简单的样式,完成了头。

.header{
    display: flex;
    align-items: center;
    justify-content: space-between;
    z-index: 100;
    border-bottom: 1px solid #f9f9f9;
}

.header__logo{
    object-fit: contain;
    height: 40px;
}

.header__icon{
    padding: 20px;
}

图 2-4 显示了应用现在在 localhost 上的样子。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-4

标题组件

创建约会卡组件

现在让我们来研究第二个部分。在components文件夹中创建两个文件DatingCards.jsDatingCards.css。然后将DatingCards组件包含在App.js文件中。更新后的代码用粗体标记。

import './App.css';
import Header from './components/Header';
import DatingCards from './components/DatingCards';
function App() {
  return (
    <div className="app">
      <Header  />
     < DatingCards />
    </div>
  );
}

export default App;

在继续之前,您需要安装一个react-tinder-card包。该包具有提供滑动效果的功能。

npm i react-tinder-card

接下来,将内容放入DatingCards.js。在这里,在一个people状态变量中,您存储了四个人的姓名和图像。接下来,导入DatingCard,并将其作为组件使用。这里,你使用react-tinder-card文档中提到的道具。

需要swipedoutOfFrame功能。当遍历每个人时,使用imgUrl背景图像并在h3标签中显示姓名。

import React, { useState } from 'react'
import DatingCard from 'react-tinder-card'
import './DatingCards.css'
const DatingCards = () => {
    const [people, setPeople] = useState([
       { name: "Random Guy", imgUrl: "https://images.unsplash.com/photo-1520409364224-63400afe26e5?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=658&q=80" },
       { name: "Another Guy", imgUrl: "https://images.unsplash.com/photo-1519085360753-af0119f7cbe7?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=634&q=80" },
       { name: "Random Girl", imgUrl: "https://images.unsplash.com/photo-1494790108377-be9c29b29330?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=634&q=80" },
       { name: "Another Girl", imgUrl: "https://images.unsplash.com/photo-1529626455594-4ff0802cfb7e?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=634&q=80" }
 ])
    const swiped = (direction, nameToDelete) => {
        console.log("receiving " + nameToDelete)
    }
    const outOfFrame = (name) => {
        console.log(name + " left the screen!!")
    }
    return (
        <div className="datingCards">
            <div className="datingCards__container">
                {people.map((person) => (
                    <DatingCard
                        className="swipe"
                        key={person.name}
                        preventSwipe={['up', 'down']}
                        onSwipe={(dir) => swiped(dir, person.name)}
                        onCardLeftScreen={() => outOfFrame(person.name)} >
                        <div style={{ backgroundImage: `url(${person.imgUrl})`}} className="card">
                            <h3>{person.name}</h3>
                        </div>
                    </DatingCard>
                ))}
            </div>
        </div>
    )
}

export default DatingCards

Localhost 显示了四个“人”,如图 2-5 所示,但是您需要设计所有的样式。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-5

所有人

DatingCards.css文件中添加第一个样式,并使datingCards__container成为 flexbox。接下来,将每张卡片设计成包含图片和其他东西的样式。请注意,您正在为每张卡片设置position: relative,这将使元素相对于自身偏移,并提供宽度和高度。

.datingCards__container{
    display: flex;
    justify-content: center;
    margin-top: 10vh;
}

.card{
    position: relative;
    background-color: white;
    width: 600px;
    padding: 20px;
    max-width: 85vw;
    height: 50vh;
    box-shadow: 0px 18px 53px 0px rgba(0, 0, 0, 0.3);
    border-radius: 20px;
    background-size: cover;
    background-position: center;
}

图 2-6 显示了这在本地主机上的样子。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-6

图像出现

让我们再添加三个样式,从这个 swipe 中可以得到一个 card 类中的类。使用position: absolute创造滑动效果的魔力。在DatingCards.css文件中添加以下内容。

.swipe{
    position: absolute;
}
.cardContent{
    width: 100%;
    height: 100%;
}
.card h3{
    position: absolute;
    bottom: 0;
    margin: 10px;
    color: white;
}

前端基本完成,如图 2-7 所示。它包含右扫和左扫功能。除了包含滑动按钮的页脚之外,一切都完成了。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-7

几乎完成

创建滑动按钮组件

现在让我们创建SwipeButtons组件,它是页脚中的按钮。这些按钮增加了应用的风格。因为这是一个简单的应用,所以它们不会起作用。在components文件夹中创建两个文件SwipeButtons.jsSwipeButtons.css。你还需要把它包含在App.js文件中。

更新的内容用粗体标记。

import './App.css';
import Header from './components/Header';
import DatingCards from './components/DatingCards';
import SwipeButtons from './components/SwipeButtons';
function App() {
  return (
    <div className="app">
      <Header  />
     < DatingCards />
     < SwipeButtons />
    </div>
  );
}
export default App;

SwipeButtons.js文件的内容很简单。有五个来自 Material-UI 的图标包裹在IconButton里面。

import React from 'react'
import './SwipeButtons.css'
import ReplayIcon from '@material-ui/icons/Replay'
import CloseIcon from '@material-ui/icons/Close'
import StarRateIcon from '@material-ui/icons/StarRate'
import FavoriteIcon from '@material-ui/icons/Favorite'
import FlashOnIcon from '@material-ui/icons/FlashOn'
import IconButton from '@material-ui/core/IconButton'
const SwipeButtons = () => {
    return (
        <div className="swipeButtons">
            <IconButton className="swipeButtons__repeat">
                <ReplayIcon fontSize="large" />
            </IconButton>
            <IconButton className="swipeButtons__left">
                <CloseIcon fontSize="large" />
            </IconButton>
            <IconButton className="swipeButtons__star">
                <StarRateIcon fontSize="large" />
            </IconButton>
            <IconButton className="swipeButtons__right">
                <FavoriteIcon fontSize="large" />
            </IconButton>
            <IconButton className="swipeButtons__lightning">
                <FlashOnIcon fontSize="large" />
            </IconButton>
        </div>
    )
}
export default SwipeButtons

接下来,在SwipeButtons.css文件中设置按钮的样式。首先,设计swipeButtons类的样式,并使用position: fixed使其灵活。在一个固定的位置,一个元素保持附着在指定的位置(在这个例子中是底部),甚至当用户滚动时。您还设计了由包创建的MuiIconButton-root类的样式。

SwipeButtons.css文件中,用不同的颜色设计每个按钮。

.swipeButtons{
    position: fixed;
    bottom: 10vh;
    display: flex;
    width: 100%;
    justify-content: space-evenly;
}

.swipeButtons .MuiIconButton-root{
    background-color: white;
    box-shadow: 0px 10px 53px 0px rgba(0, 0, 0, 0.3) !important;
}

.swipeButtons__repeat{
    padding: 3vw !important;
    color: #f5b748 !important;
}

.swipeButtons__left{
    padding: 3vw !important;
    color: #ec5e6f !important;
}

.swipeButtons__star{
    padding: 3vw !important;
    color: #62b4f9 !important;
}

.swipeButtons__right{
    padding: 3vw !important;
    color: #76e2b3 !important;
}

.swipeButtons__lightning{
    padding: 3vw !important;
    color: #915dd1 !important;
}

图 2-8 显示了本地主机上的项目。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-8

前端完成

初始后端设置

让我们从 Node.js 代码开始,转到后端。打开一个新的终端窗口,在根目录下创建一个新的dating-app-backend文件夹。输入git init,因为 Heroku 稍后需要它。

mkdir dating-app-backend
cd dating-app-backend
git init

接下来,通过在终端中输入npm init命令来创建一个package.json文件。你被问了几个问题;对于大多数情况,请按回车键。你可以输入一个描述作者,但不是强制的。您通常可以在server.js设置进入点,因为这是标准(见图 2-9 )。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-9

后端初始设置

一旦package.json被创建,你需要创建包含node_modules.gitignore文件,因为你不想以后将node_modules推送到 Heroku。以下是.gitignore文件的内容。

node_modules

接下来,打开package.json."type" : "module"需要在 Node.js 中启用类似 React 的导入,这些模块被称为 ECMA 模块。带有 require 语句的初始模块称为 CommonJS 模块。你可以在 https://blog.logrocket.com/how-to-use-ecmascript-modules-with-node-js/ 了解更多。

您还需要包含一个启动脚本来运行server.js文件。更新的内容用粗体标记。

{
  "name": "dating-app-backend",
  "version": "1.0.0",
  "description": "The dating app backend",
  "main": "server.js",
  "type": "module",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node server.js"
  },
  "author": "Nabendu Biswas",
  "license": "ISC"
}

在开始之前,您需要安装两个软件包。打开终端,在dating-app-backend文件夹中安装 Express 和 Mongoose。

npm i express mongoose

MongoDB 设置

MongoDB 的设置与第一章中描述的相同。你需要遵循它并创建一个名为的新项目。

在继续之前,将nodemon安装在dating-app-backend文件夹中。每当您对server.js文件中的代码进行任何更改时,Node 服务器都会立即重启。

npm i nodemon

初始路线设置

让我们创建初始路由,它通常检查是否一切都设置正确。Node.js 中的 Express 包允许您创建路由,这是大多数互联网的工作方式。大多数后端语言,如 Node.js、Java,都提供了创建这些与数据库交互的路由的功能。初始路由不与数据库交互,只是在您使用 GET 请求访问它时返回一个文本。在dating-app-backend文件夹中创建一个server.js文件。在这里,您首先导入 Express 和 Mongoose 包。接下来,使用 Express 创建一个在端口 8001 上运行的port变量。

第一个 API 端点是一个由app.get()创建的简单 GET 请求,如果成功,它会显示 Hello TheWebDev 文本。

然后你用app.listen()监听 8001 端口。

import express from 'express'
import mongoose from 'mongoose'

//App Config
const app = express()
const port = process.env.PORT || 8001

//Middleware

//DB Config

//API Endpoints
app.get("/", (req, res) => res.status(200).send("Hello TheWebDev"))

//Listener
app.listen(port, () => console.log(`Listening on localhost: ${port}`))

在终端中,键入 nodemon server.js 。可以看到监听 localhost: 8001 控制台日志。为了检查路线是否正常工作,转到http://localhost:8001/查看终点文本(见图 2-10 )。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-10

初始路线

数据库用户和网络访问

在 MongoDB 中,您需要创建一个数据库用户并提供网络访问。该过程与第一章中的过程相同。按照这些说明,获取用户凭证和连接 URL。

server.js中,创建一个connection_url变量,并将 URL 粘贴到从 MongoDB 获得的字符串中。输入您之前保存的密码,并提供一个数据库名称。更新后的代码用粗体标记。

...
//App Config
const app = express()
const port = process.env.PORT || 8001
const connection_url = 'mongodb+srv://admin:yourpassword@cluster0.lggjc.mongodb.net/datingDB?retryWrites=true&w=majority'

//Middleware

//DB Config
mongoose.connect(connection_url, {
    useNewUrlParser: true,
    useCreateIndex: true,
    useUnifiedTopology: true
})

//API Endpoints
app.get("/", (req, res) => res.status(200).send("Hello TheWebDev"))

...

MongoDB 模式和路由

MongoDB 以 JSON 格式存储数据,而不是像 Oracle 这样的传统数据库中的常规表结构。您创建了 MongoDB 所需的模式文件。它告诉你如何在 MongoDB 中存储字段。

这里,cards被认为是一个集合名,您在数据库中存储一个类似于cardSchema的值。它由一个有名字的对象和imgUrl键组成。这些是您在 MongoDB 中使用的名称。创建一个dbCards.js文件,将以下内容放入其中。

import mongoose from 'mongoose'
const cardSchema = mongoose.Schema({
    name: String,
    imgUrl: String
})

export default mongoose.model('cards', cardSchema)

现在,您可以使用该模式来创建向数据库添加数据的端点。这里遵循 MVC 模式;这是 web 应用的传统流程。点击 https://medium.com/createdd-notes/understanding-mvc-architecture-with-react-6cd38e91fefd 了解更多信息。

接下来,使用一个 POST 请求,从用户那里获取任何数据,并将其发送到数据库。您可以使用任何端点。例如,如果你写了一篇关于脸书的文章并点击了 POST 按钮,那么一旦发出 POST 请求,你的文章就会被保存在脸书数据库中。

GET 端点从数据库中获取所有数据。同样,你可以给出任何端点。例如,当您浏览脸书的提要时,一个 GET 请求被发送到端点,端点又从脸书数据库获取所有的帖子。

server.js,中,创建一个到/dating/cards端点的 POST 请求。负载在req.body到 MongoDB。然后你用create()dbCard。如果成功,您会收到状态 201;否则,您会收到状态 500。更新的内容用粗体标记。

接下来,创建/dating/cards的 GET 端点,从数据库中获取数据。您在这里使用find(),如果成功,将收到状态 200(否则,状态 500)。更新的内容用粗体标记。

import express from 'express'
import mongoose from 'mongoose'
import Cards from './dbCards.js'
...

//API Endpoints
app.get("/", (req, res) => res.status(200).send("Hello TheWebDev"))
app.post('/dating/cards', (req, res) => {
    const dbCard = req.body
    Cards.create(dbCard, (err, data) => {
        if(err) {
            res.status(500).send(err)
        } else {
            res.status(201).send(data)
        }
    })
})

app.get('/dating/cards', (req, res) => {
    Cards.find((err, data) => {
        if(err) {
            res.status(500).send(err)
        } else {
            res.status(200).send(data)
        }
    })
})

//Listener
app.listen(port, () => console.log(`Listening on localhost: ${port}`))

要查看路线,让我们使用邮递员应用。下载并安装它。

http://localhost:8001发送 GET 请求,检查它是否在 Postman 中工作,如图 2-11 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-11

初始路线检查

在处理 POST 请求之前,您需要完成两件事情。第一,实行 First 否则,当您稍后部署应用时,会出现跨来源错误。CORS(跨源资源共享)是限制从一个域访问另一个域的机制。假设你在http://example.com上,想访问 http://mybank.com/accountdetails 。CORS 不会允许你这么做的。只有 http://mybank.com 允许与http://example.com跨原点共享时才允许。

打开终端,在dating-app-backend文件夹中安装 CORS。

npm i cors

server.js中,导入 CORS 并与app.use()一起使用。你还需要使用express.json()中间件。它是必需的,因为您需要它来解析来自 MongoDB 的传入 JSON 对象以读取主体。

更新后的代码用粗体标记。

import express from 'express'
import mongoose from 'mongoose'
import Cors from 'cors'
import Cards from './dbCards.js'

...

//Middleware
app.use(express.json())
app.use(Cors())

...

在 Postman 中,将请求更改为 POST,然后添加http://localhost:8001/dating/cards端点。

接下来,点击身体,选择原始。从下拉菜单中选择 JSON(应用/json) 。在文本编辑器中,从DatingCards.js文件中复制数据。通过在关键字中添加双引号来生成数据 JSON。

接下来,点击发送按钮。如果一切正确,您将获得状态:201 已创建(见图 2-12 )。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-12

邮寄路线

您需要测试 GET 端点。将请求更改为 GET,然后单击发送按钮。如果一切正常,你得到状态:200 OK (见图 2-13 )。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-13

获取路线

将后端与前端集成在一起

让我们把后端钩到前端。使用axios包从前端调用。Axios 是一个 JavaScript 库,它向 REST 端点发出 API 请求。您刚刚在后端创建了两个端点。要访问它们,你需要 Axios。打开dating-app-frontend文件夹并安装。

npm i axios

接下来,在components文件夹中创建一个新的axios.js文件,然后创建一个axios的实例。基础 URL 是http://localhost:8001

import axios from 'axios'
const instance = axios.create({
    baseURL: "http://localhost:8001"
})

export default instance

DatingCards.js,中,去掉处于people状态的硬编码内容。然后导入本地的axios并使用useEffect钩子对/dating/cards端点进行 API 调用。收到数据后,使用setPeople()功能将其复位。更新后的代码用粗体标记。

import React, { useState, useEffect } from 'react'
import DatingCard from 'react-tinder-card'
import './DatingCards.css'
import axios from './axios'

const DatingCards = () => {
    const [people, setPeople] = useState([])
    useEffect(() => {
        async function fetchData() {
            const req = await axios.get("/dating/cards")
            setPeople(req.data)
        }
        fetchData()
    }, [])

    const swiped = (direction, nameToDelete) => {
        console.log("receiving " + nameToDelete)
    }
...

http://localhost:3000/看数据。应用现已完成(见图 2-14 )。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-14

应用完成

将后端部署到 Heroku

转到 www.heroku.com 部署后端。你按照第一章中的相同步骤创建了一个名为 dating-mern-backend 的应用。

返回axios.js,将端点改为 https://dating-mern-backend.herokuapp.com 。如果一切正常,你的应用应该可以运行了。

import axios from 'axios'
const instance = axios.create({
    baseURL: https://dating-mern-backend.herokuapp.com
})

export default instance

将前端部署到 Firebase

是时候在 Firebase 中部署前端了。遵循与第一章相同的程序。完成此过程后,站点应处于活动状态并正常工作,如图 2-15 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-15

部署的应用

摘要

在这一章中,我们在 MERN 堆栈中创建了一个约会应用。我们在 ReactJS 中构建前端,并在 Firebase 中托管它。后端构建在 NodeJS 中,托管在 Heroku 中。数据库是在 MongoDB 中构建的。

三、使用 MERN 打造短视频应用

欢迎来到您的下一个 MERN 项目,在这里您将使用 MERN (MongoDB,Express,React,Node.js)框架构建一个非常棒的短视频应用。后端在 Heroku 托管,前端站点使用 Firebase 托管。Material-UI ( https://material-ui.com )提供项目中的图标。

这个 web 应用显示存储在 MongoDB 中的短视频,点击它就可以播放。您可以通过再次点按它来暂停它。这款网络应用还具有非常平滑的垂直滚动功能,可以显示更多视频。在图 3-1 中,可以看到 app 最终部署的版本。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3-1

部署版本

首先使用 React,然后移动到后端。打开您的终端并创建一个short-video-mern文件夹。在里面,使用create-react-app创建一个名为短视频前端的新应用。以下是命令。

mkdir short-video-mern
cd short-video-mern
npx create-react-app short-video-frontend

Firebase 托管初始设置

由于前端站点是通过 Firebase 托管的,所以可以在 create-react-app 创建 React app 的同时创建基本设置。按照第一章中的设置说明,我在 Firebase 控制台中创建了短视频 mern。

React 基本设置

回到 React 项目,将cd转到short-video-frontend目录。用npm start启动 React 应用。

cd short-video-frontend
npm start

index.jsApp.jsApp.css中删除文件和基本设置就像在第二章中所做的一样。遵循这些指示。

图 3-2 显示了该应用在 localhost 上的外观。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3-2

初始应用

创建视频组件

接下来,在src文件夹中创建一个components文件夹。在components文件夹中创建两个文件Video.jsVideo.css。在Video.js文件中,添加一个video标签和一个垂直视频链接。我在我的频道上使用了我的 YouTube 短视频的链接。

以下是Video.js内容。

import React from 'react'
import './Video.css'
const Video = () => {
    return (
        <div className="video">
            <video
                src="https://res.cloudinary.com/dxkxvfo2o/video/upload/v1608169738/video1_cvrjfm.mp4"
                className="video__player"
                loop
            >
            </video>
        </div>
    )
}
export default Video

在本地主机上的App.js文件中包含Video组件。更新后的代码用粗体标记。

import './App.css';
import Video from './components/Video';
function App() {
  return (
    <div className="app">
            <div className="app__videos">
                <Video />
                <Video />
            </div>
    </div>
  );
}

export default App;

接下来,将基本样式放在App.css文件中,包括用于scroll-snap-type的样式,它们是用于滚动的。你还需要让一切居中。接下来,为app__videos类添加一些样式并隐藏滚动条。

html{
    scroll-snap-type: y mandatory;
}

.app{
    height: 100vh;
    background-color: black;
    display: grid;
    place-items: center;
}

.app__videos{
    position:relative;
    height: 800px;
    border-radius: 20px;
    overflow: scroll;
    width: 80%;
    max-width: 500px;
    scroll-snap-type: y mandatory;
}

.app__videos::-webkit-scrollbar{
    display: none;
}

.app__videos{
    -ms-overflow-style: none;
    scrollbar-width: none;
}

图 3-3 显示了该应用在 localhost 上的外观。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3-3

显示的视频

您还需要设计Video.css文件中的videovideo__player类的样式。您在这里再次使用了scroll-snap-type

.video{
    position: relative;
    background-color: white;
    width: 100%;
    height:100%;
    scroll-snap-align: start;
}

.video__player{
    object-fit: fill;
    width: 100%;
    height: 100%;
}

捕捉特征完成。当你滚动时,它平稳地把你带到下一个视频,如图 3-4 所示。此外,通过 CSS,边缘在所有方面都变得完美。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3-4

捕捉特征

目前,视频无法播放。要让它们播放,必须使用一个引用(或 ref)。React 在虚拟 DOM 上工作。一般情况下,只需要在特殊情况下访问 DOM(文档对象模型),使用 refs 访问 DOM 元素。在这种情况下,您需要访问<video> HTML 元素,以便能够访问play()pause()属性,这些属性只能通过引用获得。

首先,导入useRefuseState钩子以获得videoRef变量,该变量在 video 元素中使用,在这里创建一个onClick处理程序来触发一个handleVideoPress函数。

handleVideoPress函数内部,用playing状态变量检查视频是否播放,然后用videoRef.current.pause()设置暂停,将播放状态改为 false。你在else区块做相反的动作。

更新后的Video.js内容以粗体标记。

import React , { useRef, useState } from 'react'
import './Video.css'

const Video = () => {
    const [playing, setPlaying] = useState(false)
    const videoRef = useRef(null)
    const handleVideoPress = () => {
        if(playing){
            videoRef.current.pause()
            setPlaying(false)
        } else {
            videoRef.current.play()
            setPlaying(true)
        }
    }
    return (
        <div className="video">
            <video
                src="https://res.cloudinary.com/dxkxvfo2o/video/upload/v1608169738/video1_cvrjfm.mp4"
                className="video__player"
                loop
                ref={videoRef}
                onClick={handleVideoPress}
            >
            </video>
        </div>
    )
}

export default Video

点击视频在本地主机上播放。再次点按它以暂停。

创建视频页脚组件

让我们处理第二个组件**,**,它显示了用户名、视频标题和视频页脚中的滚动滚动条。

components文件夹中创建两个文件VideoFooter.jsVideoFooter.css。然后将VideoFooter组件包含在Video.js文件中。更新后的代码用粗体标记。

import React , { useRef, useState } from 'react'
import './Video.css'
import VideoFooter from './VideoFooter'

const Video = () => {
    ...
    return (
        <div className="video">
            <video
                src="https://res.cloudinary.com/dxkxvfo2o/video/upload/v1608169738/video1_cvrjfm.mp4"
                className="video__player"
                loop
                ref={videoRef}
                onClick={handleVideoPress}
            >
            </video>
            <VideoFooter />
        </div>
    )
}

export default Video

接下来,在VideoFooter.js文件中添加一个包含用户名的h3标签和一个包含描述的p标签。

import React from 'react'
import './VideoFooter.css'

const VideoFooter = () => {
    return (
        <div className="videoFooter">
            <div className="videoFooter__text">
                <h3>@nabendu82</h3>
                <p>Macbook Air to new Windows editing beast</p>
            </div>
        </div>
    )
}

export default VideoFooter

接下来,在VideoFooter.css文件中设置它们的样式。

.videoFooter{
    position: relative;
    color: white;
    bottom: 150px;
    margin-left: 40px;
    display: flex;
}

.videoFooter__text{
    flex: 1;
}

.videoFooter__text > h3{
    padding-bottom: 20px;
}

.videoFooter__text > p{
    padding-bottom: 20px;
}

图 3-5 显示了本地主机上的文本。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3-5

初始页脚

让我们首先安装 Material-UI,它提供了图标。根据 Material-UI 文档进行两次 npm 安装。通过short-video-frontend文件夹中的集成端子安装铁芯。

npm i @material-ui/core @material-ui/icons

是时候在VideoFooter.js文件中使用了。在videoFooter__ticker div 中包含音符图标MusicNoteIcon,它是从 Material-UI 导入的。

更新的内容用粗体标记。

import React from 'react'
import './VideoFooter.css'
import MusicNoteIcon from '@material-ui/icons/MusicNote'

const VideoFooter = () => {
    return (
        <div className="videoFooter">
            <div className="videoFooter__text">
                <h3>@nabendu82</h3>
                <p>Macbook Air to new Windows editing beast</p>
                <div className="videoFooter__ticker">
                    <MusicNoteIcon className="videoFooter__icon" />
                </div>
            </div>
        </div>
    )
}

export default VideoFooter

这个项目的特色是一个漂亮的跑马灯。为此,您在short-video-frontend文件夹中安装一个名为react-ticker的包。

npm i react-ticker

接下来,在VideoFooter.js文件中包含文档中的股票代码和唱片(或旋转光盘)图像。正如你在新闻频道底部看到的,滚动条在屏幕上移动文本。还显示了一个录制/旋转的光盘图像,您可以很快在其中添加漂亮的动画。

更新的内容用粗体标记。

import React from 'react'
import './VideoFooter.css'
import MusicNoteIcon from '@material-ui/icons/MusicNote'
import Ticker from 'react-ticker'

const VideoFooter = () => {
    return (
        <div className="videoFooter">
            <div className="videoFooter__text">
                <h3>@nabendu82</h3>
                <p>Macbook Air to new Windows editing beast</p>
                <div className="videoFooter__ticker">
                    <MusicNoteIcon className="videoFooter__icon" />
                    <Ticker mode="smooth">
                        {({ index }) => (
                          <>

                            <p>I am a Windows PC</p>
                          </>

                        )}
                    </Ticker>
                </div>
            </div>
            <img className="videoFooter__record" src="https://static.thenounproject.com/png/934821-200.png" alt="video footer" />
        </div>
    )
}

export default VideoFooter

接下来,在VideoFooter.css文件中为滚动条和录制的图像添加样式。在这里,您将滚动条与音乐图标对齐,并添加动画来移动录制的图像。

将以下内容添加到VideoFooter.css文件中。

.videoFooter__icon{
    position: absolute;
}

.videoFooter__ticker > .ticker{
    height: fit-content;
    margin-left: 30px;
    width: 60%;
}

.videoFooter__record{
    animation: spinTheRecord infinite 5s linear;
    height: 50px;
    filter: invert(1);
    position: absolute;
    bottom: 0;
    right: 20px;
}

@keyframes spinTheRecord {
    from {
        transform: rotate(0deg)
    }
    to {
        transform: rotate(360deg)
    }
}

图 3-6 显示了 localhost 上的页脚组件,包括一个滚动滚动条和旋转圆盘。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3-6

页脚完成

创建视频侧栏组件

现在让我们创建一个侧边栏组件,它在视频的右侧显示图标。

components文件夹中创建两个文件VideoSidebar.jsVideoSidebar.css。您还需要包含Video.js文件。更新后的代码用粗体标记。

import React , { useRef, useState } from 'react'
import './Video.css'
import VideoFooter from './VideoFooter'
import VideoSidebar from './VideoSidebar'

const Video = () => {
    ...
    return (
        <div className="video">
            <video
                src="https://res.cloudinary.com/dxkxvfo2o/video/upload/v1608169738/video1_cvrjfm.mp4"
                className="video__player"
                loop
                ref={videoRef}
                onClick={handleVideoPress}
            >
            </video>
            <VideoFooter />
            <VideoSidebar />
        </div>
    )
}

export default Video

接下来,更新VideoSidebar.js文件。这里,你使用了不同的材质界面图标。您还可以使用一个状态变量来保存 like 图标是否被按下;如果是这样,它会从空心图标变为实心图标,并且计数也会改变。

import React, { useState } from 'react'
import './VideoSidebar.css'
import FavoriteIcon from '@material-ui/icons/Favorite'
import FavoriteBorderIcon from '@material-ui/icons/FavoriteBorder'
import MessageIcon from '@material-ui/icons/Message'
import ShareIcon from '@material-ui/icons/Share'

const VideoSidebar = () => {
    const [liked, setLiked] = useState(false)
    return (
        <div className="videoSidebar">
            <div className="videoSidebar__button">
                { liked ? <FavoriteIcon fontSize="large" onClick={e => setLiked(false)} /> : <FavoriteBorderIcon fontSize="large" onClick={e => setLiked(true)} /> }
                <p>{liked ? 101 : 100}</p>
            </div>
            <div className="videoSidebar__button">
                <MessageIcon fontSize="large" />
                <p>345</p>
            </div>
            <div className="videoSidebar__button">
                <ShareIcon fontSize="large" />
                <p>109</p>
            </div>
        </div>
    )
}

export default VideoSidebar

接下来,更新VideoSidebar.css文件。

.videoSidebar{
    position: absolute;
    top: 50%;
    right: 10px;
    color: white;
}

.videoSidebar__button{
    padding: 20px;
    text-align: center;
}

图 3-7 展示了这些可爱的图标,视频侧边栏就做好了。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3-7

侧栏已完成

使组件动态化

来自App.js文件的所有数据被传递给子组件。您使组件成为动态的,以便可以向它们传递道具。像在 React 中一样,使用 props 将数据从父组件传递到子组件。视频侧边栏是第一个要处理的组件。在VideoSidebar.js中,传递数字作为道具。

更新的内容用粗体标记。

...
const VideoSidebar = ({ likes, shares, messages }) => {
    const [liked, setLiked] = useState(false)
    return (
        <div className="videoSidebar">
            <div className="videoSidebar__button">
                { liked ? <FavoriteIcon fontSize="large" onClick={e => setLiked(false)} /> : <FavoriteBorderIcon fontSize="large" onClick={e => setLiked(true)} /> }
                <p>{liked ? likes + 1 : likes }</p>
            </div>
            <div className="videoSidebar__button">
                <MessageIcon fontSize="large" />
                <p>{messages}</p>
            </div>
            <div className="videoSidebar__button">
                <ShareIcon fontSize="large" />
                <p>{shares}</p>
            </div>
        </div>
    )
}

export default VideoSidebar

同样,在VideoFooter.js文件中传递字符串作为道具。

更新的内容用粗体标记。

...
const VideoFooter = ({ channel, description, song }) => {
    return (
        <div className="videoFooter">
            <div className="videoFooter__text">
                <h3>@{channel} </h3>
                <p>{description}</p>
                <div className="videoFooter__ticker">
                    <MusicNoteIcon className="videoFooter__icon" />
                    <Ticker mode="smooth">
                        {({ index }) => (
                          <>

                            <p>{song}</p>
                          </>

                        )}
                    </Ticker>
                </div>
            </div>
            <img className="videoFooter__record" src="https://static.thenounproject.com/png/934821-200.png" alt="video footer" />
        </div>
    )
}

export default VideoFooter

您希望进一步从应用组件钻取道具,以获得不同的视频文件。让我们将这些道具添加到Video.js文件中并使用它们。

更新的内容用粗体标记。

...

const Video = ({ url, channel, description, song, likes, shares, messages }) => {
   ...
    return (
        <div className="video">
            <video
                src={url}
                className="video__player"
                loop
                ref={videoRef}
                onClick={handleVideoPress}
            >
            </video>
            <VideoFooter channel={channel} description={description} song={song}  />
            <VideoSidebar likes={likes} shares={shares} messages={messages}  />
        </div>
    )
}

export default Video

App.js中,你通过所有的道具,可以通过两个不同的视频。

更新的内容用粗体标记。

...
function App() {
  return (
    <div className="app">
      <div className="app__videos">
        <Video
          url="https://res.cloudinary.com/dxkxvfo2o/video/upload/v1608169738/video1_cvrjfm.mp4"
          channel="nabendu82"
          description="Macbook Air to new Windows editing beast"
          song="I am a Windows PC"
          likes={345}
          shares={200}
          messages={90}
        />
        <Video
          url="https://res.cloudinary.com/dxkxvfo2o/video/upload/v1608169739/video2_mecbdo.mp4"
          channel="thewebdev"
          description="Tuesday morning editing on kdenlive in Windows"
          song="Kdenlive is great"
          likes={445}
          shares={290}
          messages={109}
        />
      </div>
    </div>
  );
}
export default App;

前端完成了,该开始后端了。

初始后端设置

让我们转到后端,从 Node.js 代码开始。打开一个新的终端窗口,在根目录下创建一个新的short-video-backend文件夹。移动到short-video-backend目录后,输入git init命令,这是 Heroku 稍后需要的。

mkdir short-video-backend
cd short-video-backend
git init

接下来,通过在终端中输入npm init命令来创建package.json文件。你被问了一堆问题;对于大多数情况,只需按下回车键。你可以提供描述作者,但不是强制的。你一般在server.js做进入点,这是标准的(见图 3-8 )。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3-8

初始服务器设置

一旦package.json被创建,你需要创建包含node_modules.gitignore文件,因为你不想以后将node_modules推送到 Heroku。以下是.gitignore文件的内容。

node_modules

接下来,打开package.json."type" : "module"需要在 Node.js 中启用类似 React 的导入,包括一个启动脚本来运行server.js文件。

更新的内容用粗体标记。

{
  "name": "short-video-backend",
  "version": "1.0.0",
  "description": " The short video app backend",
  "main": "server.js",
  "type": "module",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node server.js"
  },
  "author": "Nabendu Biswas",
  "license": "ISC"
}

在开始之前,您需要安装两个软件包。打开终端,在short-video-backend文件夹中安装 Express 和 Mongoose。正如第二章所讨论的,Express 是 Node.js 框架,通过它你可以轻松构建后端代码。Mongoose 是绑定 Node.js 和 MongoDB 所需的库,因此它是负责在 Node.js 代码中创建模式的桥梁。

npm i express mongoose

MongoDB 设置

MongoDB 的设置与第一章中描述的相同。按照这些说明,创建一个名为的新项目。

在继续之前,将nodemon安装在short-video-backend文件夹中。它帮助server.js中的变化瞬间重启 Node 服务器。

npm i nodemon

初始路线设置

接下来,在short-video-backend文件夹中创建一个server.js文件。在这里,您导入 Express 和 Mongoose 包。然后使用 Express 创建一个运行在端口 9000 上的port变量。

第一个 API 端点是一个由app.get()创建的简单 GET 请求,如果成功,它会显示文本 Hello TheWebDev

然后,用app.listen()监听端口。

import express from 'express'
import mongoose from 'mongoose'

//App Config
const app = express()
const port = process.env.PORT || 9000

//Middleware

//DB Config

//API Endpoints
app.get("/", (req, res) => res.status(200).send("Hello TheWebDev"))

//Listener
app.listen(port, () => console.log(`Listening on localhost: ${port}`))

在终端输入 nodemon server.js 查看监听 localhost: 9000 控制台日志。为了检查路线是否正常工作,转到http://localhost:9000/查看端点文本,如图 3-9 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3-9

本地主机

数据库用户和网络访问

在 MongoDB 中,您需要创建一个数据库用户并授予网络访问权限。该过程与第一章中的解释相同。遵循这些说明,然后获取用户凭证和连接 URL。

server.js中,创建一个connection_url变量,并将 URL 粘贴到 MongoDB 的字符串中。您需要提供之前保存的密码和数据库名称。

更新后的代码用粗体标记。

...

//App Config
const app = express()
const port = process.env.PORT || 9000
const connection_url = ' mongodb+srv://admin:yourpassword@cluster0.ryj4g.mongodb.net/shortVideoDB?retryWrites=true&w=majority'

//Middleware

//DB Config
mongoose.connect(connection_url, {
    useNewUrlParser: true,
    useCreateIndex: true,
    useUnifiedTopology: true
})

//API Endpoints
app.get("/", (req, res) => res.status(200).send("Hello TheWebDev"))

...

MongoDB 模式和路由

接下来,让我们创建 MongoDB 所需的模式文件。它告诉您字段在 MongoDB 中的存储方式。在short-video-backend文件夹中创建一个dbModel.js文件。

这里,shortVideos被认为是一个集合名,您在数据库中存储一个类似于shortVideoSchema的值。它由一个带有 URL、频道、描述、歌曲、喜欢、共享和消息键的对象组成。

import mongoose from 'mongoose'
const shortVideoSchema = mongoose.Schema({
    url: String,
    channel: String,
    description: String,
    song: String,
    likes: String,
    shares: String,
    messages: String
})

export default mongoose.model('shortVideos', shortVideoSchema)

现在,您可以使用该模式来创建向数据库添加数据的端点。

server.js中,创建一个到/v2/posts端点的 POST 请求。负载在req.body到 MongoDB。然后使用create()发送dbVideos.如果成功,您将收到状态 201;否则,您会收到状态 500。

接下来,创建/v2/posts的 GET 端点,从数据库中获取数据。你在这里用的是find()。如果成功,您将收到状态 200(否则,状态 500)。

更新后的代码用粗体标记。

import express from 'express'
import mongoose from 'mongoose'
import Videos from './dbModel.js'
...

//API Endpoints
app.get("/", (req, res) => res.status(200).send("Hello TheWebDev"))

app.post('/v2/posts', (req, res) => {
    const dbVideos = req.body
    Videos.create(dbVideos, (err, data) => {
        if(err)
            res.status(500).send(err)
        else
            res.status(201).send(data)
    })
})

app.get('/v2/posts', (req, res) => {
    Videos.find((err, data) => {
        if(err) {
            res.status(500).send(err)
        } else {
            res.status(200).send(data)
        }
    })
})

//Listener
app.listen(port, () => console.log(`Listening on localhost: ${port}`))

为了检查路线,让我们使用真棒邮递员应用。向http://localhost:9000发送 GET 请求,检查它是否在 Postman 中工作(见图 3-10 )。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3-10

获取请求

在处理 POST 请求之前,您需要完成两件事情。首先,实施 CORS。打开终端,在short-video-backend文件夹中安装 CORS。

npm i cors

server.js中,导入 CORS,然后配合app.use()使用。你还需要使用express.json()中间件。

更新后的代码用粗体标记。

import express from 'express'
import mongoose from 'mongoose'
import Cors from 'cors'
import Videos from './dbModel.js'

...

//Middleware
app.use(express.json())
app.use(Cors())

...

在 Postman 中,将请求更改为 POST,然后添加http://localhost:9000/v2/posts端点。

接下来,点击身体,选择原始。从下拉菜单中选择 JSON(应用/json) 。在文本编辑器中,从App.js文件中复制数据。通过在关键字中添加双引号来生成数据 JSON。

然后,点击发送按钮。如果一切正确,你得到状态:201 已创建,如图 3-11 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3-11

成功消息发布

我类似地插入了其他数据。您需要测试 GET 端点。将请求更改为 GET,然后单击发送按钮。如果一切正确,你得到状态:200 OK ,如图 3-12 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3-12

成功消息获取

将后端与前端集成在一起

让我们用axios包把后端钩到前端。打开short-video-frontend文件夹并安装。

npm i axios

接下来,在components文件夹中创建一个新的axios.js文件,并创建一个axios的实例。基础 URL 是http://localhost:9000

import axios from 'axios'

const instance = axios.create({
    baseURL: "http://localhost:9000"
})

export default instance

App.js中,导入本地axios。然后使用useEffect钩子对/v2/posts端点进行 API 调用。一旦收到数据,使用setVideos()将其存储在videos状态变量中。

在 return 语句中,去掉硬编码的东西。之后,映射视频数组,并将道具传递给视频组件。

更新的内容用粗体标记。

import React, { useState, useEffect } from 'react';
import './App.css';
import Video from './components/Video';
import axios from './components/axios';

function App() {
  const [videos, setVideos] = useState([])
  useEffect(() => {
    async function fetchData() {
        const res = await axios.get("/v2/posts")
        setVideos(res.data)
        return res
    }
    fetchData()
  }, [])

  return (
    <div className="app">
      <div className="app__videos">
        {videos.map(({ url, channel, description, song, likes, shares, messages }) => (
            <Video
              key={url}
              url={url}
              channel={channel}
              description={description}
              song={song}
              likes={likes}
              shares={shares}
              messages={messages}
            />
          ))}
      </div>
    </div>
  );
}

export default App;

可以看到http://localhost:3000/的数据。应用现在已经完成。但是在喜欢的数量上有一个小问题;它显示 3451 而不是 346(见图 3-13 )。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3-13。

出现此问题的原因是从数据库中传递字符串数字。在VideoSidebar.js中,在喜欢的前面加一个 + ,把字符串改成数字。

...

            <div className="videoSidebar__button">
                { liked ? <FavoriteIcon fontSize="large" onClick={e => setLiked(false)} /> : <FavoriteBorderIcon fontSize="large" onClick={e => setLiked(true)} /> }
                <p>{liked ? +likes + 1 : likes}</p>
            </div>

'''

将后端部署到 Heroku

转到 www.heroku.com 部署后端。按照你在第一章中所做的相同步骤,创建一个名为短视频后端的应用。

成功部署后,转到链接。图 3-14 显示了正确的文本。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3-14。

axios.js中,将端点改为 https://short-video-backend.herokuapp.com 。如果一切正常,你的应用应该可以运行了。

import axios from 'axios'
const instance = axios.create({
    baseURL: " https://short-video-backend.herokuapp.com"
})
export default instance

将前端部署到 Firebase

是时候在 Firebase 中部署前端了。遵循与第一章相同的程序。完成此过程后,站点应处于活动状态并正常工作,如图 3-15 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3-15。

摘要

在本章中,我们创建了一个短视频分享应用。我们在 ReactJS 中构建前端,并在 Firebase 中托管它。后端构建在 NodeJS 中,托管在 Heroku 中。数据库是在 MongoDB 中构建的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值