Blitz.js的介绍——一个用于Next.js的全栈框架
Blitz.js采用零API的方式,将你的Next.js前端与后端数据存储连接起来。以下是它的工作原理。
Blitz.js是一个新兴的JavaScript框架,建立在React和Next.js之上。它是一个全栈式的、有主见的框架--这意味着它对如何构建你的JavaScript应用做出了某些假设。也许Blitz最有趣的方面是它所谓的零API方法,其中框架做了连接用户界面和后端数据存储的工作。
让我们亲身体验一下这种有趣而独特的JavaScript应用开发方式。
设置Blitz演示
首先,用以下命令将Blitz添加为一个全局NPM包。npm i blitz@alpha -g.现在,你可以使用工具创建一个新的项目,键入:blitz new demo-app 。清单1显示了我对演示程序的设置。
清单1.创建一个新的Blitz应用程序
$ blitz new demo-app
✔ Pick which language you'd like to use for your new blitz project › Javascript
✔ Pick which template you'd like to use for your new blitz project › full
✔ Install dependencies? … yes
✔ Pick which form you'd like to use for your new blitz project › React Hook Form
Hang tight while we set up your new Blitz app!
在Blitz完成了所有的依赖项的安装后,你可以进入刚刚创建的新目录,cd demo-app ,并通过命令blitz dev ,启动开发服务器。服务器现在运行在localhost:3000,你会得到一个像这里所示的欢迎屏幕。
IDG
图1.Blitz的欢迎屏幕。
你可能注意到的第一件事是,与大多数前端框架不同,Blitz的生成器提供了更精细的全栈脚手架;特别是,它支持用户创建和登录。如果你玩一玩这些功能,你会发现你可以创建一个用户并登录和退出。
添加一个模型
现在让我们按照欢迎页面的建议,通过命令行添加一个模型。转到命令行,按CTRL-C键,停止开发服务器。输入blitz generate all project name:string 。这个命令告诉Blitz添加一个新的模型对象,叫做project ,有一个单独的字符串字段,叫做name 。
通过Prisma迁移数据库
当提示时,确认你要运行prisma migrate dev 。Blitz已经安装并正在使用基于文件的SQLite数据库,它通过Prisma,即对象关系映射(ORM)层进行映射。SQLite和Prisma是数据对象被持久化的地方和方式。prisma migrate dev 命令通知Prisma更新自己以反映dev 数据库(用于开发的默认数据库)中的变化。
选择你自己的数据库和ORM层
请注意,在Blitz中,数据库和ORM层是模块化的,所以如果你喜欢,你可以改变默认值,使用类似MongoDB的东西。你需要给Blitz的改变起一个名字,用于跟踪。
创建一个新的项目
当生成器完成后,用命令blitz dev ,再次启动服务器。返回到浏览器,访问localhost:3000/projects。你会看到一个新的用户界面,你可以用它来创建一个新项目。(点击创建项目)。
如果你在试图创建一个新项目时没有登录,你会得到这样的信息。"错误。你没有经过认证"。这证实了授权已经开始工作了。
现在以用户身份登录,再次尝试创建项目选项。这一次,你会看到一个包含单一名称字段的表格。你可以创建一些项目,你会发现Blitz正在为你搭建一个合理的RESTful模式。
localhost:3000/projects页面给你一个项目列表,你可以点击它来获得项目的详细信息,地址是*localhost:3000/projects/。*注意,你也可以编辑和删除:一个完整的CRUD往返体验。
Blitz项目的布局
让我们看一下Blitz中的项目布局。这让我们感受到Blitz如何设法为我们做这么多事情。
/project-root/db/db.sqlite:SQLite引擎和模式/schema.prisma:Prisma ORM映射/migrations:显示迁移的目录(对回滚很有用)
/mailers:包含用于配置邮件的存根,如忘记密码邮件。/jest.config.js:JEST测试框架的配置/next.config.js:Next.js的配置文件/pages:React.js和Next.js的前端文件/api:支持外部(非Blitz)API访问/auth:登录、注销和注册的页面/projects:project实体的页面。其他对象也遵循同样的模式。
/test:测试文件(JEST)/app:应用程序基础设施/blitz-client.js:客户端配置/blitz-server.js:服务器端的配置/core:应用程序的组件、钩子和布局/projects:project对象的查询、变异和组件。/auth和 :与认证有关的查询、变异和组件。/user
/integrations:第三方集成,如Auth0和Sentry/package.json:Node配置文件,包括Blitz脚本,例如dev/public:静态文件,如favicon
Blitz.js中的RPC
Blitz最不寻常的地方是它对远程过程调用的使用,即RPC。你没有使用REST或GraphQL API,而是将服务器端的代码直接导入客户端的JavaScript中。然后Blitz将服务器端的代码转换为RPC调用。你可以直接从你的客户端JavaScript访问服务器端代码,而Blitz会将网络交互连接起来,使其全部工作。
现在,让我们打开这个文件:blitz-demo/pages/projects/[projectId]/edit.js 。注意,方括号的语法是Next.js处理路径衔接的方式。(projectID 变量将被暴露给处理请求的页面,持有路径中该位置的参数值)。
清单2有Blitz演示的主要重要部分。
清单2.项目 edit.js
import { Suspense } from "react";
import Head from "next/head";
import Link from "next/link";
import { useRouter } from "next/router";
import { useQuery, useMutation } from "@blitzjs/rpc";
import { useParam } from "@blitzjs/next";
import Layout from "app/core/layouts/Layout";
import getProject from "app/projects/queries/getProject";
import updateProject from "app/projects/mutations/updateProject";
import { ProjectForm, FORM_ERROR } from "app/projects/components/ProjectForm";
export const EditProject = () => {
const router = useRouter();
const projectId = useParam("projectId", "number");
const [project, { setQueryData }] = useQuery(
getProject,
{ id: projectId },
{ staleTime: Infinity }
);
const [updateProjectMutation] = useMutation(updateProject);
return (
<>
<Head>
<title>Edit Project {project.id}</title>
</Head>
<div>
<h1>Edit Project {project.id}</h1>
<pre>{JSON.stringify(project, null, 2)}</pre>
<ProjectForm
submitText="Update Project" initialValues={project}
onSubmit={async (values) => {
try {
const updated = await updateProjectMutation({
id: project.id,
...values,
});
await setQueryData(updated);
router.push({
pathname: `/projects/[projectId]`,
query: {
projectId: updated.id,
},
});
} catch (error) {
console.error(error);
return {
[FORM_ERROR]: error.toString(),
};
}
}}
/>
</div>
</>
);
};
const EditProjectPage = () => {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<EditProject />
</Suspense>
<p>
<Link
href={{
pathname: "/projects",
}}
>
<a>Projects</a>
</Link>
</p>
</div>
);
};
EditProjectPage.authenticate = true;
EditProjectPage.getLayout = (page) => <Layout>{page}</Layout>;
export default EditProjectPage;
首先,注意到import 行让我们很好地了解了Blitz的工作方式:该演示导入了React.js、Next.js和Blitz.js库的混合体,以及项目专用组件和Blitz生成的RPC。
表单本身是从app/projects/components/ProjectForm.js ,它从app/core/components/Form.js 下来。Form.js 扩展了react-hook-form 库,它完成了使表单工作的重任。另外,注意到表单是用属性预填充的:initialValues={project} 。页面是如何首先获得project 对象的?
project 对象是通过useQuery 钩子(来自 Blitz)填充的,行数是const [project, { setQueryData }] = useQuery{...} 。项目变量被设置为最终将持有useQuery 钩子的结果。setQueryData 是一个来自Blitz的函数,用于更新对象的缓存,使其执行。
关于查询的更多信息
关于useQuery 和setQueryData 的更多信息,请参见Blitz文档。
useQuery 钩子依赖于getProject 函数,用路径中的projectId 值作为参数。getProject 函数来自于app/projects/queries/getProject.js 。如果你跟着线程走,你会看到getProject.js 依赖于对db 对象的调用,Blitz在/d 目录下创建并导出该对象。清单3显示了getProject 的调用是如何实现的。
清单3. getProject.js
import db from "db";
//...
const project = await db.project.findFirst({
where: {
id: input.id,
},
})
所以,db 对象暴露了一个关于项目成员的查询API。在这里,我们使用它来通过标准API找到项目,使用我们传回的那个projectId 参数。db API主要是Prisma库,Blitz用一些辅助工具来装饰它。
突变对象的过程与创建和查询对象的过程类似,视图依赖于Blitz的基础设施,它将前端与后端联系起来,而不需要明确的API调用。
认证
如果你看一下projects/create.js ,看看那里是如何处理认证的,你会注意到导出的页面被设置了一个authenticate 字段,设置为true ,如清单4所示。
清单4.确保一个页面的安全
NewProjectPage.authenticate = true;
export default NewProjectPage;
再一次,Blitz向我们隐藏了很多工作和细节。类似地,在主pages/index.js ,当前用户是由useCurrentUser 钩子检索的。在查询和突变的情况下,ctx 对象被自动作为一个参数传入,持有会话对象。你可以通过queries/getProject.js 文件看到后面这个功能的作用,它通过检查ctx.session.$isAuthorized() 来保证调用的安全性。
总结
Blitz是一个独特而强大的全栈框架。它在引擎盖下做了大量的工作,而没有完全混淆这些底层操作。一旦你理解了Blitz.js和它的习性,开发就可以沿着许多快乐的路径快速进行,比如围绕基本对象图构建CRUD和执行简单的授权。