如今,你不需要知道如何从头开始设置服务器和数据库来构建全栈应用程序。无服务器技术的出现,使你更容易扩展你的应用程序,而无需手动管理基础设施的麻烦。在现代技术世界中,一切都由API驱动。
有许多工具可以帮助你建立可扩展的应用程序,而没有通常与全栈开发相关的复杂性和运营成本。根据你的项目要求选择最合适的解决方案,可以在未来为你节省很多麻烦和技术债务。
在本指南中,我们将比较Firebase和Fauna,对每个工具的学习曲线、复杂性、可扩展性、性能和价格进行评估。
什么是Firebase?
Firebase是一个后台即服务(BaaS)工具,提供各种服务,包括认证、实时数据库、crashlytics、存储和无服务器云功能,仅举几例。
- 实时数据库和云防火墙用于存储文件结构化的数据和同步应用程序
- 云功能是无服务器功能,用于部署自定义的业务逻辑
- Firebase主机使你能够同时部署静态和动态内容
- 云存储是用来存储和提供大量的用户生成的内容,如照片和视频。
什么是Fauna?
Fauna(原名FaunaDB)是一个无服务器应用框架,它在传统数据库上提供了一个GraphQL API层。此外,它还将DBMS转化为数据API,提供你操作数据库所需的所有功能。
Fauna提供。
- 多种模型来操作数据
- 用于数据访问的多个API,包括本地GraphQL
- 强大的数据一致性
- 内置认证
为了证明使用Firebase和Fauna的优点和缺点,我们将指导你如何用每个数据库建立一个例子。
下面是我们将建立的一个快速演示。
React和Firebase
在前端领域,使用React和Firebase是很常见的,因为它可以让前端开发者构建全栈式的应用程序。Firebase是一个BaaS工具,使Web和移动开发者更容易实现常见的功能,如认证、文件存储和CRUD数据库操作。
对于更深入的研究,包括Firebase的配置和初始设置,请查看 "开始使用react-redux-firebase"。
Firebase组件图
让我们从实体/关系图和组件图开始。
首先,在根目录下创建firebase.js
,并添加以下代码。
import firebase from "firebase";
const config = {
apiKey: "API_KEY",
authDomain: "AUTH_DOMAIN",
databaseURL: "DATABASE_URL",
projectId: "PROJECT_ID",
storageBucket: "STORAGE_BUCKET",
messagingSenderId: "MESSAGING_SENDER_ID",
appId: "APP ID",
};
// Initialize Firebase
firebase.initializeApp(config);
export default firebase;
复制代码
一旦你配置好了Firebase,你就可以在你的组件中直接使用它。
从Firebase读取数据
下一步,我们将从Firebase获取所有项目数据。
useEffect(() => {
const fetchData = async () => {
setLoading(true);
const db = firebase.firestore();
const data = await db.collection("projects").get();
setProjects(data.docs.map((doc) => ({ ...doc.data(), id: doc.id })));
setLoading(false);
};
fetchData();
}, []);
复制代码
使用下面的代码连接到Firebase。
const db = firebase.firestore();
复制代码
一旦Firebase建立了DB连接,我们就可以用下面的代码从一个特定的集合中获取数据了。
const data = await db.collection("projects").get();
复制代码
写入数据到Firebase
将数据插入到Firebase中和读取数据一样简单。首先,创建一个项目。
将以下代码添加到 [onClick](https://blog.logrocket.com/a-guide-to-react-onclick-event-handlers-d411943b14dd/)
函数。
const db = firebase.firestore();
db.collection("projects")
.add({ name })
.then(async (res) => {
// component logic comes here //
setModalState(!modalState);
toast.success("Project created Successfully");
})
.catch((err) => {
toast.error("Oops!! Something went wrong");
console.log("err", err);
});
复制代码
我们可以使用Firebase的add
函数来向指定的集合添加数据。
在Firebase中更新数据
要更新Firebase中的数据,使用set
函数。
const db = firebase.firestore();
db.collection("projects")
.doc(id)
.set(
{
description: project.description,
},
{ merge: true }
)
.then((res) => {
toast.success("Project Updated Successfully");
})
.catch((err) => {
toast.error("Oops!! Something went wrong");
console.log("Error while updating project", err);
});
复制代码
merge
选项使我们能够将新的数据与现有的数据一起添加。否则,它就会替换掉这些数据。
Firebase ACID事务
Firebase支持事务。你可以通过批量设置操作来保持数据的一致性。例如,如果你删除一个项目,你也需要删除所有与之相关的任务。因此,你需要把它作为一个事务来执行。
关于事务,有几件重要的事情需要注意。
- 读取操作必须先于写入操作
- 如果一个并发编辑影响到事务所读取的文档,调用事务的函数(事务函数)可能会运行不止一次
- 事务函数不应该直接修改应用程序的状态
- 当客户端处于离线状态时,交易将失败。
var sfDocRef = db.collection("projects").doc();
return db.runTransaction((transaction) => {
// This code may get re-run multiple times if there are conflicts.
return transaction.get(sfDocRef).then((sfDoc) => {
if (!sfDoc.exists) {
throw "Document does not exist!";
}
// delete tasks here
});
}).then(() => {
console.log("Transaction successfully committed!");
}).catch((error) => {
console.log("Transaction failed: ", error);
});
复制代码
Fauna的设置
在我们开始为我们的示例应用程序设置Fauna之前,我们必须在Dashboard中创建一个账户、数据库和集合。
现在是设置Fauna的时候了。我们将把我们的应用程序构造如下。
config
components
api
config
将设置Fauna,api
将包含对db
的所有查询。创建db.js
,并添加以下内容。
import Fauna from "Fauna";
const client = new Fauna.Client({
secret: process.env.REACT_APP_Fauna_KEY,
});
const q = Fauna.query;
export { client, q };
复制代码
在Fauna中创建数据
接下来,我们将为读取、插入和更新操作创建API。
import { client, q } from "../config/db";
const createProject = (name) =>
client
.query(
q.Create(q.Collection("projects"), {
data: {
name,
},
})
)
.then((ret) => ret)
.catch((err) => console.error(err));
export default createProject;
复制代码
Fauna中的每个查询都以client.query
开始。要将数据插入DB,使用q.Create
来包装集合和数据。
q.Create(<Collection>, {<data>})
复制代码
从Fauna读取数据
有两种方法可以从Fauna中读取数据。
- 使用索引来获取所有的数据
- 直接从集合中获取数据,只要你有
id
当你需要获取所有的数据而不是某些特定的数据时,建议使用索引来获取数据。
import { client, q } from "../config/db";
const getAllProjects = client
.query(q.Paginate(q.Match(q.Ref("indexes/all_projects"))))
.then((response) => {
console.log("response", response);
const notesRefs = response.data;
const getAllProjectsDataQuery = notesRefs.map((ref) => {
return q.Get(ref);
});
// query the refs
return client.query(getAllProjectsDataQuery).then((data) => data);
})
.catch((error) => console.warn("error", error.message));
export default getAllProjects;
复制代码
在这里,我们使用集合的索引来获取所有的项目数据。默认情况下,我们可以使用q.Paginate
分页数据,并获取所有符合indexes/all_projects
的数据。
如果我们有id
,我们可以按以下方式获取数据。
client.query(
q.Get(q.Ref(q.Collection('projects'), <id>))
)
.then((ret) => console.log(ret))
复制代码
Fauna中一对多的关系
关系是设计数据库及其模式时的一个重要概念。在这里,我们有一个project
和task
实体,具有一对多的关系。有两种方法可以为这种关系设计我们的数据库:你可以把任务ID作为一个数组添加到项目集合中,或者把项目ID添加到每个任务的数据中。
下面是如何将任务ID作为数组添加到项目集合中。
这里是如何将项目ID添加到每个任务的数据中。
让我们按照第一种方法,将任务ID添加到项目集合中。
import { client, q } from "../config/db";
const createTask = async (projectId, name, description) => {
try {
const taskData = await client.query(
q.Create(q.Collection("tasks"), {
data: {
name,
description,
projectId,
},
})
);
let res = await client.query(
q.Let(
{
projectRef: q.Ref(q.Collection("projects"), projectId),
projectDoc: q.Get(q.Var("projectRef")),
array: q.Select(["data", "tasks"], q.Var("projectDoc"), []),
},
q.Update(q.Var("projectRef"), {
data: {
tasks: q.Append(
[q.Ref(q.Collection("tasks"), taskData.ref.value.id)],
q.Var("array")
),
},
})
)
);
return taskData;
} catch (err) {
console.error(err);
}
};
export default createTask;
复制代码
首先,将数据插入到任务集合中。
const taskData = await client.query(
q.Create(q.Collection("tasks"), {
data: {
name,
description,
projectId,
},
})
);
复制代码
接下来,将任务ID添加到项目集合中。
let res = await client.query(
q.Let(
{
projectRef: q.Ref(q.Collection("projects"), projectId),
projectDoc: q.Get(q.Var("projectRef")),
array: q.Select(["data", "tasks"], q.Var("projectDoc"), []),
},
q.Update(q.Var("projectRef"), {
data: {
tasks: q.Append(
[q.Ref(q.Collection("tasks"), taskData.ref.value.id)],
q.Var("array")
),
},
})
)
);
复制代码
该 [Let](https://docs.fauna.com/fauna/current/api/fql/functions/let?lang=javascript)
函数将一个或多个变量绑定为一个单一的值或表达式。
更新Fauna中的数据
要更新Fauna中的数据,请使用以下查询。
await client.query(
q.Update(q.Ref(q.Collection("projects"), projectId), {
data: { description },
})
);
复制代码
我们已经涵盖了使用Firebase和Fauna的CRUD应用中涉及的所有功能。你可以在GitHub上找到这个例子的完整源代码。
现在我们了解了它们的工作原理,让我们来比较一下Firebase和Fauna,并总结一下它们的优点和缺点。
Firebase与Fauna
在我们开始比较Firebase和Fauna之前,值得注意的是,这些只是我基于个人偏好、我自己的分析以及我在构建上述例子的经验的意见。其他人可能不同意,欢迎你在评论中表达你的意见。
学习曲线
Firebase很容易学习和适应,因为它的大部分功能都与JavaScript的功能相似。比如说。
get()
从Firebase检索数据set()
向Firebase中插入数据update()
更新Firebase中的数据
另一方面,Fauna有一个相当陡峭的学习曲线。你可以使用GraphQL或者Fauna查询语言(FQL)。要理解这些概念并学习FQL的工作方式需要一些时间。但是,一旦你很好地掌握了它,在更短的时间内编写复杂的查询就变得很容易。
设置和可扩展性
Firebase和Fauna在客户端的设置都是简单明了的。这两个数据库都是为构建可扩展的后端解决方案而设计的。在我看来,Fauna是构建复杂应用程序的更好选择。我很快就会解释原因。
Fauna与GraphQL配合得很好,可以用低延迟的全球CDN提供服务。与Fauna相比,Firebase速度快,反应快,而且容易设置。
复杂的查询和操作
随着你的应用程序的增长,你可能会遇到需要写一些复杂的查询,比如。
- 获取汇总的数据以生成报告
- 支付处理
- 事务性查询
- 聚合
正如你从我们上面的例子中看到的,Fauna可以有效地处理复杂的查询和操作。Fauna是一个分布式数据库,可以是一个关系型、文档型和图型数据库。
Fauna的主要特点之一是它能够处理ACID事务,这就是为什么它能够轻松处理复杂的查询。
例如,Fauna中的函数,如Lambda()
,Let()
, 和Select()
,使你能够用较少的代码编写强大的查询。
价格
Fauna的免费层包括100,000次读、50,000次写和500,000次计算操作。对于个人企业来说,每月23美元涵盖了大部分的操作。
Firebase包括50,000次读取,20,000次写入,以及1GB的存储空间,这涵盖了操作。它是基于 "按需付费 "的模式。
支持和社区
Firebase和Fauna都有很好的支持和文档。与Fauna相比,Firebase社区是成熟而庞大的,因为网络和移动开发者都广泛使用它。Fauna有特别好的文档,可以帮助你轻松理解基本概念。
Firebase vs. Fauna。哪个更好?
如果你打算使用较少的复杂查询,并且需要快速建立一个应用程序,那么Firebase更适合。因此,当你的应用程序的整合程度有限时,它是一个不错的选择。同样,如果你需要在短时间内开发一个快速的原型或小规模的应用程序,Firebase是最好的解决方案,因为它包含了电池。
当你的应用程序在处理复杂的查询方面需要高度的可扩展性时,Fauna是理想的选择。它可以处理一个多模型的数据库,所有的模型都可以从一个查询中获得。如果你需要建立一个可以处理关系型数据库结构的可扩展的应用程序,Fauna就特别有用。然而,请注意,Fauna不提供企业内部数据库。
The postFirebase and Fauna:前端开发人员的数据库工具比较首次出现在LogRocket博客上。