使用 React 和 Firebase 构建全栈笔记本应用程序 📓🔥
在本博客教程中,我们将从零开始,使用 React.js 设置并构建一个全栈笔记本应用程序。我们将从零开始创建所有内容。不过,在开始之前,应用程序的最终版本应该类似于此。您也可以查看该应用程序的最终正式版。
https://react-firebase-notebook.netlify.app/
配置 React 应用程序
让我们启动并运行我们的第一个 React 应用程序。如果您的电脑上尚未安装 Node.js,那么首先需要安装它。为此,请访问 Node.js 官方网站并下载相关且必要的版本。我们需要 Node.js 才能使用 Node 包管理器的 NPM 功能。
现在创建一个文件夹,并在您首选的编码编辑器中打开它。我将使用 VScode。接下来,打开集成终端并输入 npx create-react-app react-firebase-notebook 。该应用将被命名为 react-firebase-notebook,并将使用此命令在当前目录中创建。
安装通常只需几分钟。通常,我们会使用 npm 将软件包导入项目,但这里我们使用软件包运行器 npx,它会为我们下载并配置所有内容,以便我们立即开始使用一个优秀的模板。现在是时候启动我们的开发服务器了,只需运行 npm start,react-app 就会立即在浏览器中打开。
现在是时候研究 create-react-app 提供的文件和文件夹结构了。我们所有的 Node 依赖项都存储在一个名为 node module 的子目录中。然后是 public 文件夹,其中只有 index.html 文件。到目前为止,这似乎是一个普通的 HTML 文件,包含 head、body 和 meta 标签。在 body 标签内,你会发现一个 id 为 root 的 div,后面跟着 fallback noscript 标签,该标签仅在用户的浏览器不支持 JavaScript 时可见。
所以你肯定对内容的来源感到好奇。记住,我们所有的源代码都放在 src 文件夹中,React 会将其注入到根 div 元素中。在 src 文件夹中查找一些样式表、JavaScript 脚本和 SVG 文件。
现在,转到我们的 App.js 文件
在这种情况下,我们只需使用常规 JavaScript 从 React 导入 React 并从 Logo 导入 Logo。接下来,我们有一个名为 App 的常规 JavaScript 函数,它在 React 中称为函数组件,该函数返回一个看起来像 HTML 但实际上是 jsx 的 React 元素,如您所见,其中有一个 div 标签,其 className 为 APP,我们不能单独说 class,因为 class 是 JavaScript 中的保留字,所以我们必须在 jsx 中使用 className。接下来,我们有标题和图像,请注意,在图像源上我们有徽标,它实际上是我们在顶部导入的 JavaScript 变量,因此我们必须用大括号将其括起来以便在 JSX 中使用 JavaScript,然后我们有一个段落,一个锚标记,这就是这个组件的全部内容。
现在让我们看一下index.js文件。
因此,我们再次从 react 导入 react,这次我们还导入了 react-dom,然后导入 CSS 样式表文件,最后,我们从 App.js 导入 App,这是我们刚刚讨论过的文件,还有 service worker,用于使您的应用完全离线工作。之后,我们使用 ReactDom.render,它需要两个参数。第一个参数是 jsx 对象,它包含我们的用户定义组件(react strict mode 是 react 定义的组件,而 App 是用户定义的组件),第二个参数是 document.getElementById('root'),它针对我们 index.html 文件中的根 div,也是我们访问网页中内容的方式。请始终记住,ReactDom 将我们的内容渲染到 index.html 文件的根 div 中。
制作我们的笔记本应用程序。
让我们从头开始,用 React 构建一个小型笔记本应用程序。在此之前,我们先制作一个最终产品的原型或思维导图。最终成品看起来是这样的。
在开始创建项目之前,我们必须先清理一下 create-react-app 提供的一些文件。清理完 src 文件后,它们应该看起来像这样。
现在我们已经完成了 React 的配置,让我们开始在我们的项目上配置 Firebase。
Firebase 是什么?
Firebase 是一个面向 Web 开发者的无服务器移动和 Web 应用平台。这意味着我们无需从头开始设置服务器。由于 Firebase 是预配置服务器,我们无需购买和配置服务器来将网页服务于万维网。所有这些功能都已到位。现在我们只需使用他们的后端服务即可。使用 Firebase,后端操作非常简单。让我们设置这些工具,以便您对我们将在项目中使用的工具有一个大致的了解。
设置 Firebase
您需要一个 Google 帐户来设置 Firebase。注册一个 Firebase 帐户。然后,从顶部栏中选择“转到控制台”以访问 Firebase 控制台,其界面应如下所示:
我们现在可以在 firebase 中创建一个新项目。
我们正在创建一个名为“React-Firebase-Notebook”的项目。您可以随意命名您的应用。
您可以选择启用或禁用 Google Analytics。
现在,选择 Google Analytics 帐户。这样,您选择的 Google Analytics 帐户中就会生成一个新的 Google Analytics 媒体资源,并在您构建项目时将其链接到您的 Firebase 项目。此链接将允许数据在产品之间来回移动。
创建一个项目可能需要几秒钟。
现在,如果我们点击“继续”,就会跳转到我们新的 React-Firebase-Notebook 项目。这是一个免费套餐,非常慷慨,因为它允许你免费试用很多很棒的功能。
现在,我们在该项目概览页面上唯一需要做的就是单击“将 Firebase 添加到 Web 应用程序”,然后注册应用程序名称。
之后,您将可以访问 API 密钥、数据库 URL、身份验证域、应用程序 ID 以及大量其他有趣的内容。
我们将使用该配置来初始化 Firebase 应用程序,因此请将其复制并保存在记事本或其他程序的某个位置。之后,只需单击“下一步”,您将获得用于安装 Firebase 的 CLI 命令,您也应该将其复制到记事本的某个位置。
最后,您将获得部署程序的命令,您应该将其复制并存储在记事本上的某个位置。
现在您已完成 Firebase 项目的配置,请继续控制台。
让我们回到我们的 react 应用程序并打开 App.js 文件,我们将在其中复制并粘贴 firebase 配置。
// App.js
import React from "react";
const firebaseConfig = {
apiKey: "--------------------",
authDomain: "--------------------",
databaseURL: "--------------------",
projectId: "--------------------",
storageBucket: "--------------------",
messagingSenderId: "--------------------",
appId: "--------------------",
measurementId: "--------------------",
};
const App = () => {
return <div>Hello there</div>;
};
export default App;
现在,让我们进入终端,在安装 Firebase 之前,确保我们位于正确的项目文件夹中。安装可能需要几分钟时间。
之后,您的 package.json 文件应该类似于以下内容。
{
"name":"react-firebase-notebook",
"version":"0.1.0",
"private":true,
"dependencies":{
"@testing-library/jest-dom":"^5.11.4",
"@testing-library/react":"^11.1.0",
"@testing-library/user-event":"^12.1.10",
"firebase":"^9.2.0",
"react":"^17.0.2",
"react-dom":"^17.0.2",
"react-scripts":"4.0.3",
"web-vitals":"^1.0.1"
},
"scripts":{
"start":"react-scripts start",
"build":"react-scripts build",
"test":"react-scripts test",
"eject":"react-scripts eject"
},
"eslintConfig":{
"extends":[
"react-app",
"react-app/jest"
]
},
"browserslist":{
"production":[
">0.2%",
"not dead",
"not op_mini all"
],
"development":[
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
现在 Firebase 已经安装,让我们导入所有 (*) firebase 的导出。
import firebase from "firebase/app"
另外,不要忘记初始化您的 Firebase 配置。
//App.js
import React from "react";
import firebase from "firebase/compat/app";
import "firebase/compat/database";
const firebaseConfig = {
apiKey: "--------------------",
authDomain: "--------------------",
databaseURL: "--------------------",
projectId: "--------------------",
storageBucket: "--------------------",
messagingSenderId: "--------------------",
appId: "--------------------",
measurementId: "--------------------",
};
firebase.initializeApp(firebaseConfig);
const App = () => {
return <div>Hello there</div>;
};
export default App;
安装并设置好该项目所需的所有组件后,我们就可以开始创建它了。为此,我们需要对 App.js 文件进行一些更改。首先,我们需要添加 Navbar 部分,因此在 src 文件夹中创建一个 components 文件夹,然后在该 components 文件夹中创建另一个名为 Navbar 的文件夹,其中包含三个名为 index.js 、 Navbar.js 和 Navbar.css 的文件。您的文件夹结构应该如下所示。
// components/Navbar/index.js
import Navbar from "./Navbar.js"
export default Navbar;
让我们开始制作我们的导航栏部分。
// components/Navbar/Navbar.js
import React from "react";
import "./Navbar.css";
const Navbar = () => {
return (
<>
<header className="navbar">
<h2 className="heading">📓 React firebase Notebook </h2>
</header>
</>
);
};
export default Navbar;
现在是时候给我们的导航栏添加一些样式了。
/*components/Navbar/Navbar.css */
.navbar {
display: flex;
justify-content: center;
align-items: center;
width: 99vw;
height: 70px;
background: rgba( 255, 255, 255, 0.15 );
box-shadow: 0 8px 32px 0 rgba( 31, 38, 135, 0.37 );
backdrop-filter: blur( 7.5px );
-webkit-backdrop-filter: blur( 7.5px );
border-radius: 20px;
border: 1px solid rgba( 255, 255, 255, 0.18 );
}
.navbar .heading {
color: #fd5252;
font-size: 20px;
font-weight: 700;
font-family: 'Poppins', sans-serif;
}
现在让我们在 App 组件中导入并初始化 Navbar 组件
// App.js
import React from "react";
import firebase from "firebase/compat/app";
import "firebase/compat/database";
import Navbar from "./components/Navbar";
const firebaseConfig = {
apiKey: "--------------------",
authDomain: "--------------------",
databaseURL: "--------------------",
projectId: "--------------------",
storageBucket: "--------------------",
messagingSenderId: "--------------------",
appId: "--------------------",
measurementId: "--------------------",
};
firebase.initializeApp(firebaseConfig);
const App = () => {
return (
<div>
<Navbar />
</div>
);
};
export default App;
现在是时候创建另一个用于添加注释的组件了,因此为此创建另一个名为 NoteAdd 的文件夹,其中包含三个名为 index.js、NoteAdd.js 和 NoteAdd.css 的文件,您的文件夹结构应类似于此。
// components/NoteAdd/index.js
import NoteAdd from "./NoteAdd";
export default NoteAdd
让我们开始为 NoteAdd 部分制作骨架
// components/NoteAdd/NoteAdd.js
import React from "react";
import "./NoteAdd.css";
const NoteAdd = () => {
return (
<>
<div className="noteadd">
<h1>Add a New Note</h1>
<div className="form-group">
<input
type="text"
className="noteadd-header"
name="noteadd-header"
placeholder="Note Title"
/>
</div>
<div className="form-group">
<textarea
name="noteadd-description"
className="noteadd-description"
placeholder="Note Description"
></textarea>
</div>
<div className="noteadd-button">
<button>Add a Note</button>
</div>
</div>
</>
);
};
export default NoteAdd;
现在是时候为我们的 NoteAdd 部分添加一些样式了。
/* components/NoteAdd/NoteAdd.css */
.noteadd {
display: block;
width: 100%;
max-width: 500px;
margin: 10px auto;
}
.noteadd h1 {
display: flex;
background: rgba(255, 255, 255, 0.15);
justify-content: center;
align-items: center;
color: #8661d1;
font-family: "poppins";
margin-bottom: 10px;
}
input {
border-style: none;
background: transparent;
outline: none;
}
textarea {
border-style: none;
background: transparent;
outline: none;
}
.noteadd-header {
display: flex;
justify-content: center;
align-items: center;
flex-direction: row;
width: 100%;
max-width: 400px;
height: 10px;
margin: 0 auto;
border-radius: 5px;
padding: 1rem;
background: white;
}
.noteadd-header input {
flex-grow: 1;
color: white;
font-size: 1.8rem;
line-height: 2.4rem;
vertical-align: middle;
}
.noteadd-description {
position: relative;
display: flex;
flex-direction: row;
width: 100%;
max-width: 400px;
height: 40px;
margin: 0 auto;
border-radius: 5px;
padding: 1rem;
background: white;
}
.noteadd-header textarea {
flex-grow: 1;
color: white;
font-size: 1.8rem;
line-height: 2.4rem;
vertical-align: middle;
}
.noteadd .form-group {
display: flex;
flex-direction: column;
margin-bottom: 15px;
justify-content: center;
}
.noteadd-button {
display: flex;
justify-content: center;
align-items: center;
}
button {
padding: 10px 15px;
font-size: 24px;
text-align: center;
cursor: pointer;
outline: none;
color: #fff;
background-color: #8661d1;
border: none;
border-radius: 15px;
box-shadow: 0 5px rgb(109, 57, 129);
}
button:hover {
background-color: #906ed3;
}
button:active {
background-color: #fd5252e5;
box-shadow: 0 5px rgb(212, 93, 93);
transform: translateY(4px);
}
现在让我们在 App 组件中导入并初始化 NoteAdd 组件
// App.js
import React from "react";
import firebase from "firebase/compat/app";
import "firebase/compat/database";
import Navbar from "./components/Navbar";
import NoteAdd from "./components/NoteAdd";
import "./App.css";
const firebaseConfig = {
apiKey: "--------------------",
authDomain: "--------------------",
databaseURL: "--------------------",
projectId: "--------------------",
storageBucket: "--------------------",
messagingSenderId: "--------------------",
appId: "--------------------",
measurementId: "--------------------",
};
firebase.initializeApp(firebaseConfig);
const App = () => {
return (
<div className="app">
<Navbar />
<div className="note-section">
<NoteAdd />
</div>
</div>
);
};
export default App;
在 NoteAdd.js 文件中创建一个 useState() hook,因为这个 hook 允许我们将状态集成到函数式组件中。与类组件中的状态不同,useState() 不适用于对象值。如有必要,我们可以直接使用原语为多个变量创建多个 React hook。
const [state, setState] = useState(initialState);
在 React 中,Hooks 必须始终在函数开头声明。这也有助于组件的状态维护以及渲染之间的保存。
现在,我们已经将 title 和 descripton 设置为状态变量,我们可以使用 setter 函数在函数中修改它们。接下来,让我们构建应用程序的第二部分,以便将笔记添加到 Firebase 实时数据库中。我们将创建一个单独的组件来显示这些笔记。NoteAdd 组件包含一个带有输入字段的表单,用户可以在其中输入新笔记的标题和描述。它还包含一个用于将笔记添加到列表的按钮。此外,还需要一个变量来存储用户在输入文本框中输入的标题和描述。为此,我们将创建一个此组件独有的 title 和 description 状态,并将该状态下的 title 和 description 值推送到 Firebase 实时数据库中。NoteAdd 组件将如下所示:
// components/NoteAdd/NoteAdd.js
import React, { useState } from "react";
import firebase from "firebase/compat/app";
import "firebase/compat/database";
import "./NoteAdd.css";
const NoteAdd = () => {
const [title, setTitle] = useState("");
const [description, setDescription] = useState("");
const handleTitleChange = (event) => {
setTitle(event.target.value);
};
const handleDescriptionChange = (event) => {
setDescription(event.target.value);
};
const addNote = () => {
if (title !== "" && description !== "") {
firebase.database().ref("notebook").push({
title: title,
description: description,
});
}
};
return (
<>
<div className="noteadd">
<h1>Add a New Note</h1>
<div className="form-group">
<input
type="text"
className="noteadd-header"
name="noteadd-header"
placeholder="Note Title"
value={title}
onChange={(val) => handleTitleChange(val)}
/>
</div>
<div className="form-group">
<textarea
name="noteadd-description"
className="noteadd-description"
placeholder="Note Description"
value={description}
onChange={(val) => handleDescriptionChange(val)}
></textarea>
</div>
<div className="noteadd-button">
<button onClick={() => addNote()}>Add a Note</button>
</div>
</div>
</>
);
};
export default NoteAdd;
现在您已经更改了 NoteAdd 组件,您将需要转到 Firebase 控制台并选择实时数据库。
系统将提示您配置数据库位置,因此请选择您喜欢的位置,然后单击下一步。
之后,系统会提示您选择 Firebase 规则的测试模式或锁定模式。选择其中一种,然后点击“下一步”继续。
最后,既然您已成功创建 Firebase 实时数据库,让我们更新安全规则。我们不会在这里实现任何安全功能,因此只需将“read”和“write”设置为“true”即可。
现在是时候创建另一个用于显示笔记本的组件了,为此,请创建另一个名为 Notebook 的文件夹,其中包含三个名为 index.js 、 Notebook.js 和 Notebook.css 的文件。因此,您的文件夹结构应该类似于以下内容。
// components/Notebook/index.js
import Notebook from "./Notebook";
export default Notebook;
让我们开始为 NoteAdd 部分制作骨架
// components/Notebook/NoteAdd.js
import React from "react";
import firebase from "firebase/compat/app";
import "firebase/compat/database";
import "./Notebook.css";
const Notebook = (props) => {
const deleteNotebook = (id) => {
firebase.database().ref("notebook").child(id).remove();
};
return (
<>
<section className="notebook-container">
<div className="notebook">
{props.notebook.map((note, index) => (
<React.Fragment key={index}>
<div className="notebookInfo" key={note.id}>
<div className="notebookInfo-title">
<h3>{note.title}</h3>
<div
className="remove"
onClick={() => deleteNotebook(note.id)}
>
🗑️
</div>
</div>
<div className="notebookInfo-description">
<p>{note.description}</p>
</div>
</div>
</React.Fragment>
))}
</div>
</section>
</>
);
};
export default Notebook;
现在是时候给我们的笔记本部分添加一些样式了。
.notebook {
display: grid;
grid-template-columns: 1fr;
grid-gap: 20px;
padding: 20px;
}
.notebook .notebookInfo {
background: rgba(209, 97, 175, 0.25);
box-shadow: 0 8px 32px 0 rgba(135, 31, 100, 0.37);
backdrop-filter: blur(2.5px);
border-radius: 20px;
border: 1px solid rgba(255, 255, 255, 0.18);
display: flex;
flex-direction: column;
justify-content: center;
padding: 2rem;
min-height: 1rem;
width: 20rem !important;
margin: 0rem auto;
}
.notebook .notebookInfo .notebookInfo-title {
background: rgba(212, 134, 98, 0.25);
box-shadow: 0 8px 32px 0 rgba(135, 31, 130, 0.37);
border-radius: 15px;
border: 1px solid rgba(255, 255, 255, 0.18);
backdrop-filter: blur(2.5px);
display: flex;
justify-content: space-between;
align-items: center;
}
.notebookInfo-title h3 {
padding-left: 1rem;
}
.notebookInfo-title .remove {
padding-right: 1rem;
}
.notebook .notebookInfo .notebookInfo-title {
color: #f3f3f3;
margin: 0;
padding: 0;
font-family: "Poppins";
}
.notebook .notebookInfo .notebookInfo-title .remove {
color: #ff0000;
font-size: 24px;
font-weight: 700;
}
.notebook .notebookInfo .notebookInfo-description {
padding: 10px;
}
.remove {
cursor: pointer;
}
@media screen and (min-width: 768px) {
.notebook {
grid-template-columns: repeat(3, 1fr);
}
}
现在让我们在 App 组件中导入并初始化 Notebook 组件,但在此之前,让我们先深入了解一下 useEffect() 钩子。通过使用此钩子,你可以告诉 React 你的组件需要在渲染后执行某些操作。React 会记住你传递的函数(我们将其称为“效果”),并在执行 DOM 更新后调用它。为此,我们设置了文档标题,但我们也可以执行数据获取或调用其他一些命令式 API。将 useEffect() 放置在组件内部可以让我们直接从效果中访问 count 状态变量(或任何 props)。我们不需要特殊的 API 来读取它——它已经在函数作用域中了。钩子采用了 JavaScript 闭包,避免在 JavaScript 已经提供解决方案的情况下引入特定于 React 的 API。useEffect() 钩子有点类似于我们所知道的类组件的生命周期方法。它在组件的每次渲染(包括初始渲染)后运行。因此,它可以被认为是 componentDidMount、componentDidUpdate 和 componentWillUnmount 的组合。如果我们想要控制 effect 何时运行(仅在初始渲染时,或仅在特定状态变量发生变化时),我们可以将依赖项传递给 effect 来实现。此钩子还提供了一个清理选项,允许在组件销毁前清理资源。effect 的基本语法是: useEffect(didUpdate) 。
让我们创建一个函数,从 Firebase 数据库中添加和删除注释,然后将其保存在 useEffect 钩子中。
const updateNotes = () => {
firebase
.database()
.ref("notebook")
.on("child_added", (snapshot) => {
let note = {
id: snapshot.key,
title: snapshot.val().title,
description: snapshot.val().description,
};
let notebook = noteBookData;
notebook.push(note);
setNoteBookData([...noteBookData]);
});
firebase
.database()
.ref("notebook")
.on("child_removed", (snapshot) => {
let notebook = noteBookData;
notebook = noteBookData.filter((note) => note.id !== snapshot.key);
setNoteBookData(notebook);
});
};
useEffect(() => {
updateNotes();
}, []);
最后,将 Notebook 作为组件导入,并将 noteBookData 作为 props 传递。
// components/Notebook/Notebook.js
import React, { useState, useEffect } from "react";
import firebase from "firebase/compat/app";
import "firebase/compat/database";
import Navbar from "./components/Navbar";
import NoteAdd from "./components/NoteAdd";
import Notebook from "./components/Notebook";
import "./App.css";
const firebaseConfig = {
apiKey: "--------------------",
authDomain: "--------------------",
databaseURL: "--------------------",
projectId: "--------------------",
storageBucket: "--------------------",
messagingSenderId: "--------------------",
appId: "--------------------",
measurementId: "--------------------"
};
firebase.initializeApp(firebaseConfig);
const App = () => {
const [noteBookData, setNoteBookData] = useState([]);
const updateNotes = () => {
firebase
.database()
.ref("notebook")
.on("child_added", (snapshot) => {
let note = {
id: snapshot.key,
title: snapshot.val().title,
description: snapshot.val().description,
};
let notebook = noteBookData;
notebook.push(note);
setNoteBookData([...noteBookData]);
});
firebase
.database()
.ref("notebook")
.on("child_removed", (snapshot) => {
let notebook = noteBookData;
notebook = noteBookData.filter((note) => note.id !== snapshot.key);
setNoteBookData(notebook);
});
};
useEffect(() => {
updateNotes();
}, []);
return (
<div className="app">
<Navbar />
<div className="note-section">
<NoteAdd />
<Notebook notebook={noteBookData} />
</div>
</div>
);
};
export default App;
最后,让我们将更多样式添加到项目中。现在,转到您的 App.css 文件并更新样式,或者直接复制并粘贴以下 CSS 代码。
* {
margin: 0;
padding: 0;
}
.app {
background-image: linear-gradient( to right, rgb(242, 112, 156), rgb(255, 148, 114) );
min-height: 100vh;
width: 100vw;
align-items: center;
}
.note-section {
padding: 15px;
}
如果您正确地遵循了所有步骤,您的项目应该是这样的。
该项目的完整源代码可以在这里找到。
https://github.com/aviyeldevrel/devrel-tutorial-projects/tree/main/React-firebase-notebook
结论
在本篇博客教程中,我们成功创建了一个外观精美的笔记本应用,它允许您添加笔记并将其保存到数据库中。从这里开始,我们可以发挥无限创意,想出各种方法来改进这款应用,同时磨练或掌握您的 React 和 Firebase 技能。如果您觉得这很简单,请尝试一下代码,并尝试添加更多功能,例如用户身份验证功能、个性化笔记配置文件、通知等等。干杯!祝您编程愉快!
主要文章可在此处查看 => https://aviyel.com/post/1277
编码愉快!!
如果您是项目维护者、贡献者或只是开源爱好者,请关注@aviyelHQ或在 Aviyel 上注册以获得早期访问权限。
加入 Aviyel 的 Discord => Aviyel 的世界
推特 =>[ https://twitter.com/AviyelHq ]
文章来源:https://dev.to/aviyel/build-a-full-stack-notebook-app-using-react-and-firebase-47m