探索 AdonisJS

MMaximilian Schwarzmüller
Computing/SoftwareSmall Business/StartupsInternet Technology

Transcript

00:00:00谢谢。
00:00:30好的。大家好。欢迎来到今天的直播,我们来聊聊 Adonis.js,应该会很有趣。这是一个我从未使用过的、相当有趣的框架和库。我一直想尝试一下,所以我想今天就在直播中试试看。
00:01:00让我先快速准备一下。
00:01:07那么,希望大家都很好。感谢大家的加入。
00:01:16让我们开始吧。是的。嗨,大家好。所以,今天我想深入了解一下 Adonis 并对其进行一些探索。纯粹是为了好玩,因为 Adonis.js 是一个与众不同的 JavaScript 框架。
00:01:38当然,我们已经不在 2019 年了。所以,你可能会争论说如今 JavaScript 框架到底有多重要,对吧?框架战争的时代已经结束了。我们每天都有一个新框架出现的日子已经过去了。
00:01:54现在一切都与人工智能有关。但正因为如今一切都围绕着 AI,我想为什么不举办一场直播,来看看一些与 AI 完全无关的东西呢。
00:02:08Adonis 是一个非常有趣的框架和库。正如我提到的,我从没用过它,但我了解它。多年来我一直在断断续续地关注它。
00:02:20总而言之,它是 JavaScript 版的 Laravel。这就是我对它的理解。这是我对 Adonis 的理解。它是一个全栈框架,在所有的 React 框架变得全栈化之前,它就已经存在了。
00:02:40所以,在 Next.js 超级流行之前……我不确定它是否在 Next.js 发布之前就有了,但我想说是在它变得非常流行之前。
00:02:51而且,是的,它自带了很多有趣的东西。我今天的想法实际上就是浏览一下这里的官方介绍指南,看看里面有什么,顺便玩一玩,找出它是什么以及它是如何工作的。
00:03:10所以,是的,我看到那里有一条关于 Google AI 计划中新的基于计算机的配额的评论。我想我读到过一些相关信息。还没有仔细看,所以我不能对此发表评论。
00:03:24哇,那相当大。让我们看看。什么是 Adonis?它是一个后端优先、类型安全的框架。虽然它是后端优先,但我知道它具备全栈能力。
00:03:37你可以用它渲染视图,就像 Laravel 一样,如果你了解那个的话。
00:03:42它为使用 Node.js 和 TypeScript 构建 Web 应用程序提供了编写和维护完整后端的核心构建块,消除了对第三方服务的需求。
00:03:52是的,正如我刚才提到的,就像 Laravel 一样,它不仅仅是一个像 Next.js 那样的全栈框架,Next.js 本质上让你能够在服务器上渲染 React 组件。
00:04:06这是一个粗略的总结,但确实如此,这就是 Next.js 背后的主要理念。
00:04:12他们有路由等等,但 Adonis 和 Laravel 一样,提供了更多功能。
00:04:16它自带身份验证功能,因此你不需要为此使用额外的库。
00:04:21它自带对文件上传、缓存、速率限制等的内置支持。
00:04:26它有自己的 ORM,也就是对象关系映射,我想这就是它的意思。
00:04:33它是它自己对 SQL 数据库的封装等等。
00:04:35所以它非常强大,非常有能力。
00:04:38它的理念确实是,如果你想构建一个全栈应用程序,你只需要 Adonis.js,而不需要太多的其他依赖。
00:04:47当然,特别是在今天这个有各种供应链攻击的时代,拥有有限的依赖集是非常有趣的。
00:04:55显然,如果 Adonis 被攻破,你依然会有麻烦,但你需要担心的额外依赖变少了。
00:05:03嘿,Daki,我快完成你那著名的 React 课程了。
00:05:07你建议在学完纯 TypeScript 或 React 加 TypeScript 之后学什么?
00:05:11是的,React 加 TypeScript 是个好主意。
00:05:12Next.js 也是个好主意。
00:05:14或者你在学完 React 后深入研究 React Native,这样如果你想的话就可以用它构建移动应用。
00:05:20所以这些都是合理的下一步,我想。
00:05:24所以事不宜迟,不用阅读这里所有的文字,让我们直接开始吧。
00:05:28让我们选择我们的路径。
00:05:31你会在这里学习。
00:05:33我其实想开始行动。
00:05:36有一件事,这三种方法。
00:05:40Adonis.js 支持三种构建前端的主要方法。
00:05:44好的,那听起来很重要。
00:05:47每种方法都代表了对视图层的一种不同思考方式。
00:05:52超媒体。
00:05:52超媒体应用程序在服务器上生成完整的 HTML 页面并将其发送到浏览器。
00:05:57你使用模板引擎构建你的界面。
00:06:00Adonis.js 自带它自己的模板引擎,即 Edge。
00:06:05并使用轻量级 JavaScript 库(如 Alpine.js)添加交互性。
00:06:10或者在需要时使用 HTMX。
00:06:12我想我其实想使用这种方法。
00:06:16不过,你也可以使用 Adonis 和 React 作为前端,通过 Inertia.js,
00:06:25我想这是来自 Laravel 世界的,
00:06:28它有点像前端单页应用程序和全栈应用程序后端之间的桥梁。
00:06:32和全栈应用程序的后端。
00:06:38而且,是的,你当然也可以只构建一个 REST API,仅仅是一个后端,如果你想的话。
00:06:46同一个控制器,三种不同的返回方式。
00:06:49好的,让我们看看。
00:06:50我们可以使用 Adonis 自带的路由器注册一个路由。
00:06:56控制器的 post 方法,这就是那里的控制器。
00:07:02在那里,我们可以调用 viewRender 来渲染之前定义的视图。
00:07:06而这种特殊的语法,本质上就是 Adonis 自带的 Edge 模板语言。
00:07:16另一种选择是使用 Inertia,我们也在这里有我们的控制器。
00:07:20但在控制器中,我们仍然在视图上调用 render,并且可以传递一些 props。
00:07:28是的,就是 props。
00:07:30然后我们可以在这里写 JavaScript,我们的 React 代码。
00:07:34据我理解,它会被服务端渲染。
00:07:37基本上,我们不需要在那个 React 组件中获取数据。
00:07:42那一切都会为我们处理好。
00:07:44所以这也很好。
00:07:45但我认为我,嗯,我们会看到我会用哪一个。
00:07:47两者之一,显然我想在这里构建一个小的全栈应用程序。
00:07:50但我们走着瞧。
00:07:51我可能会从 Edge 模板引擎开始,构建一个好的老式多页应用程序,
00:07:57我们在服务端渲染 HTML。
00:07:59因为并不是所有的应用程序都需要客户端 React。
00:08:03我认为理解这一点真的很重要。
00:08:05这取决于你在构建什么。
00:08:07而且不是每个应用程序都需要一个超级反应式、交互式的前端。
00:08:12如果你在构建像博客这样的东西,比如,你很可能不需要那样。
00:08:16但即使是对于许多更复杂的应用程序,你可能也不需要它。
00:08:20让我快速赶上聊天内容。
00:08:22你好,Max。
00:08:23AI 工程课程。
00:08:24也许在某个时候,但现在情况变化太快了,我发布的任何相关课程
00:08:28可能在我发布的那一刻就过时了。
00:08:32所以我不想那样。
00:08:32所以今天不行。
00:08:35我宁愿先创建一些关于软件基础知识之类的课程。
00:08:39但在某个时候,我当然想分享我是如何使用 AI 工作的。
00:08:44但我希望在它不再每周都在变的时候做,尽管它现在确实在变。
00:08:48你好,Max。
00:08:48我知道 Adonis 已经被维护了,我想,超过十年了。
00:08:51鉴于它相对小众的市场,你认为押注它是一项商业风险吗?
00:08:55是的,那是一个非常好的问题。
00:08:57我知道它已经被维护了很长很长时间。
00:09:01但我不知道它已经十年了。
00:09:02但是的,绝对是很长一段时间。
00:09:05而且我也不知道 Adonis 背后的团队有多大。
00:09:09我知道它现在维护得很好。
00:09:13我看到 Adonis 项目的首席维护者、所有者在 X 上有很多活动。
00:09:20显然,对于一个不是全世界都在使用的项目,总是会有一定的风险,可以这么说。
00:09:30话虽如此,因为它已经维护了这么长时间,这证明了首席维护者的奉献精神。
00:09:38但是,是的,显然,像 Next.js 这样由 Vercel 支持的项目,我想,很有可能更有保障能够长期维护下去。
00:09:50当然,你永远不知道。
00:09:52而且 Adonis 当然有 Next.js 不具备的某些优势。
00:09:57但是,是的,这绝对是一个合理的担忧。
00:10:00很高兴你正在直播。
00:10:01我真的很喜欢你对待编程语言、框架的方法,而且 Adonis 看起来很有趣。
00:10:05所以我很好奇我们今天能得到什么。
00:10:07是的,我也很好奇。
00:10:08感谢加入。
00:10:09这应该很有趣。
00:10:10你用什么工具来做区域高亮框?
00:10:12那是一个名为 DemoPro 的应用程序。
00:10:15那是一个只存在于 Mac 上的应用程序。
00:10:17我想我大概是很多年前买它的。
00:10:21它很棒。
00:10:21它给了你所有那些可以在屏幕上画画的东西。
00:10:25是的。
00:10:27我还是不明白为什么它没有被 JavaScript 社区所拥抱。
00:10:30我认为有主见的框架很棒。
00:10:33人们喜欢 Svelte 和 Vue,那为什么不喜欢 Adonis 呢?
00:10:35是的。
00:10:36我也一直对这一点感到好奇。
00:10:37我知道我大概在七、八年前第一次看过 Adonis。
00:10:44当时我也纳闷为什么它没那么受欢迎。
00:10:48而我个人从未抽出时间深入研究它。
00:10:52而且它从来都不是那么受欢迎。
00:10:54所以它从未成为课程主题或类似的东西。
00:10:59但是,是的,我不知道。
00:11:00我真的不知道,因为它看起来不错。
00:11:02但我想当我们开始时,我们会发现它有多好。
00:11:07因此,让我们开始吧。
00:11:09是的。
00:11:10当然,我们需要 Node,创建一个新应用程序,npm create。
00:11:16所以让我复制一下。
00:11:19我已经在这里创建了一个小文件夹。
00:11:21我不使用 npm,而是用 bun,因为它更快而且更安全一些。
00:11:25我可以在这个文件夹中创建它吗?
00:11:27这行得通吗?
00:11:29是的。
00:11:29好的。
00:11:30现在我必须选择。
00:11:31我是想使用超媒体应用吗?
00:11:33也就是模板引擎、React,视图应用。
00:11:38我在这里选择超媒体。
00:11:39它正在下载入门套件,安装依赖项,迁移数据库。
00:11:45所以它似乎给了我一个基本的入门应用程序。
00:11:53命令失败了。
00:11:55Node ace migration run。
00:11:58嗯。
00:11:59这可不是一个好的开始。
00:12:01是因为我使用了 bun 吗?
00:12:04或者是由于我使用了...
00:12:05这也可能是因为我拥有最新版本的 Node.js,Node 26。
00:12:12也许这有问题,因为我试图用 Node 运行它。
00:12:20让我看看。
00:12:21我可以这样运行吗?
00:12:24好的。
00:12:25我得到一个错误,一个新的异常。
00:12:28噢,天哪。
00:12:29有一种趋势,每当我在直播时尝试某些东西,它就会失败。
00:12:35无法在 Adonis bin console TS 中找到模块 startenv。
00:12:45让我检查一下。
00:12:47那是 Node 吗?
00:12:48让我们在这里使用旧版本的 Node。
00:13:01不。
00:13:02现在是不同的错误了。
00:13:08无法定位绑定文件。
00:13:14所以,好的。
00:13:14糟糕。
00:13:15我想尝试的一件事是,既然可能是 Node 版本的问题,我会快速清除该文件夹中的所有内容。
00:13:24现在我将用 NPM 重新创建它,我只希望这次不会被某些供应链攻击所害。
00:13:34我最近对使用 NPM 非常偏执。
00:13:38我更喜欢 BUN 或 PNPM。
00:13:42好的。
00:13:43让我们再试一次。
00:13:44让我们看看那样是否效果更好。
00:13:52我的意思是,在这一点上,我不应该犯太多错误。
00:14:11命令失败了。
00:14:12这真的不是一个好的开始。
00:14:19这是这里已知的问题吗?
00:14:23这是已知的吗?
00:14:27看起来不像。
00:14:36好的。
00:14:37让我们看看。
00:14:38我们可以尝试的一件事是把它喂给 AI。
00:14:43看看它能否解决这个问题。
00:14:46所以,我会启动 Pi,我目前最喜欢的编码代理。
00:14:57试图在这个项目中开始使用 Adonis。
00:15:02运行了。
00:15:03命令是什么?
00:15:04和 NPM create 这个。
00:15:09好的。
00:15:10NPM create。
00:15:13Adonis.js at latest dot。
00:15:17它在尝试运行迁移时失败了。
00:15:22手动运行也失败了。
00:15:24所以,让我们把那个错误日志粘贴进去,看看我们能得到什么。
00:15:29早上好。
00:15:30或者晚上好。
00:15:32从我这边来说是下午好。
00:15:34让我们看看 AI 是否能在这里帮助我们。
00:15:38看看它能不能搞定那个错误信息。
00:15:41Adonis.js 是什么?
00:15:42Adonis.js 是一个 JavaScript 框架。
00:15:44是一个全栈框架。
00:15:47它就像 JavaScript 界的 Laravel。
00:15:50如果你了解 Laravel 的话。
00:15:51所以,它是一个功能完备的框架。
00:15:53不像 Next。
00:15:54不像 Next.js 或 Tanstack Start,它们主要关注路由和服务器渲染。
00:15:59相反,它自带了身份验证功能。
00:16:01它自带了 ORM。
00:16:03这就是它的理念。
00:16:04现在,我只是在初始设置修复方面遇到了一些问题。
00:16:10噢,好吧。
00:16:11所以,并不是...
00:16:14好吧。
00:16:15我明白了。
00:16:16这不是 Adonis 的问题。
00:16:18大概吧。
00:16:19但问题似乎是这个设置命令想要运行一些生命周期脚本。
00:16:28也就是附属于某个依赖项的脚本。
00:16:31因为担心供应链攻击,我设置了禁止运行脚本。
00:16:35据我所知,Bun 默认也不会运行它们。
00:16:38所以,看起来它需要执行某个脚本。
00:16:45所以,AI 临时禁用了这个限制。
00:16:49现在,运行了数据库迁移。
00:16:54好吧。
00:16:57所以,希望初始设置步骤现在没有什么缺失了。
00:17:05我想我们一会儿就能看到。
00:17:07因为我们当然可以尝试运行开发服务器了。
00:17:15那么,我们开始吧。
00:17:19是的,我不想在这里用那个浏览器。
00:17:22看看吧。
00:17:24好吧。
00:17:25所以,屏幕上出现了一些东西。
00:17:31看起来有点奇怪。
00:17:37它本该是这样吗?
00:17:41呵。
00:17:43好吧。
00:17:45总之。
00:17:50那么,现在来看看。
00:17:51我可以注册账户。
00:18:05现在,我登录了。
00:18:07我可以登出。
00:18:08这一切都是开箱即用的。
00:18:12经典问题总是在本不该出现的时候出现。
00:18:15是啊。
00:18:16这真的很经典。
00:18:17我想这已经是我直播中第三次想深入研究某项技术了。
00:18:22然后它就直接失败了。
00:18:23不过,是的。
00:18:23现在正常了。
00:18:25现在看起来已经没有问题了。
00:18:31不太确定这个临时文件夹。
00:18:33那是我的生产数据库存储的地方吗?
00:18:36还是开发数据库?
00:18:37Schema。
00:18:38用户。
00:18:39是的。
00:18:39好吧。
00:18:40它用的是 SQLite,我很喜欢,因为这轻量又简单。
00:18:45我们稍后会探索这里有什么。
00:18:48Udemy 需要添加关注讲师的功能,这样就能在他们有新内容出来时知道。
00:18:53你说的对。
00:18:54我是说,我和其他讲师已经告诉 Udemy 很久了,他们可以添加多少酷炫的功能。
00:19:01但是,唉。
00:19:02我想他们就是不想做。
00:19:05我喜欢你尝试在求助 AI 前自己搞定问题的过程。
00:19:08对我来说,我第一件事就是复制粘贴给 AI。
00:19:11看来我没救了。
00:19:12好吧,正如你所见,我没搞定的时候 AI 搞定了。
00:19:15但我也不想钻进整个错误栈里去,因为我希望直播的内容是关于 Adonis,而不是调试。
00:19:25所以,尤其是在解析长错误消息和进行推理时,AI 真的很擅长。
00:19:31我只是觉得在发给 AI 之前,自己先粗略看一下错误信息是有意义的。
00:19:38因为,第一,有时候它很容易解决。
00:19:42可能只是端口占用之类的问题。
00:19:45虽然不是这次的情况,但其他问题经常这样。
00:19:47第二,至少能对发生了什么、哪里出错了之类的有个了解,这总不是坏事。
00:19:58总之,让我们看看这里有什么。
00:20:01在这个项目中,显然有一个 package.json 文件。
00:20:08有一些映射,基本就是构成这个项目的那些文件。
00:20:14然后我们从 Adonis 那里得到一些依赖项。
00:20:19什么是 Vine?
00:20:24一个验证库,好吧。
00:20:25有验证库,better-sqlite3,edge.js,据我所知那是模板语言。那什么是 Luxon?
00:20:34什么是 Luxon?
00:20:39那是什么?
00:20:41一个用于处理日期和时间的库。
00:20:44好吧,我们有这些,然后还有一些,断言库在哪,测试运行器类型,Alpine。
00:20:54Alpine 本质上是一个用于添加轻量级客户端 JavaScript 代码的库,这样你就不用写原生 JavaScript,也不需要像 React 这样的大型库。
00:21:05好的,Adonis RC.ts 看起来是配置文件,有实验性标志、命令,以及要注册的 ace 命令列表。
00:21:15是的,我上次用 Laravel 已经是很久以前了。
00:21:20我记得在 2014、2015 和 2016 年左右我做了很多 Laravel 开发,它有很多功能,就像现在的 Adonis 一样,它也允许你注册自己的命令。
00:21:38在 Laravel 中我想那叫 artisan 命令,而这里看起来叫 ace 命令,你需要用 Node 执行它。
00:21:49所以这是 Adonis 提供的一个工具,你可以注册自己的子命令,就像我理解的那样,这些是内置命令,你基本上可以导入自己的文件。
00:22:05比如 my-file.ts 之类的,可以用来注册你自己的自定义命令,这样你就有自己的工具命令了。
00:22:15关于要导入和注册的服务提供者,我的理解是这涉及到依赖注入,以及注册各种在 Web 服务器启动时随之启动的服务。
00:22:26那么这里有什么?
00:22:28那么这里有什么?
00:22:28我们有数据库提供者、Auth 提供者,所以我们有针对不同功能的提供者。
00:22:34预加载,好的,所以本质上这就是我们的配置文件,明白了。
00:22:40还有什么?
00:22:41我们有一个 env 文件,在里面配置端口等等,日志级别,还有一个 app key,用于...
00:22:50我不知道,我不知道它是干嘛用的,是用来进行哈希密码或者之类的吗,我不知道。
00:23:01会话驱动 cookie,好的,所以我们使用 cookie 进行身份验证,获取我们的数据库,应该就是这样。
00:23:12被 git 忽略。
00:23:14Temp,是的。
00:23:16Tests,我们有单元测试设置在这里。
00:23:23Resources,是的,这就像 Laravel 一样,我想。
00:23:27在 Laravel 中,我们也有一个 resources 文件夹,里面基本上存放着客户端资源,这里有我们的客户端 CSS 代码、使用 Alpine 的客户端 JavaScript 代码。
00:23:40还有 views,也就是前端页面,或者是将被渲染成页面的模板,看来所有组件都在这里。
00:23:49但我还不知道这个模板系统是怎么工作的。
00:23:53现在,让我快速看看能不能安装一些扩展,且不被这个 edge 模板语言给搞个供应链攻击。
00:24:02Edge 模板支持,在这里,官方的,由 Adonis.js 提供的 Edge 模板语法高亮器。
00:24:09安装它。
00:24:12噢天哪,别黑我。
00:24:16好吧,看起来好多了。
00:24:18现在有语法高亮了。
00:24:20所以,模板语言背后的想法是,在过去,使用模板语言在其他 JavaScript 框架中也相当普遍。
00:24:32而其真正想法在于在服务器上渲染你的 HTML 页面,然后将完成的 HTML 代码发送到客户端。
00:24:39但为了让它更方便、更动态,你会从不同的模板组合你的 HTML 文件,并在这些模板中有占位符,可以用动态值来替换。
00:24:51而我们这里所拥有的,看起来是,我们说,好的,这个主页,我们位于页面文件夹中,这个主页应该基于一个布局。
00:25:02我会假设你可以定义不同的布局,但如果你不在这里指定任何内容,它会使用一个在某处注册的默认布局。
00:25:09然后这一部分会被注入到该布局中。
00:25:12所以,在那个布局中,我们可能有占位符。
00:25:14那么,让我们看看,布局定义在哪,组件布局在这里。
00:25:20所以,这是一个布局文件。
00:25:22所以,这里我们看到了通用的 HTML 骨架。
00:25:24我们还看到在 body 中,有这个 main 插槽。
00:25:29所以,这看起来是 Edge 模板语言定义插槽的语法,用于渲染实际页面内容。
00:25:39所以,再次申明,我不知道那种语言是怎么工作的。
00:25:42我只是试图通过阅读来理解它。
00:25:44所以,看起来也许那是一个我们可以分配的名字。
00:25:47也许是一个保留名。
00:25:49我不知道。
00:25:50而且它是异步的。
00:25:51这很有趣。
00:25:52我们使用了 await。
00:25:53所以,直到这里被渲染出来,我们才完成布局的渲染。
00:25:57插槽 main。
00:25:59所以,那看起来确实是一个保留名。
00:26:01除此之外,我们还有 partials 的概念,我从 Laravel 中也了解这个,这也很典型,所以你不止有一个布局,有一个用于主内容的插槽,还有其他组件,本质上,你可以把它们带入布局中,也可能带入其他页面。
00:26:18所以,这里我们有页眉的 partial,闪现提示的 partial,也许是你想在屏幕上显示的小型额外警示信息。
00:26:26例如,这里的页眉 partial 可以在 partial 文件夹的 header 中找到。
00:26:33在里面,我们现在有了页眉。
00:26:35在里面,我们有更多的模板语法来渲染一个链接。
00:26:40链接是这样渲染的,因为它们也是动态的。
00:26:43如果它们是内部链接,则取决于你注册的路由。
00:26:47所以,这里我们设置了一个通往主页路由的链接,文本为“主页”。
00:26:52我们可以看到那个。
00:26:54在哪能看到?
00:26:55我们能在底部看到吗?
00:26:58或者,噢,没有。
00:26:59是这个。
00:26:59这是一个 SVG。
00:27:01所以,我想那本质上就是这个 SVG。
00:27:04它有一个名为“主页”的回退名称。
00:27:06如果我们点击它,就会被带到主页路由。
00:27:13好的,这里还有一些条件渲染,取决于我们是否有已登录的用户。
00:27:19说得通。
00:27:20那仍然是在页眉里。
00:27:23好的。
00:27:23然后这里,例如,那个 @vite 东西。
00:27:26所以,我们似乎在使用 Vite 来处理前端资产,打包和管理前端 CSS 和 JavaScript 资产。
00:27:35并且我们正在将我假设是优化打包过的 app.js 和 app.css 文件注入到这里。
00:27:45或者更具体的,我们在 resources 文件夹下的 JS 和 CSS 里找到了它们。
00:27:49所以,就是这个 JavaScript 和 CSS 文件。
00:27:53所以,我不知道它们是否被处理了。
00:27:57明白了。
00:27:58而且当启动主开发服务器时,Vite 服务器会自动启动,我假设是这样。
00:28:05所以,好的。
00:28:06说得通。
00:28:06这里我不完全理解。
00:28:08这个堆栈的东西可能是一些调试用的。
00:28:11我不知道。
00:28:12然后我们有更多组件在这里。
00:28:15而且,是的。
00:28:16我得深入研究一下。
00:28:17到底发生了什么。
00:28:19但我们到时候会做的。
00:28:21让我回到聊天。
00:28:22有一些问题。
00:28:24Max 的 LLM 模型运行和使用起来变得越来越贵,不再有补贴了。
00:28:28开源或自托管模型会变得更重要吗?
00:28:31因此,开发者是否应该为了这种转变学习 DevOps?
00:28:35所以,我认为未来开源模型会变得越来越重要。
00:28:42但是,当然,如果你想自己运行它们,那也会花钱。
00:28:45因为要么你需要一台配置相当强劲的本地机器。
00:28:48比如一台 1 万美元的 Mac Studio 之类的。
00:28:52或者你需要再次租用它。
00:28:53所以,这总是会花费你一些钱。
00:28:56但当然,根据前沿模型变得有多贵,开源模型可能是很有意思的替代方案。
00:29:03取决于你想做什么,目前并没有太多超级强大的开源模型。
00:29:10但我肯定我们会达到的。
00:29:12不过我不觉得你应该为此学习 DevOps。
00:29:16如果你对它感兴趣,你应该学习它。
00:29:19但部署和运行你自己的开源模型是你可以做的事情,如果你有硬件的话,
00:29:25或者如果你租了一个 VPS,只需做一次或者几次。
00:29:28但你只需要学习如何一次性把它弄对。
00:29:32我不觉得你需要大范围地学习很多关于 DevOps 的东西。
00:29:36嘿,谢谢你选修我的 Flutter 课程。
00:29:41感谢 Don Solid 的热心推荐。
00:29:47还有,请问我为什么要用这个工具,而不直接使用 AI 服务呢?
00:29:51我不确定你指的是哪个工具。
00:29:54你们有没有可能把新课程发布在 Udemy 上?
00:29:57你们还在做 Udemy 课程吗?
00:30:01偶尔会做。
00:30:02但 Udemy 目前的发展前景并不乐观。
00:30:07所以,还得看看 Coursera 的收购案后续会如何发展等等。
00:30:14关于内容安全策略 (CSP),我的意思是,我想你应该可以进入那个布局页面,
00:30:22然后在 head 部分添加任何你需要添加的内容。
00:30:25毕竟归根结底它只是 HTML,所以你可以直接加在那里。
00:30:32所以,这样应该可行。
00:30:34当然,我本人才刚刚开始接触 Adonis。
00:30:42好的,这就解决了。
00:30:44我们先跳过或者说暂时搁置前端和资源文件夹。
00:30:48让我们来看看 start 文件夹。
00:30:50那应该是服务器端的部分,因为我们要探索的... 不,我们还没探索完其他东西。
00:30:56还有更多内容。
00:30:57我们有数据库文件夹,里面包含了迁移文件。
00:31:00所以这里的思路和许多其他框架一样,我们通过迁移文件来定义数据库表,
00:31:08这些文件似乎就是这种格式。
00:31:12而且我猜应该也有一些命令可以让我们自动创建这些迁移文件。
00:31:17然后我们再运行这些迁移,将其应用到数据库中,基本上就是规划好表及其模式。
00:31:24所以我们有 schema.ts 文件。
00:31:28好的,看来我们是把数据库表设置成了一个类。
00:31:33然后我们可能会通过特定的命令来以编程方式创建迁移。
00:31:40Config,噢,这里有好多东西需要配置。
00:31:43好了,我们可以在这里配置一大堆东西。
00:31:50好的,我想等需要的时候我们再研究它。
00:31:53Bin,server.ts,HTTP 服务器,入口点。
00:31:57server.ts 文件是启动 Adonis.js HTTP 服务器的入口点。
00:32:02你可以直接运行这个文件,或者使用 serve 命令。
00:32:05所以,当我们用 serve 命令启动开发服务器时,它就是被执行了。
00:32:13里面都在做些什么呢?
00:32:18Importer,igniter。
00:32:23基本上就是启动服务器,开始监听端口。
00:32:27肯定还在某处注册了路由。
00:32:34console 是引导 Adonis.js 命令行框架的入口点。
00:32:38所以,我想这是在我们注册自定义命令或执行某些内置命令时用的,就是用 ace 命令。
00:32:45还有 app,好的。
00:32:46所以在这里,我们注册启动相关的配置。
00:32:49start 文件夹似乎很有意思。
00:32:51比如在 start 文件夹里,我们有 routes.ts 文件。
00:32:54在这里,我们明确注册了应用程序的路由。
00:33:00就像那样,针对根路径。
00:33:02也就是域名下的空路径。
00:33:04我们要渲染主页。
00:33:06所以在这里,我们指向 resources/views 文件夹中的路径。
00:33:12也就是在 resources/views 中。
00:33:15所以如果我们执行类似 render pages/home 的操作,它就会指向那里的 pages 文件夹。
00:33:20然后在那里面渲染 home。
00:33:23基本上就是这么回事。
00:33:28我们还可以对路由进行分组。
00:33:31那么为什么要分组呢?
00:33:33是为了应用一些共享的中间件,对吧?
00:33:35比如 guest 中间件。
00:33:38不管它是做什么的。
00:33:40然后我们在这里获取注册和登录路由。
00:33:44是的,大概这就是某种中间件,只有未认证用户才能访问这些路由。
00:33:49另一方面,我们还有 auth 中间件来保护应该只有已认证用户才能访问的路由。
00:33:56比如注销路由,只有在执行注销时才有意义。
00:34:00每个路由要么像这样注册,直接渲染某些东西,或者——我知道 Laravel 里也是这样,可能更符合官方哲学——像这样把控制器和特定的控制器方法连接到路由。
00:34:21在这里,对于注册功能,我们有两个路由,一个 GET 和一个 POST 路由,它们在同一个控制器文件中,但我们指向了不同的控制器,分别是 create 和 store 控制器。
00:34:35如果我们看一下那个文件,我们的控制器在 controllers.ts 文件中,它们位于 adonis.js 服务器文件夹里。
00:34:47等等,那是动态创建的,不是吗?
00:34:51是我们创建的吗?
00:34:53不是我们创建的吗?
00:34:55不,我们是在这里注册我们的控制器。
00:34:59我猜这部分是动态生成的。
00:35:01所以,这里是 new account controller。
00:35:04我们有我们的 NewAccountController 类。
00:35:08在里面,我们有 create 方法和 store 方法。
00:35:12最后,这些名字就对应了这些逻辑。
00:35:15再说一次,我们要看看,但我猜这是某种方式动态或自动创建的。
00:35:21然后在 create 中(针对 GET 路由),我们只是渲染了另一个视图。
00:35:25所以理论上,我们当然可以不用这个,而是可以... 我猜也可以直接用 get。
00:35:41是的,我们可以,不,我们不能,我们不能直接在这里渲染吗?
00:35:46必须使用控制器吗?
00:35:50看起来是这样,好吧。
00:35:51但最后,我们渲染的方式正如我们这里所做的那样。
00:35:56而对于这里的 POST 路由,我们正在使用内置验证器验证用户输入。
00:36:04然后我们使用用户模型创建一个新的用户实例,这一点我稍后会讲到。
00:36:11然后我们为 Web 认证该用户。
00:36:15所以,我猜是基于会话和 Cookie 的认证。
00:36:18然后我们再次重定向到主页路由。
00:36:21所以,让我们看看。
00:36:24我们有 models 文件夹。
00:36:26在里面定义了用户模型。
00:36:28我之前提到过我们有那些数据库表,有那些 Schema。
00:36:40也许我们只需要定义模型,剩下的部分会自动生成,但我们会看到的。
00:36:47所以归根结底,在这里我们定义了想要在应用程序中拥有用户。
00:36:53然后我们扩展了一些来自 Adonis 的功能。
00:36:57然后我们在这里有一个小的 getter,即 initials。
00:37:03好的,那只是一个简单的辅助函数,通过组合姓和名来获取用户的全名。
00:37:10仅仅是为了展示如何像这样创建计算值。
00:37:15好的,明白了。
00:37:17用户的验证器。
00:37:19一个小小的验证文件,我们使用了这个叫 Vine 的库(我之前不知道),基本上用于确保我们获得一个具有一定最大长度的非空电子邮件地址。
00:37:31不好意思。
00:37:36然后我们似乎可以创建一些可重用的验证器,可以在所有我们想要验证注册的地方使用,在那里我们强制执行某些值。
00:37:49好的,auth 中间件只是一个带有 handle 方法的类,我们在里面检查一些东西,如果想允许用户访问他们要去的目标路由,就返回 next。
00:38:07我猜如果认证失败,这会抛出一个错误,从而阻止下一步的导航。
00:38:14我猜 guest 中间件的作用正好相反。
00:38:21是的,我们检查用户是否已认证,如果他们已认证,就重定向。
00:38:28并且只有当他们未经过认证时,我们才允许他们访问他们要去的地方。
00:38:32好的。
00:38:33异常处理允许我们定义自己的错误,我们自己的异常。
00:38:38好的。
00:38:40好的。
00:38:42所以。
00:38:45你计划在不久的将来发布什么新课程?
00:38:52也许是关于 BUN 的。
00:38:55我还在琢磨这是否是一门我满意的课程。
00:38:58所以我已经录制了一些东西,但我想检查一下我对质量等等是否满意,以及我是否满意这个方向。
00:39:06一些软件工程基础、系统设计是我今年想要构建的课程。
00:39:12关于代理 AI (Agentic AI),我知道这可能会卖得很好,但我之前已经回答过了。
00:39:20问题在于我非常想创作一门课程,分享我是如何进行代理工程以及如何与 AI 协作的。
00:39:26问题只是它改变得太频繁了。
00:39:30所以我认为它现在不会太有用。
00:39:33一旦我觉得它更稳定一些,我很乐意创建一门这样的代理工程、AI 工程或任何你想称呼它的课程。
00:39:42看起来像 puck.js。
00:39:44是的。
00:39:45为什么有人会在 2026 年使用它?
00:39:48我不认为有人会使用 Vue。
00:39:51现在全是单页应用 (SPA)。
00:39:53我不赞同这一点。
00:39:56我想我们已经非常习惯单页应用和到处都是 React 了。
00:40:01React 当然很棒。
00:40:02我的意思是,AI 喜欢 React 也是事实。
00:40:07但总的来说,Adonis 是相当小众的。
00:40:10但我认为非得在任何地方都使用 React 这种假设是错误的。
00:40:17仅仅能够在服务器上渲染模板通常就足够了。
00:40:23通常就足够了。
00:40:24而且添加 React 通常只会增加复杂性,增加包体积。
00:40:30想想我们过去几周遇到的那些安全漏洞。
00:40:35所以,不因为习惯而引入你不需要的东西是有价值的。
00:40:41我们太习惯于在任何地方都使用 React,以至于我们把它当作理所当然,当作我们必须使用的标准选项。
00:40:47但我并不认为情况是这样。
00:40:49好吧,我确实同意。
00:40:50这看起来确实有点过时了。
00:40:53就像我说的,我很多年前就是这样写页面的。
00:41:00但我仍然不会说它在今天就是错的。
00:41:03它确实很不寻常,看起来也不像原生的,或者让人觉得非常有意义。
00:41:12但我会说它有其道理。
00:41:14好吧,所有这些话,正如我之前所说,Adonis 确实支持不同的视图渲染方式。
00:41:23它确实支持 React 作为前端,然后使用 Inertia.js 作为桥梁。
00:41:29所以你完全可以构建 Adonis.js 全栈应用,而不必采用这种基于视图的方法。
00:41:38但我首先想尝试一下,因为我想看看它看起来是什么样子的。
00:41:43看起来有点像 Hano。
00:41:45是的,我觉得 Hano 更精简,对吧?
00:41:48所以 Hano 没有内置所有这些功能,比如身份验证和 ORM 等等。
00:41:56你是怎么发现 Adonis.js 的?
00:42:00我觉得它很有趣,但我之前完全没用过。
00:42:03所以我今天只是在探索它。
00:42:05我才刚刚开始接触它。
00:42:07我喜欢在一个框架中包罗万象的想法,特别是在这些日子里,
00:42:13各种供应链攻击频发。
00:42:16而且我认为在 JavaScript 生态系统中,我们往往会去寻找过于复杂的解决方案。
00:42:21我们拼凑了几十个库,除了供应链攻击之外,
00:42:26还有个问题就是并不是所有这些库都维护得很好。
00:42:31所以拥有一个“内置电池”的框架绝对有其价值,我是这么认为的。
00:42:39你对那些有基础知识的计算机科学系学生有什么学习编程的建议?
00:42:44下一步该做什么?
00:42:45构建项目还是探索 AI?
00:42:46我会利用 AI 来进行学习。
00:42:48我也会在 AI 的帮助下构建项目,但我不会只依赖 AI,因为很明显
00:42:53那样你什么都学不到。
00:42:54我会试着学习软件工程的基础知识。
00:42:57我会尝试亲手写一些代码。
00:43:01我绝对会复核并真正理解 AI 生成的所有代码。
00:43:05我会批判性地质疑它。
00:43:07也许还可以利用 AI 来讨论代码。
00:43:10老实说,我本人也还在摸索如何最好地教,当然,
00:43:15在当今时代,如何利用 AI 来学习。
00:43:20如果你问 AI,今天应该如何构建项目以达到学习目的?
00:43:27是的。
00:43:27所以我会真正把 AI 当作一个陪练。
00:43:32好的。
00:43:32让我们看看。
00:43:34现在,我想看看其他替代方案,既然我们刚聊到这个。
00:43:38然后我们再进入代码编辑环节。
00:43:40但我现在想看下替代方案。
00:43:42如果我用了...
00:43:47让我重新创建这个。
00:43:49想从头开始。
00:43:51让我重新创建这个。
00:43:52停止服务器。
00:43:53然后使用 React Kit 的替代方法。
00:44:01现在,它可能会再次崩溃。
00:44:08所以,让我快速检查一下。
00:44:15它运行了哪个命令才让它工作的?
00:44:19Permanent。
00:44:20我不需要 Permanent。
00:44:27所以那个应该就可以了。
00:44:32让我们看看。
00:44:36让我们看看。
00:44:37糟糕。
00:44:38我不想那样做。
00:44:39一个点就够了。
00:44:42好的。
00:44:44看看这行不行。
00:44:55把你裤子都吓尿了。
00:44:57不知道这是好事还是坏事。
00:45:09我有没有从零开始设计我的 Flutter 课程?
00:45:11有的。
00:45:12全是我自己的心血。
00:45:14那么,现在让我们重新启动它。
00:45:21看看看起来是不是一样。
00:45:24没错。
00:45:25同样的应用程序。
00:45:26但现在,当然,文件结构应该有所不同了。
00:45:29让我们看看。
00:45:32App 文件夹。
00:45:36现在我有一个转换器了。
00:45:38但那个,我猜那只是后端的东西。
00:45:42不对。
00:45:42我想那是用来将数据从服务器传输到客户端的。
00:45:46也就是传输到前端,看看。
00:45:48Bin、config、database、inertia。
00:45:53好的。
00:45:53我猜 Inertia 现在是前端文件夹。
00:45:56CSS。
00:45:57没错。
00:45:57好的。
00:45:58那么,我们这里有什么?
00:45:59有一个 app.tsx 文件。
00:46:11我不知道那是什么,但我猜它是用来实现前端水合作用并将它与后端连接起来的。
00:46:18创建 Inertia 应用程序。
00:46:20似乎是设置了页面标题。
00:46:22App。
00:46:23好的。
00:46:24脚本的配置文件在哪里?它向我显示一个错误,但那只是因为它已弃用。
00:46:29没关系。
00:46:32服务器端渲染 TSX。
00:46:34所以,那是用于服务器端渲染入口点,以及客户端入口点。
00:46:39好的。
00:46:39我不知道 Inertia 是如何工作的,也不知道这个两小时的东西是什么。
00:46:46这儿有我的主页。
00:46:47所以,现在它是一个普通的 React 组件。
00:46:51没什么特别的。
00:46:52我们有布局或类似的东西吗?
00:46:53是的,我们有一个布局。
00:46:54Default TSX。
00:46:55这是在哪里设置的?
00:46:56是在应用程序中设置的吗?
00:46:59解析页面组件。
00:47:00查看 pages 文件夹。
00:47:03哪里有布局,布局,布局,布局。
00:47:05布局已被导入。
00:47:07好的。
00:47:08原来是这样工作的。
00:47:10我们的布局在这里。
00:47:12然后是默认布局。
00:47:16好的。
00:47:16所以,我们有,喔,useEffect。
00:47:19那是被禁止的。
00:47:24好的。
00:47:25没错。
00:47:26所以,那真的只是 React。
00:47:27就像我们知道的那样,Link 组件来自 Inertia。
00:47:30或者来自 Adonis.js 及其 Inertia 包。
00:47:35好的。
00:47:37据我所知,让我们看看。
00:47:47没错。
00:47:47Children 显然是用于……
00:47:51好的。
00:47:52好的。
00:47:53好的。
00:47:54有趣。
00:47:54Children,当然,就像总是 React 那样,是标签之间的组件。
00:48:05但 children 也有一个带有 user 属性的 props 对象。
00:48:13我假设它是在我们的控制器中的某个地方填充的。
00:48:20首先,新会话,注册的新账户控制器或 Inertia 渲染。
00:48:29但在那里我们没有传递任何 props。
00:48:32会话控制器。
00:48:35这是在哪里填充的?
00:48:36因为我们这里有这个转换器东西,我原以为它是用来将服务器端数据转换为客户端数据的。
00:48:46我不确定。
00:48:50返回这个 pick,这个资源,不管是什么。
00:48:56也许它也只是用来从数据库中检索数据。
00:48:59我不确定。
00:49:01好的。
00:49:02好了,我们设置好了。
00:49:03让我们继续官方指南。
00:49:05我只是非常喜欢先自己深入研究代码库。
00:49:08然后在跟随指南之前看一看,尝试去理解它。
00:49:12因为我觉得像这样学到的东西更多。
00:49:16而且现在谁还读代码呢,对吧?
00:49:18所以,为什么不利用直播来做呢?
00:49:22开个玩笑。
00:49:23我确实会读代码。
00:49:26文件夹结构。
00:49:28好的。
00:49:28我刚才探索了文件夹结构。
00:49:31App 文件夹。
00:49:32App 目录组织了你应用程序领域逻辑的代码。
00:49:36例如,控制器、模型、邮件。
00:49:39所以,是的,就像 Laravel 一样,它也有助于发送邮件,这当然可以非常方便。
00:49:44Bin 目录包含用于启动你的 Adonis.js 应用程序、控制台、服务器、TS 的入口点文件。
00:49:50你通常不需要修改这些文件,除非你想自定义应用程序的启动方式。
00:49:56好的。
00:49:56Config,所有应用程序和第三方配置文件都存放在 config 目录中。
00:50:01你也可以在此目录中存储本地于你应用程序的配置。
00:50:05是的,在 config 文件夹中,我们已经看到有像 database 这样的东西,我们使用 SQLite。
00:50:13我假设你可能也可以连接到 Postgres 数据库或类似的东西。
00:50:20我们可以选择客户端。
00:50:21所以,也许我们可以在这里使用像 Bun 的 SQLite 客户端。
00:50:24我不知道。
00:50:25定义数据库路径,临时路径。
00:50:28所以,在那个 temp 文件夹里,迁移,我们还有什么?
00:50:34Config,Inertia,服务器端渲染,切换服务器端渲染模式。
00:50:39是的,我想要服务器端渲染,所以让我们把它改为 true。
00:50:47日志记录器,是的。
00:50:48所以,这很有道理。
00:50:50我只想快速检查一下。
00:50:52如果我把它设置为 false,我的假设是,如果我查看这里的页面源代码,会有一个带有传给它的数据的 div app。
00:51:06是的,但是主要的 HTML 内容丢失了,因为它是客户端渲染的。
00:51:10我们正在导入一些脚本,然后执行客户端渲染。
00:51:15这对于搜索引擎优化来说不太好。
00:51:18所以,如果我把它设置为 true,现在我们可以看到里面多得多了,对吧?
00:51:26那是实际的,全是内联的,所以有点难读,但实际的 HTML 内容就在那里。
00:51:31所以,我会让它保持设置为 true。
00:51:34好的,数据库。
00:51:35数据库目录包含与数据库层相关的工件。
00:51:39默认情况下,它通过 Lucid ORM(它自己的 ORM)来处理 JS。
00:51:43ORM 背后的想法是,你将数据库表本质上表示为模型,也就是你代码中的类。
00:51:51然后为你生成底层的架构和迁移。
00:51:57而且,是的,这是我第一次探索这个框架,所以我现在只是在弄清楚它是如何工作的。
00:52:03切换数据库不需要重新组织这个文件夹,迁移、版本、架构更改、种子,如果你想拥有种子数据,比如一些初始管理员用户之类的。
00:52:15好的,提供程序。
00:52:16Providers 目录用于存储你的应用程序使用的服务提供程序。
00:52:20什么是服务提供程序?
00:52:22服务提供程序。
00:52:22本指南涵盖了服务提供程序。
00:52:24好吧,我可能不会完全读完。
00:52:27服务提供程序是具有生命周期钩子的 JavaScript 类,在应用程序启动和关闭期间的特定时刻执行。
00:52:35这允许你向控制反转容器注册绑定,使用宏扩展框架类,在精确时刻执行初始化,并在关闭期间清理资源。
00:52:53所以,这就是我们拥有一些内置提供程序的地方。
00:52:58它在哪里?
00:53:02我之前看到了。
00:53:04内置提供程序在哪里?
00:53:09提供程序。
00:53:09这里是 API 提供程序。
00:53:12用于 API 响应的自定义序列化程序。
00:53:17好的,所以在这里我们有一个用于序列化数据的提供程序,看起来是这样。
00:53:22我假设还有更多内置的提供程序,有助于身份验证,因此会注册一堆默认中间件之类的东西。
00:53:30我会这么假设。
00:53:33不要保存这个。
00:53:35好的,public,public 目录包含原始静态资产。
00:53:39我忽略了这个文件夹吗?
00:53:40Public。
00:53:46我没有 public 文件夹。
00:53:48但我猜我没有任何公共资产。
00:53:51我大概可以添加一个 public 文件夹来存放不会被优化的原始资产。
00:54:05我在 Adonis 中几乎看不到工作机会。
00:54:07那为什么还要探索那些没有需求的东西呢?
00:54:10只是为了好玩?
00:54:10要是你在找工作,那么 Adonis.js……
00:54:15喔,这是一个如此困难的问题。
00:54:17我是说,是的,如果你只是在找工作,Adonis.js 不会帮到你。
00:54:22但是,首先,探索替代方案总是会拓宽你的视野。
00:54:27你可能会在这里学到新的概念,它们也适用于具有其他技术栈的其他应用程序。
00:54:33为了好玩是一个非常正当的理由。
00:54:36当然,你可能,如果你不是在找工作,如果你想,比如,构建你自己的 SaaS,你自己的业务,等等,
00:54:48那么,这当然可能是一个合理的选择。
00:54:52如果你在找工作,当然取决于你在哪里,它可能会非常小众。
00:54:58而且,老实说,它太小众了,很难找到工作。
00:55:01但是,当然,如果你身处一个有一些此类工作机会的地方,你的竞争就不会那么激烈。
00:55:07但这当然是非常理论化的。
00:55:08但是,是的,对我来说,现在只是为了好玩。
00:55:10我只是想探索它,因为我八年前就读过关于它的文章了。
00:55:15而且我终于想理解它是如何工作的了。
00:55:19资源目录存储 Edge 模板和未编译的前端资产,例如 Inertia 应用程序中的 CSS 和 JavaScript 文件。
00:55:30我有资源吗?
00:55:32我有。
00:55:33Inertia 布局。
00:55:35喔。
00:55:35所以那就像是这个 React 应用程序渲染进入的原始 HTML 骨架。
00:55:41所以我们可以在这里设置我们的内容安全策略。
00:55:44我想我们可以在这里引入一些静态资产。
00:55:48除了我们已经拥有的内容之外,注册一些元数据。
00:55:51好的。
00:55:52Inertia,inertia 目录仅存在于使用 Inertia 入门套件的项目中。
00:55:56很有道理。
00:55:57它代表包含前端源代码的子应用程序。
00:56:01你可以自由创建额外的文件夹,例如组件、布局、工具来组织。
00:56:06所以本质上,你是在那里构建你的视图或你的 React 应用程序。
00:56:09前端和后端之间有明确的界限。
00:56:11Adonis.js 在后端和前端之间保持了清晰的界限。
00:56:14你不应该将后端代码导入到你的前端应用程序中,以免暴露任何东西,或者因为它会导致崩溃,因为显然后端 API 或服务器端 Node.js API 不会在浏览器中的客户端上运行。
00:56:30实际上,你的前端通过 HTTP 请求与后端通信,并接收纯 JSON 数据。
00:56:35Adonis.js 鼓励你明确地模拟这种现实。
00:56:39数据通过 API 响应获取并转换。
00:56:43我很好奇,这很有趣,当我们使用 Inertia 时,我们到底是如何通信的?
00:56:49所以,比方说我们正在登录。
00:56:52所以我们这里有一个表单。
00:56:54没错。
00:56:54这很有趣。
00:56:56这是一个来自 Adonis.js 框架的组件。
00:57:01它需要一个路由,而且我想我们不需要像在普通 React 应用中那样向我们自己构建的 REST API 发送 HTTP 请求,对吧?
00:57:12相反,我们构建它的方式就像旧时代的多页面应用一样,表单直接连接到某个路由。
00:57:19当我提交那个表单时,我想在后台,Inertia 和 Adonis 会创建该请求并将其发送到那个路由。
00:57:28一切基本上都会为我们处理好。
00:57:31所以我们不需要自己发送请求、等待响应或管理某些状态。
00:57:37我们在这里不需要做这些。
00:57:42是啊。
00:57:44你在学习新框架时的思维过程是什么样的?
00:57:48你会把它与其他框架进行比较吗?
00:57:51还是会把它当作一个全新的事物来对待?
00:57:53我觉得你会自动地总是把它和你已经知道的东西进行一些比较。
00:57:57但当然,Adonis 和 Next.js 等框架非常不同,所以我不会过度比较它。
00:58:03我最初做的主要比较是理解或需要理解它是一种完全不同的哲学。
00:58:10所以它不仅仅是关于路由和渲染组件,相反,它是关于拥有这种功能齐全、配置完善的框架。
00:58:18所以我并没有过多地比较它。
00:58:19我把它看作一个相当新的事物。
00:58:21我确实把它和 Laravel 做了一些比较,因为它是 JavaScript 版的 Laravel。
00:58:25谢谢 Bleem,发表了非常好的评论。
00:58:29很高兴你喜欢这些内容。
00:58:32好的。
00:58:34共享类型。
00:58:35前端仍然可以依赖 Adonis.js 自动生成的共享 TypeScript 类型。
00:58:40这些存储在 .adonis.js 客户端目录中,包含路由、属性等的类型定义,以便我们可以利用 TypeScript。
00:58:48Start 目录。
00:58:49Start 目录包含了你在应用程序启动生命周期中想要导入的文件。
00:58:55例如,注册路由和定义事件监听器的文件应该放在这个目录中。
00:59:00Adonis.js 不会自动导入 start 目录下的文件。
00:59:03它只是作为一种约定来归类相似的文件。
00:59:06好的。
00:59:07所以在那个 start 文件夹中,我有 kernel TS 文件。
00:59:12哦,是的,我已经看到了。
00:59:14对,对,对,对,对,对。
00:59:16我想我们可以在这里注册新的中间件,我们会看看指南是否要求我们这样做。
00:59:24我们有一个测试文件夹。
00:59:25我们有一个用于存放临时文件(如数据库)的 temp 文件夹。
00:59:28Ace.js 是执行 ace 命令的入口点。
00:59:33我们这里有那个吗?
00:59:35Ace。
00:59:36Ace,是的。
00:59:37好的。
00:59:38不要修改这个文件。
00:59:39好的。
00:59:41是的。
00:59:44是项目清单吗?
00:59:46使用 lint 包。
00:59:48使用 config。
00:59:49好的,明白了。
00:59:51开发环境设置。
00:59:54Adonis 应用程序自带一个配置完善的开发环境。
00:59:57所以,是的,代码编辑器。
01:00:01是的,我已经安装了 Edge 扩展,但如果我使用 Inertia 和 React,我就不需要它了。
01:00:09TypeScript。
01:00:10据我了解,这一切应该都已配置好了。
01:00:12所以我真的不想在这上面浪费时间。
01:00:14配置与环境。
01:00:18Adonis 中的配置组织为三个不同的系统,每个系统都有特定的用途。
01:00:23配置文件包含 config 文件夹中的应用程序设置。
01:00:28 .env 文件中的环境变量保存运行时机密和在不同环境间会发生变化的值。
01:00:33很有道理。
01:00:34顺便说一下,关于机密信息,特别是考虑到所有这些供应链攻击,我的建议是不要把它们存在 .env 文件中。
01:00:41我可能会为了教程之类的目的这样做,因为很快,事后无论如何我都会把它删除。
01:00:47但对于你持续使用的机密,我会使用像 Infisical 这样的服务。
01:00:53我知道他们没有给我付费。
01:00:54你也可以使用 Doppler 或类似的任何服务。
01:00:57这些都是云服务。
01:00:58使用 Infisical,你可以免费上手。
01:01:01是啊。
01:01:02然后你可以把你的机密存储在云端,并通过 CLI 拉取。
01:01:06如果你的机器被入侵,你的机密也不会被提取,或者至少不会那么容易被提取。
01:01:14所以这只是一个小小的侧记。
01:01:17Adonis RC TS 文件负责配置框架本身。
01:01:20好的。
01:01:20所以配置文件用于应用程序,而那个文件用于框架本身。
01:01:26所以在这里我们大概可以更改默认查找的文件夹名称等等。
01:01:33配置位于 config 目录中。
01:01:35典型的 Adonis 项目包含多个配置文件。
01:01:38我看到了。
01:01:39这是数据库配置文件的样子。
01:01:42邮件配置。
01:01:43注意这个配置文件是如何引用环境变量的。
01:01:46这是使用环境变量的正确方法。
01:01:49是的。
01:01:49所以这很有道理。
01:01:50比如,如果我们有一个发送邮件的应用程序,我们可以配置邮件是如何发送的。
01:01:55但关于一些值,我们把它们放进 .env 文件,而不是把它们硬编码在配置文件中。
01:02:02这只是为了调整设置进行常规配置,但对于具体的值,然后对于机密信息,我们是把它们作为环境变量加载进来的。
01:02:12配置文件在应用程序启动周期中加载。
01:02:16所以我们不使用 Edge 模板,我们不使用 Edge 模板环境变量,.env 大概是自动加载的。
01:02:25应用密钥是一个特殊的环境变量,Adonis.js 用它来进行加密 cookie、签名会话和其他加密操作。
01:02:35所以我们要运行 generate key 来创建一个应用密钥,但我们已经有一个了。
01:02:41好的,我们知道了。
01:02:46所以我假设默认设置在这里就可以了。
01:02:49我不想把这个直播的时间全花在调整配置文件上。
01:02:53所以让我们继续部署。
01:02:58是啊,好的,但我不想部署。
01:03:00我想构建 FAQ,是的,现在构建一个功能齐全的开发展示,一个社区展示网站。
01:03:11在本教程中,你将构建开发展示网站。
01:03:14是的,我想做那个。
01:03:17但先回答问题。
01:03:21你认为 GitHub Actions 是导致 TanStack 安全问题的罪魁祸首,还是 TanStack 的错?
01:03:27从技术上讲,这是 TanStack 的错,因为如果我没记错的话,他们本可以进行配置以防止这种情况发生。
01:03:38但当然,如果你有一个像 GitHub Actions 这样的系统,它使这种特定错误极易发生,那当然也不太好。
01:03:50现在,我要说的是,我不是一个资深的 CICD 专家。
01:03:55多年来,我一直使用 GitHub Actions 进行各种自动化工作。
01:04:00但话又说回来,我没有在大型企业工作,在那里我们有五个团队一直推送到同一个仓库,面临着各种不同的边缘情况和复杂的工作流程。
01:04:12所以我对 GitHub Actions 很满意。
01:04:14但再次说明,我是在以不同的形式使用它。
01:04:18并不都是超级、超级简单的,但绝对不是你所见过的最复杂的工作流程。
01:04:29我想问一下你的 JavaScript 课程。
01:04:31观看超过 90 个视频后仍然不理解,这算错吗?
01:04:33如果你什么都不理解,那可不是一个好迹象。
01:04:38大概是对我来说吧,我想。
01:04:39但这很难回答。
01:04:43但如果按顺序观看视频的话,我想应该会有所感悟。
01:04:51但是,是的,我发现这很难,因为我使用 Windows,而你使用 Apple。
01:04:54但这应该不会有什么区别。
01:04:56JavaScript 就是 JavaScript。
01:04:57所以,但我再说一次,我不知道,如果你说不理解,如果你是不理解 JavaScript 语法,那其实是一样的。
01:05:07所以 Windows 和 Apple 不应该是问题。
01:05:10你好,Max。
01:05:11感谢你对社区的巨大贡献。
01:05:13你好,非常感谢你的赞美。
01:05:16你更喜欢使用 React 还是 Angular,还是取决于项目?
01:05:20取决于项目。
01:05:21但如今,我主要使用 React,当然也是因为它是 AI 的最爱。
01:05:26而且我经常使用 AI 进行编码,无论好坏。
01:05:33在 AI 出现之前,我没有特别强烈的偏好。
01:05:36如果非要说,从语法和易用性来看,我可能更喜欢 Vue,尤其是 Vue 2。
01:05:42但那些日子早就过去了。
01:05:44所以,无所谓了。
01:05:46你认为在学习新框架范式概念时,什么最重要?
01:05:51所以对我来说,深入探究非常重要。
01:05:54我想了解事物在底层是如何工作的。
01:05:57我不想只是得到一个肤浅的答案。
01:06:03我想了解事物是如何连接的,为什么我们以某种特定的方式做某些事情,何时使用某种特定的方法。
01:06:10我真的尝试去质疑事物,并深入挖掘一下,如果你想这么说的话。
01:06:16但现在让我们开始构建吧。
01:06:19我们从 Adonis.js 超媒体开始。
01:06:22我正在使用 Inertia。
01:06:23我们会看看能否依然让它工作。
01:06:27是的,我们有那些路由。
01:06:29我相信我这里也有它们。
01:06:34我们在哪?
01:06:35我们在 start 文件夹。
01:06:36是的,这是 start 文件夹里的路由。
01:06:39是的,我们这里也有。
01:06:40所以看起来是一样的。
01:06:43启动套件提供了注册、登录、注销路由。
01:06:45注意 guest 中间件是如何确保只有未登录用户才能访问注册、登录,而 auth 中间件保护注销路由的。
01:06:53我们已经搞清楚了这一点。
01:06:55控制器是如何工作的。
01:06:56让我们看看 signup 控制器,看看请求是如何在应用程序中流转的。
01:07:07是的,让我们看一看 app controllers 中的这个新账户控制器。
01:07:12我当然是在用 Inertia,而不是 Edge,但仍然如此。
01:07:19每个控制器方法都接收一个 HTTP 上下文对象作为其第一个参数。
01:07:26这个就是。
01:07:28上下文包含关于当前请求的一切,请求数据、响应对象、身份验证状态、视图渲染器等。
01:07:34我们只解构我们需要的部分。
01:07:39create 方法仅仅展示注册表单。
01:07:42是的,我理解了。
01:07:44在这里,因为我们使用 Inertia,所以我们得到了这个 Inertia 对象。
01:07:48这里面还有更多东西。
01:07:50我们可以获取请求来找出,我不知道,URL 之类的东西。
01:07:57但在这里,我们使用这个 Inertia 对象来渲染视图。
01:08:01而对于这个 post 路由,似乎我正在使用它,是的,我们正在提取请求以使用 validate using 方法来验证请求体。
01:08:12是的。
01:08:17你可能注意到控制器引用了一个用户模型和一个 signup 验证器。
01:08:21启动套件已经包含了这些。
01:08:23我们将在后面的章节中探索它。
01:08:27好的。
01:08:30当控制器调用视图渲染器时,它会寻找模板文件并渲染它。
01:08:34所以,是的,我们知道了。
01:08:35视图位于 resources 文件夹中。
01:08:38尝试创建一个账户。
01:08:39是的,我已经做过了。
01:08:40命令行和 REPL。
01:08:42你可能在想为什么我们要涵盖 CLI 和 REPL,而不是直接跳到构建功能。
01:08:46原因如下。
01:08:46在整个教程中,你将不断使用 ACE 命令来生成控制器、模型和其他文件。
01:08:51熟悉 CLI 现在可以防止我们以后中断流程。
01:08:54好的。
01:08:55我们可以通过 node ace list 查看可用命令。
01:08:59让我们做那个。
01:09:02你好。
01:09:05好的。
01:09:06所以,我们有一堆可以运行的内置命令。
01:09:11安装和配置一个或多个 Adonis 包。
01:09:14构建、移除、用于运行数据填充程序的数据库命令。
01:09:19生成密钥。
01:09:21列出我们的路由。
01:09:23制造像事件这样的东西,我们要为其设置监听器。
01:09:29或者创建一个新的中间件或模型。
01:09:32好的。
01:09:32是的。
01:09:38REPL。
01:09:39是的。
01:09:40我实际上要跳过它。
01:09:42我感觉有点冒险。
01:09:54在本章中,你将为帖子和评论资源创建模型和迁移。
01:09:59建立关系。
01:10:01生成模拟数据。
01:10:01好的。
01:10:02本章介绍 Lucid,即 AdonisJS 的 SQL ORM。
01:10:06你不用编写原生 SQL 查询,而是使用名为“模型”的 JavaScript 类来表示你的数据库表。
01:10:11这部分我之前已经说过了。
01:10:14一个重要的区别。
01:10:15模型定义了你如何与数据交互,但它们不会修改数据库结构。
01:10:20那是迁移(Migrations)的工作。
01:10:21没错。
01:10:21所以,我们有一个模型。
01:10:22我们会在代码中使用该模型。
01:10:24来与数据进行交互。
01:10:25但为了做到这一点,我们需要一个数据库表。
01:10:27为了创建那个表,我们需要所谓的“迁移”。
01:10:31可以把它看作是一个小脚本。
01:10:33或者说是调整数据库以获得我们需要正确表的蓝图。
01:10:37而且迁移是从模型派生出来的。
01:10:40从模型类中派生。
01:10:42这就是 Laravel 的工作方式。
01:10:43我理解在 Adonis 中也是这样工作的。
01:10:49那么,让我们添加一个 Post 模型。
01:10:54使用 node ace make:model post 命令。
01:11:01这会给我们生成一个 models/post.ts 文件。
01:11:05并且它也已经创建了一个新的迁移文件。
01:11:09因为正如我所说,我们需要模型和迁移两者。
01:11:11所以,我们现在得到了这个 Post 模型。
01:11:13现在是一个空类。
01:11:19那可能只是我 IDE 中的 TypeScript 错误。
01:11:26或者,不是。
01:11:27我想我们还没有。
01:11:28因为我还没有设置迁移。
01:11:33也就是模式。
01:11:34表的形状还没有定义。
01:11:41在迁移中定义表结构。
01:11:43对。
01:11:43所以,我们需要做这件事。
01:11:45所以,让我把它复制一下。
01:11:48让我们去处理这个 post。
01:11:53迁移文件。
01:11:54基础模式。
01:11:56没错。
01:11:56就是这个。
01:11:59所以,它这里最初做的是建立一个名为 posts 的基本表。
01:12:07它只是给了它一个 ID 和一些时间戳。
01:12:10而我们现在要添加的是这三行。
01:12:14所以,我可以直接复制它们。
01:12:16粘贴到这里。
01:12:18给每篇文章添加一个标题、一个 URL 和一个摘要。
01:12:24看起来是这样。
01:12:25我们将它们设置为不可为空,这样我们就必须在里面填入值。
01:12:28它们不能为 null。
01:12:30好的。
01:12:31那么,我们现在可能需要运行它。
01:12:34没错。
01:12:34“向上(Up)”部分负责创建它。
01:12:36“向下(Down)”部分负责删除表。
01:12:39创建评论模型。
01:12:40让我们创建评论模型。
01:12:43所以,让我们开始。
01:12:45实际上,让我先停止开发服务器。
01:12:47因为我想我们需要重启它,它才能采纳这些更改。
01:12:51所以,再次创建另一个模型。
01:12:53评论模型。
01:12:54就像之前一样。
01:12:56得到了一个新的迁移文件。
01:12:57得到了一个新的……
01:13:01它在哪里?
01:13:02新模型。
01:13:03评论模型在这里。
01:13:04以及迁移文件。
01:13:06同样,基础骨架。
01:13:09我们要在这里添加什么?
01:13:10看来只有一行是关于……
01:13:13内容的。
01:13:18好的。
01:13:21现在我们可以运行它们,应用到数据库以添加这些表。
01:13:27看起来不错。
01:13:29现在让我们看看。
01:13:31再次启动开发服务器,可以运行了。
01:13:35现在的那个错误也不见了。
01:13:36因为在我们应用了迁移后,它可能在幕后创建了这些类型。
01:13:47没错。
01:13:48数据库模式 TS 文件已经更新了。
01:13:51所以,是在这个数据库文件夹下的 schema.ts 文件中。
01:13:56所以,那个文件基本上是为我们管理的。
01:13:58它现在有了由 Adonis 为我们创建的模式。
01:14:02最终是通过这个 ace 命令完成的。
01:14:04感谢你的回答,Max。
01:14:11我买了你几门课程。
01:14:12它们在我的软件开发职业生涯中给了我很大帮助。
01:14:16嗯。
01:14:17非常感谢。
01:14:18非常感谢你能参加这些课程,也感谢这些好评。
01:14:21听到这些内容对你有帮助,我感到非常高兴。
01:14:24能成为你职业生涯的一小部分,我倍感荣幸。
01:14:27真的非常非常感谢。
01:14:31好了,现在来看看我们如何添加关系。
01:14:33因为显然,我们希望在评论和文章之间建立关系。
01:14:37我们希望每篇文章都能拥有一个或多个评论。
01:14:44而每条评论都从属于且仅从属于一篇文章。
01:14:47所以,这是一个一对多的关系。
01:14:54评论属于文章,而文章属于用户。
01:14:57没错,是对的。
01:14:57我们也需要那个。
01:14:58所以,创建用于外键的迁移。
01:15:01接下来的命令将创建一个新的迁移文件,修改我们现有的表。
01:15:04好的,让我们看看。
01:15:06这有什么用?
01:15:09Make migration add_foreign_keys。
01:15:11好的,那只是一个额外的迁移文件。
01:15:13之前,我们有模型,它们自带迁移文件。
01:15:16现在,我们只使用一个迁移文件。
01:15:22因为我们不需要模型,也不需要类。
01:15:25但我们需要一个迁移,以便调整数据库设置,本质上是在一些表中添加一些新列。
01:15:40我们要做的就是添加列来建立这些关系,因为 SQL 数据库中的关系,当然就是简单地通过添加列来表示,比如说明这条评论有这个用户 ID 和这个文章 ID 等等。
01:15:53所以,让我复制一下。
01:15:55让我复制一下。
01:15:56这是文章的外键。
01:16:00这是我创建的吗?
01:16:05添加外键。
01:16:11等一下。
01:16:22哦,现在它会了。
01:16:23我没运行它吗?
01:16:25不,是这个。
01:16:28好的,它只是有一个不同的文件名。
01:16:31嗯,没关系。
01:16:34所以,让我们把它粘贴进去。
01:16:35我们在做什么?
01:16:37我们是在说,在 post 表中,添加一个 user_id 列,它是 integer 类型,unsigned,仅限正值。
01:16:45它不能为空。
01:16:45并且我们设置了那个外键关系。
01:16:48如果用户被删除,我们也删除文章。
01:16:51对于评论,我们也将其关联到用户,但也关联到文章,因为每条评论当然都属于一篇文章。
01:16:58然后我们有向下的迁移来回滚它们。
01:17:01好的。
01:17:03运行它们。
01:17:07运行它们。
01:17:11开发服务器启动。
01:17:13并在 Post 模型中定义关系。
01:17:15既然数据库现在有了外键列,让我们更新模型来定义这些关系。
01:17:20所以,我要复制这个。
01:17:22去我的 Post 模型,它目前基本上是空的,然后把它粘贴进去。
01:17:27现在在那里,在那个类中,我们要通过添加一个 comments 属性来声明那个关系,虽然它没有初始值,但我们要添加那个装饰器。
01:17:43所以,我们只是添加属性以便能够添加一个装饰器,因为看起来在 Adonis 中,我们通过 Adonis 提供的装饰器来设置关系,说明一篇文章有许多评论,并属于一个用户。
01:17:57这部分我想在 Laravel 中的用词也差不多。
01:18:02所以,相当容易阅读和理解。
01:18:05那部分可能有点奇怪,我们只是声明一个变量,声明一个属性。
01:18:11我们需要 declare,因为没有它,我们会从 TypeScript 那里得到一些问题,因为它没有初始值。
01:18:18有了 declare,就不会出现这种情况。
01:18:20所以,就是这样。
01:18:22而且我们还需要在 Comments 模型中定义关系。
01:18:26所以,让我们粘贴进去。
01:18:28在这里,我们基本上说一条评论属于一篇文章,属于一个用户。
01:18:31所以,你有 has 和 belong,取决于你从哪一边看它。
01:18:36你总是把两者都设置好。
01:18:39好的。
01:18:40所以,现在我们搞定了。
01:18:41我们还没有在 User 模型中添加反向的 has many,因为我们在本教程中不需要它们。
01:18:46如果你以后需要从用户端查询文章或评论,你可以再添加它们。
01:18:51好的。
01:18:52所以看起来我们在本教程中永远不会查询某个用户的所有文章。
01:18:58所以,我们不需要设置这些关系,但如果我们想查询某个用户的所有文章,我们就需要设置它们。
01:19:04但是,看来在这里,我们只会查询所有文章,我们只是想显示它们属于哪个用户。
01:19:16天呐,这个框架看起来就像 Angular 和 Nest 的结合体。
01:19:20我猜大型且复杂的框架在今天仍然很有市场。
01:19:24所以我认为这个框架在理论上可能相当棒,因为它内置了太多东西。
01:19:30而且起初看起来确实有点让人不知所措。
01:19:33但巨大的优势在于,你基本上不需要添加任何东西。
01:19:39不是完全没有,但你不需要添加太多东西,对于许多应用程序,你可能根本不需要添加任何东西就能让它适用于各种各样的应用。
01:19:48我是 Next.js、TanStack、Start 的忠实粉丝,但我以前从未使用过 Adonis.js。
01:19:54所以我不能告诉你,嘿,它太棒了,请使用它,对吧?
01:19:56我自己没用过。
01:19:58但是,当然,无论你使用的是 Next.js 还是 TanStack 或者其他什么东西,Angular 的东西,你总是需要为了身份验证添加一个额外的库。
01:20:07对于数据库,你必须添加额外的库,可能是一个 ORM,Prisma,等等。
01:20:14所以,你必须像缝补一样把所有这些库拼凑起来,尤其是现在,考虑到供应链攻击,这可能会很烦人。
01:20:21即使在那之前,维护之类的问题也很让人头疼。
01:20:23所以从理论上讲,这应该很棒,是的。
01:20:26你在没有 AI 的情况下工作,感觉既不错又有些怀旧。
01:20:30哦,是的。
01:20:31有一种怀旧的感觉。
01:20:33显然,我在这里没有使用 AI,因为我想了解它是如何工作的,了解这里发生了什么。
01:20:38而且我觉得如果你正在学习新东西,即使在今天这也是很重要的。
01:20:42如果只是单纯靠“瞎写代码(Vibe coding)”的话,直播也不会有意思。
01:20:47而且,是的,无论如何,“瞎写代码”能走到的地步是有限的。
01:20:52既然我们的模型和数据库表已经准备好了,我们需要用模拟数据来填充它们,以便进行开发和测试。
01:20:57工厂(Factories)充当了创建填充了真实假数据的模型实例的蓝图。
01:21:03你只需定义一次蓝图。
01:21:04好的。
01:21:05所以,我们想要制作一个文章工厂来批量生成一些假文章。
01:21:10所以,我们运行 make:factory 命令。
01:21:14这会给我们一个文章工厂文件,我们现在基本上可以在里面定义如何生成文章,我想。
01:21:21所以,让我们把它复制进来,然后看一看。
01:21:25我们是在说我们要构建一个假文章。
01:21:30当它生成文章时,这个 faker 工具可以从中选择一堆标题。
01:21:37还有一些编造的 URL 和它应该添加的一些乱数假文段落。
01:21:45所以,这只是快速添加一些假数据的编程方式。
01:21:48显然,这些天我们也可以为此使用 AI。
01:21:51但让我们回归老派做法,而且更便宜,因为不用 Token。
01:21:56让我们也创建一个评论工厂。
01:22:02我们就在这里。
01:22:03然后把那段代码复制进来。
01:22:06在这里我们只是输出一些用于填充的乱数假文段落。
01:22:11现在我们需要一个填充程序(Seeder)。
01:22:12好了,我们已经有了可以批量生成虚假数据的工厂。
01:22:15但我们要拿这些虚假数据做什么呢?
01:22:17我们想把它们存入数据库。
01:22:19这就是填充程序的作用所在。
01:22:23所以,我们要创建一个。
01:22:24这些都是通用的概念。
01:22:26工厂,填充程序。
01:22:27这不仅仅是在 Adonis 中才有。
01:22:28你在 Laravel 中也见过它们。
01:22:29或者你可以在任何架构中使用这些概念。
01:22:37同样,在 Next.js 应用中,你也可能有一个需要填入数据的数据库。
01:22:41显然,这些 Node Ace 命令是 Adonis 特有的。
01:22:45但现在我们来创建一个填充程序。
01:22:47让我们看看这个文章填充程序。
01:22:49这是一个我们可以运行的文件,用来将一些初始数据推送到数据库中。
01:22:54因此,我们需要在里面定义我们想做的事情。
01:22:57我猜我们在这里要做的是创建一些虚假的文章和评论,把它们关联起来,然后运行某个命令把它们存入数据库。
01:23:07不过,在 Adonis 中就是这样体现的。
01:23:10所以,让我们把它粘贴进来看看。
01:23:13所以,我们正在使用查找或失败的方法。
01:23:17我们正在尝试查找一个用户。
01:23:19而且那应该会失败。
01:23:21因为它没有说会创建一个用户,而且我们也没有那个用户。
01:23:25所以,我们尝试查找那个用户。
01:23:26我们将不得不创建它。
01:23:28然后我们合并这个用户。
01:23:33我们创建许多新文章,并使用这个命令将它们关联到该用户。
01:23:37然后对于每一篇文章,我们创建一些虚拟评论,然后把它们也关联到那篇文章和那个用户上。
01:23:46我们创建随机数量的评论,再加三个。
01:23:51所以,每篇文章至少有三条评论,总共最多六条。
01:24:02也就是三到六条评论之间。
01:24:06首先,我们用这个邮箱获取一个用户。
01:24:09这就是我们在上一章中创建的那个用户。
01:24:12所以,我会亲自创建那个用户。
01:24:15让我快速完成它。
01:24:19让我们提供这个服务。
01:24:23好吧,我还没试过运行这个填充程序。
01:24:26那应该可以正常工作。
01:24:27但这不对。
01:24:28让我快速注册一下。
01:24:34是啊,是啊,是啊。
01:24:36不,不是这个。
01:24:37来吧。
01:24:39我会使用。
01:24:44好的。
01:24:45所以,我会使用一个不同的电子邮件地址。
01:24:47显而易见,我会用。
01:24:50test@example.com。
01:24:52好的。
01:24:53所以,我们得到了那个。
01:25:02是的,请继续。
01:25:03这样的直播。
01:25:03我们喜欢。
01:25:04对,我绝对想继续做下去。
01:25:06而且,顺便说一下,我的计划是每周四都进行这样的直播。
01:25:13下周我可能无法进行。
01:25:16那天我有其他的约会。
01:25:18但总的来说,每周四的这个时间,也就是从中欧夏令时间下午5点开始,就是我的计划。
01:25:26所以,在我看来,Anthropic 似乎真的想推广他们自己的工具。
01:25:40他们只想推广 Claude Code,其他的都不想。
01:25:42他们想把人们锁定在那里。
01:25:43所以,我们走着瞧吧。
01:25:45我不太确定我们是否能在各种其他工具中使用 Claude。
01:25:51至少不是用订阅支付 token 的那种方式。
01:25:55那大概是可能的,因为那样你就只是在使用 API 了。
01:25:58但是,当然,那也超级昂贵。
01:26:02所以,现在让我们运行这个填充程序。
01:26:06取消这个服务器。
01:26:09运行它。
01:26:10而且,这现在创建了虚拟数据。
01:26:12而且,我们应该已经能看到了,即使在应用程序还没准备好的情况下。
01:26:17在数据库里,我们可以看到文章表和评论表。
01:26:21在用户表里,我有我的用户。
01:26:23并且,在文章表中,我们在这里得到了一些虚拟文章。
01:26:27而且,在评论表中,我们得到了一些虚拟评论。
01:26:33并且,每条评论都链接到一个特定的用户和文章 ID。
01:26:36显然,我们这里只有一个用户。
01:26:38所以,它们都链接到那个用户。
01:26:40那正是我们在填充程序中编写的内容。
01:26:41而且,对于这些文章,我们在这里看到,有一篇文章有,这是多少,五条评论。
01:26:47在这里,我们得到一条有三条评论的,诸如此类。
01:26:50所以,这与我们编写的内容是一致的。
01:26:52好的。
01:26:53所以我理解我们在这里做了什么。
01:26:55用 REPL 查询数据是做不到这一点的。
01:26:57我们已经看到它在那里了。
01:27:00但是,是的。
01:27:01但我们会看看这里的代码。
01:27:02所以,有趣的事情当然是,通过那种基于模型的方法,你不用编写 SQL 查询或类似的东西。
01:27:10相反,你所做的是使用你的模型,或者访问所有的模型。
01:27:17然后,你的模型,这个存在是因为我们注册了一个同名的模型。
01:27:20然后你可以使用像 all 这样的方法来获取所有文章。
01:27:24或者,正如我们在这里看到的,你可以编写查询,其中有一些 where 子句来限制文章的数量。
01:27:30这就是 ORM 的工作原理。
01:27:32你以编程方式查询数据,而不编写 SQL 语句。
01:27:37现在,老实说,我不是 ORM 的忠实粉丝,或者说我并不反对它们,但我通常不使用它们。
01:27:46而且,特别是在人工智能的今天,我发现编写原始 SQL 查询比以往任何时候都容易。
01:27:52所以,对我来说,我通常在我的应用程序中使用原始 SQL 查询。
01:27:58我不使用 ORM。
01:28:00因为,当然,如果你编写原始查询,你就能获得全部的强大功能。
01:28:06而且,即使在人工智能出现之前,许多查询也不难,老实说。
01:28:10即使有连接查询,那也不难。
01:28:13而且,当然,SQL 查询可能会变得相当复杂,人工智能可能会弄错。
01:28:18但是,以我的经验来看,它在大多数查询中表现得相当不错,包括更复杂的查询。
01:28:22所以,这只是我的一点拙见。
01:28:24但是,在这里,我们正在使用 ORM,所以我将坚持使用它。
01:28:29路由、控制器和视图。
01:28:32在上一章中,我们创建了带有数据库表和关系的文章和评论模型。
01:28:37现在,我们将通过构建页面来让这些模型焕发生机,用户可以在页面上实际看到文章。
01:28:46你的文章和评论只存在于数据库中。
01:28:49服务器已经启动了。
01:28:52所以,现在,我们想要创建一个新的控制器。
01:28:54据我理解,这个框架的想法是,你有一个路由,你有一个控制器,你有一个视图。
01:29:02所以,这是好的老式 MVC,如果你们中有人还记得的话。
01:29:07模型视图控制器。
01:29:09这是一种非常古老的应用结构方式,不仅仅是针对 Web 应用。
01:29:18但是,古老并不意味着糟糕,这一点必须非常明确。
01:29:24但是,我想在所有那些 Next.js 应用等等中,你真的看不到它了。
01:29:30所以,是的。
01:29:31在另一个选项卡中,让我运行这个,创建一个新的文章控制器。
01:29:38它在这里,一个空文件,我们在里面注册方法,这些方法定义了在四个不同路由中会发生什么。
01:29:44同样,为了节省时间,我把它复制进来。
01:29:47这里我们有一个 index 方法。
01:29:52我们在这里获取了一些 HTTP 上下文,它再一次给了我们各种数据。
01:29:57而且,实际上,我们必须调整这个,因为这这里是针对,我需要使用 inertia。
01:30:05我需要使用 inertia,当然,我们需要添加这个组件。
01:30:09但是,我们在这里所做的是,我们正在使用我们的文章类,也就是,来自我们的模型文件,对吧?
01:30:16所以,这里,文章类,也就是我们之前定义的。
01:30:20然后,再一次,这是运行中的 ORM,我们在那里创建一个查询。
01:30:24预加载用户可能就在那里,所以我们加载它一次,而不是为我们获取的每一篇文章都再次加载它,我假设。
01:30:32然后,我们按创建日期降序排列文章,很明显。
01:30:38所以,第一篇文章是最年轻的,最新的那一篇。
01:30:45所以,那么,我们渲染,因此,我们将需要添加这个。
01:30:53定义路由。
01:30:56所以,现在,我们必须,好吧,路由就是我们去 start 路由。
01:31:03而且,这里,我们在里面添加了它。
01:31:07当然,我们可以在任何地方添加它。
01:31:10为什么这行不通?
01:31:12为什么这行不通?
01:31:13我是需要重启开发服务器,还是类似的东西?
01:31:16哎呀,那是错的。
01:31:18哦,我没有启动开发服务器。
01:31:21那是我的错。
01:31:22所以,现在,让我们把它启动起来,然后,希望,是的。
01:31:25现在,这是自动创建的。
01:31:26所以,基本上,Adonis 正在动态创建一堆类型定义。
01:31:31所以,现在,这个就在这里。
01:31:34我们告诉 Adonis,对于请求,即到 /posts 的 get 请求。
01:31:40我们想要触发文章控制器中的 index 方法。
01:31:43而且,当然,那是这个方法。
01:31:47现在,我们将需要视图。
01:31:50而且,我们可以制作一个新视图。
01:31:53我不知道这是否是正确的命令,如果我想要一个 inertia 视图的话。
01:31:56所以,让我们看看。
01:31:59我可以运行 node as list 来看看我能制作什么。
01:32:04所以,我可以制作一个视图,但那是一个 Edge.js 模板文件。
01:32:08我不要那个。
01:32:11也许我确实得手动创建它。
01:32:20看起来我得手动创建它,但这应该不会太难。
01:32:24所以,让我们去 inertia 页面。
01:32:29所以,在里面,我现在会添加文章。
01:32:31并且,在里面,添加一个 index.js 文件,对吧?
01:32:34然后,我导出一个默认函数。
01:32:38而且,我会把它命名为文章。
01:32:41而且,我会说类似我的文章。
01:32:44所以,就是一个常规的 React 组件。
01:32:50现在,这个错误消失了。
01:32:51因为,现在,我确实添加了那个东西,对吧?
01:32:55所以我确实添加了一个带有 index.js 文件的文章文件夹。
01:32:58而且,那正是我在这里说的。
01:32:59所以,这就是这个是如何关联起来的。
01:33:02而且,到目前为止,当然,随着开发服务器的启动并运行,
01:33:05我可以去这里,输入 /posts,我就在这里看到了我的文章。
01:33:09所以,那是有效的。
01:33:10而且,现在,当然,我们只需要将数据从控制器获取到那个组件中。
01:33:20而且,实际上,我假设我们已经以 props 的形式在这里做了。
01:33:23所以,问题是,如果在里面,我们得到了这个。
01:33:30那么,我该如何...
01:33:33哦,不,我想我们需要使用 children。
01:33:36这在另一个文件里是怎么工作的?
01:33:40我们有任何传输数据的文件吗?
01:33:45没有。
01:33:46我们没有,我们没有,我们没有,我们没有。
01:33:50在布局里,对吧?
01:33:52Children。
01:33:54所以,我假设这就是我所需要的。
01:33:58但我之前从未这样做过。
01:34:00所以,我们得弄清楚这一点。
01:34:19这难道不是我们这里有的吗?
01:34:23哦,是的,我的错,我的错,我的错。
01:34:25当然,这不是有效的 TypeScript 代码。
01:34:27这应该像这样。
01:34:30好的。
01:34:30所以,现在,我们需要添加那些导入。
01:34:36导入。
01:34:38所以,我假设这是正确的。
01:34:40这是我们这里有的吗?
01:34:41数据生成数据。
01:34:42是啊。
01:34:43好吧。
01:34:45好吧。
01:34:45那么,我们来看看。
01:34:46我们可以这里加个片段吗?
01:34:50然后做点类似这样的事。
01:34:54子组件。
01:34:57Props。
01:34:59嗯。
01:35:02用户。
01:35:03是啊,也许这不是共享的 props。
01:35:04我该怎么获取……
01:35:06我可能得深入研究一下……
01:35:10让我看看。
01:35:11我可能得去看看 Inertia 的文档。
01:35:15指南。
01:35:17Inertia 在哪呢?
01:35:19我该怎么把 props 传进组件里?
01:35:24我需要……
01:35:25我需要一个转换器吗?
01:35:29使用转换器将模型实例序列化为纯对象。
01:35:33明白了。
01:35:34明白了。
01:35:35明白了。
01:35:37好吧。
01:35:37所以,我打算偷个懒。
01:35:40请 AI 帮个忙。
01:35:44嗯。
01:35:48构建一个 Adonis。
01:35:51JS 应用。
01:35:53我需要获取我的文章。
01:35:56看看。
01:35:57文章控制器。
01:36:02进入我的文章 Inertia。
01:36:04哎呀。
01:36:05我发现这对你来说不可读。
01:36:10好吧。
01:36:10好吧。
01:36:10等一下。
01:36:18好吧。
01:36:18构建一个 Adonis。
01:36:20JS 应用。
01:36:21我需要获取我的文章。
01:36:24来自我的文章控制器。
01:36:27添加。
01:36:28哎呀。
01:36:28添加文章。
01:36:30控制器。
01:36:32进入我的文章。
01:36:35视图。
01:36:35Inertia。
01:36:37视图。
01:36:39所以,这就是文章文件夹里的索引。
01:36:45我会链接到这些页面。
01:36:48请查看。
01:36:50然后。
01:36:56帮我做这件事。
01:36:59看看它能不能搞定。
01:37:03嗯。
01:37:03ORM 对于模式转换和协作来说是好东西。
01:37:07嗯。
01:37:08是啊。
01:37:09再说一次。
01:37:09我没有说。
01:37:10ORM 很糟糕。
01:37:11只是。
01:37:11只是我的项目不需要它们。
01:37:14但话又说回来。
01:37:14我没在什么大型团队工作之类的。
01:37:18嗯。
01:37:18我。
01:37:19我确实。
01:37:20说过。
01:37:21我确实使用迁移。
01:37:23所以。
01:37:23并不是说。
01:37:24我不。
01:37:25使用迁移。
01:37:26或者类似的东西。
01:37:27嗯。
01:37:28我只是也。
01:37:29自己写它们。
01:37:30或者让。
01:37:31LLM 写它们。
01:37:32所以迁移非常有用。
01:37:34我只是不需要 ORM 部分。
01:37:36我一直在想。
01:37:36要不要问你。
01:37:37快节奏编码。
01:37:38好不好。
01:37:39我不认为。
01:37:40快节奏编码。
01:37:41适合。
01:37:42任何严肃的事情。
01:37:43但是。
01:37:44快节奏编码。
01:37:44绝对是非常好。
01:37:45如果你只是需要。
01:37:46一个快速。
01:37:47工具。
01:37:48或者你只是需要。
01:37:48把工作完成。
01:37:50对吧。
01:37:50如果你在构建一些软件。
01:37:51你要销售的。
01:37:52如果你在。
01:37:53从事任何严肃的工作。
01:37:55你打算分发的。
01:37:56或者在你的工作中。
01:37:57快节奏编码。
01:37:58只会让你丢失。
01:37:59所有的追踪。
01:38:00你并不了解。
01:38:01代码库中发生了什么。
01:38:02最终。
01:38:03你会撞墙。
01:38:04并且你不会。
01:38:04知道如何继续。
01:38:06所以。
01:38:07不关心代码。
01:38:08不是一个好的解决方案。
01:38:09对于。
01:38:09这种情况。
01:38:11但话又说回来。
01:38:11如果你只是需要。
01:38:13嗯。
01:38:13一个快速。
01:38:15工具。
01:38:16你在你的机器上运行的。
01:38:17或者类似的东西。
01:38:18你可能不在乎。
01:38:19嗯。
01:38:20关于。
01:38:21所有的细节。
01:38:23之类的。
01:38:24所以是的。
01:38:25快节奏编码在那儿可能不错。
01:38:26而且是的。
01:38:26直播编码总是。
01:38:28充满挑战。
01:38:29但我们看看 AI 能不能搞定。
01:38:31它似乎正在这里对抗一些 lint 错误。
01:38:36但是的。
01:38:38我只是在用 AI。
01:38:40顺便说一下。
01:38:40通常我会钻研这里。
01:38:41并且自己构建它。
01:38:44但我 20 分钟后得走。
01:38:47我宁愿现在就拿到完成的代码。
01:38:49然后通过它工作。
01:38:51现在。
01:38:52这样我们之后也能继续在这里工作。
01:38:54这是主要原因。
01:38:56所以好吧。
01:38:56它刚才做了点什么。
01:38:58让我们先看看它是否有效。
01:39:00在我开始解释之前。
01:39:02然后我们再看看它做了什么。
01:39:04但这看起来不错。
01:39:05样式完全糟糕。
01:39:07但正如我们所见。
01:39:10我有一堆文章被渲染出来了。
01:39:12我能看到它们与我的用户相关联。
01:39:16所以。
01:39:17从控制器发送数据。
01:39:19似乎有效。
01:39:19所以让我们看看。
01:39:20它是如何工作的。
01:39:22而且再说一次。
01:39:22我给了它官方文档的链接。
01:39:24所以这只是。
01:39:28这只是。
01:39:31文档教我们做的。
01:39:34所以在文章控制器中。
01:39:36我们正在获取文章。
01:39:38这里唯一改变的是。
01:39:39本质上。
01:39:40我们。
01:39:41传递我们的文章。
01:39:42而不是像这样原始的。
01:39:43而是。
01:39:44借助文章转换器。
01:39:45这是它添加的新东西。
01:39:47所以这个文件是新的。
01:39:48文章转换器。
01:39:50那是一个类。
01:39:51它扩展了基础转换器。
01:39:53好吧。
01:39:53在那里。
01:39:54我们只是描述。
01:39:55如何序列化一篇文章。
01:39:56所以我们在说。
01:39:57嘿。
01:39:58挑选所有这些属性。
01:40:01然后。
01:40:02对于用户。
01:40:03也要转换用户。
01:40:04所以本质上。
01:40:05我的意思是,在这种情况下。
01:40:06转换过程应该非常简单。
01:40:08它可能只需要提取日期。
01:40:11ISO
01:40:12格式。
01:40:13这里的日期。
01:40:14以及其他原始数据。
01:40:15可以打包。
01:40:16放进 JavaScript 对象里。
01:40:17像这样。
01:40:18所以它基本上。
01:40:19描述了。
01:40:20如何转换。
01:40:21这个类。
01:40:21其中可能包含一些内容。
01:40:23不容易转换的内容。
01:40:25转换成 JSON。
01:40:26如何将那些。
01:40:27转换成 JSON。
01:40:28这就是。
01:40:28转换器的工作。
01:40:30我是这样理解的。
01:40:33好的。
01:40:33所以我们要在这里设置帖子。
01:40:34现在。
01:40:35在视图中。
01:40:40在视图中。
01:40:41我们得到页面。
01:40:42好的。
01:40:43我们只是设置了。
01:40:43页面属性,像这样。
01:40:44好的。
01:40:45我们只是设置了。
01:40:46一个 props 类型。
01:40:47TypeScript 类型。
01:40:48使用 Inertia props。
01:40:51代替。
01:40:51一些生成的类型。
01:40:54或者。
01:40:55加上那个。
01:40:56添加那个。
01:40:57我想。
01:40:58这是一些。
01:40:58标准类型。
01:41:00不是。
01:41:00不是 AI 生成的。
01:41:02并且。
01:41:02我们只是说。
01:41:03好的。
01:41:03嘿。
01:41:04这里。
01:41:04我们将有一个 post 键。
01:41:06并且。
01:41:06那是。
01:41:07类型的。
01:41:08是的。
01:41:08所以那是。
01:41:09一个自动生成的。
01:41:10类型。
01:41:10对于。
01:41:11对于我们的帖子。
01:41:12而且我认为。
01:41:13这个类型。
01:41:14是生成的。
01:41:15基于我们。
01:41:15如何转换。
01:41:16帖子。
01:41:17所以帖子在这里。
01:41:20这个东西。
01:41:23是的。
01:41:24来自帖子转换器。
01:41:25没错。
01:41:27好的。
01:41:28然后我们就可以。
01:41:29在这里渲染它们。
01:41:30并不太难。
01:41:32明白了。
01:41:35好的。
01:41:36现在再次。
01:41:36官方教程。
01:41:38使用了 Edge 视图。
01:41:39所以。
01:41:39我正在使用 Inertia。
01:41:40而且。
01:41:41如你所见。
01:41:41它就是一个 React 应用。
01:41:43最终。
01:41:44我们还要转换。
01:41:46并且。
01:41:46发送我们的。
01:41:48props。
01:41:50好的。
01:41:53显示单个。
01:41:54帖子。
01:41:55是的。
01:41:55当然。
01:41:55所以现在。
01:41:56我们可以添加。
01:41:56另一个。
01:41:58方法在这里。
01:42:00在我们的。
01:42:00控制器中。
01:42:01因为。
01:42:02在我们的。
01:42:02帖子。
01:42:03控制器中。
01:42:03目前。
01:42:04我们有一个。
01:42:05索引。
01:42:05方法。
01:42:05用于。
01:42:06显示。
01:42:07所有的帖子。
01:42:08但现在。
01:42:08我们要。
01:42:08也。
01:42:09添加一个方法。
01:42:09用于。
01:42:10显示。
01:42:10单个。
01:42:10帖子。
01:42:11所以。
01:42:11我们正在添加。
01:42:12一个 show。
01:42:12方法。
01:42:12在这里。
01:42:13看起来。
01:42:14而且再次。
01:42:14这些名字。
01:42:16由你决定。
01:42:16稍后。
01:42:17当你连接它们时。
01:42:18在。
01:42:18路由。
01:42:19文件中。
01:42:19你。
01:42:19必须确保。
01:42:20它们匹配。
01:42:20但我们稍后会看到。
01:42:21那一点。
01:42:21用不了多久。
01:42:23所以这里。
01:42:23我们得到了一些。
01:42:24参数。
01:42:25因为现在。
01:42:25我们有。
01:42:26一个动态。
01:42:26路由。
01:42:26再次。
01:42:27你可能知道。
01:42:28从 React。
01:42:28等等。
01:42:28那是。
01:42:29路由。
01:42:29带有。
01:42:30带有。
01:42:30冒号。
01:42:31参数。
01:42:31在路由名字中。
01:42:32例如。
01:42:32或者。
01:42:33美元符号。
01:42:33在文件名中。
01:42:34所以这里。
01:42:34我们也。
01:42:35得到一些。
01:42:35动态。
01:42:35参数。
01:42:36我们要看。
01:42:37如何注册。
01:42:38那些路由。
01:42:38马上。
01:42:39然后。
01:42:39我们期望。
01:42:40有一个。
01:42:40ID 参数。
01:42:41所以我们要。
01:42:42寻找一个。
01:42:43特定 ID 的帖子。
01:42:43我们想。
01:42:44失败。
01:42:44如果。
01:42:45找不到它。
01:42:46这样我们就可以。
01:42:46显示 404 错误。
01:42:46或者其他任何错误。
01:42:47我想。
01:42:48我们想。
01:42:49渲染。
01:42:49而不是一个视图。
01:42:50相反。
01:42:50我们想。
01:42:51渲染。
01:42:52这里。
01:42:53我们可以。
01:42:54获取 Inertia。
01:42:55并且。
01:42:55我们重用。
01:42:57我们的帖子转换器。
01:42:58来传递。
01:42:58我们的帖子。
01:42:59我们要渲染。
01:43:05我们想要。
01:43:05获取。
01:43:06这里的 Inertia。
01:43:09然后我们重用。
01:43:10我们的文章。
01:43:11转换器。
01:43:12来传递。
01:43:13我们的文章。
01:43:14给。
01:43:17到一个展示。
01:43:18组件。
01:43:18例如。
01:43:19所以。
01:43:20现在。
01:43:20在 Inertia。
01:43:21文件夹里。
01:43:21在文章。
01:43:22文件夹里。
01:43:22我们需要。
01:43:23有。
01:43:23Show。
01:43:24TSX。
01:43:24或者。
01:43:25Detail。
01:43:26TSX。
01:43:26或者。
01:43:26任何。
01:43:27类似的东西。
01:43:28然后。
01:43:29现在。
01:43:29在那里。
01:43:31我们有。
01:43:35我们的。
01:43:35我们的。
01:43:35文章。
01:43:36组件。
01:43:36在这里。
01:43:38实际上。
01:43:40我们命名为。
01:43:42Post。
01:43:45Post。
01:43:47像这样。
01:43:48然后。
01:43:49就像之前一样。
01:43:50我会。
01:43:50快速地。
01:43:51抄过来。
01:43:55我们得到。
01:43:55单一的文章。
01:43:56在这里。
01:43:56不是数组。
01:44:00选择这个。
01:44:01然后。
01:44:01我们可以。
01:44:02返回。
01:44:05好的。
01:44:05当然。
01:44:06文章。
01:44:06标题。
01:44:07像这样。
01:44:11你好。
01:44:12好的。
01:44:12像这样。
01:44:13好的。
01:44:16我们在这里做什么?
01:44:17所以。
01:44:19然后。
01:44:19当然。
01:44:19还有。
01:44:20那个。
01:44:21文章。
01:44:21摘要。
01:44:21例如。
01:44:22好的。
01:44:23好的。
01:44:23所以我们得到这个。
01:44:24呃。
01:44:25应该能行。
01:44:26现在。
01:44:27我们需要注册它。
01:44:28在路由文件夹中。
01:44:30所以现在。
01:44:30我们可以添加一个新的。
01:44:31哎呀。
01:44:32GET 路由。
01:44:33对于斜杠。
01:44:34对于斜杠。
01:44:35文章。
01:44:35然后。
01:44:36我假设。
01:44:37这就是我们注册的方式。
01:44:38动态路由。
01:44:39看看。
01:44:41是的。
01:44:42用冒号 ID。
01:44:44非常典型。
01:44:45现在。
01:44:46我们说。
01:44:46嘿。
01:44:47在我们的文章控制器中。
01:44:48是 show 方法。
01:44:49我们感兴趣的。
01:44:51因为。
01:44:52在那个控制器里。
01:44:53我们命名了这个方法。
01:44:54Show。
01:44:54如果你在这里选了不同的名字。
01:44:56我们也必须在这里选一个不同的名字。
01:44:58但 show 是我用的名字。
01:44:59所以那应该能行。
01:45:02所以。
01:45:05我们这里有哪些链接?
01:45:08哦。
01:45:08那不是到详情的链接。
01:45:10但如果我加上文章一。
01:45:13是的。
01:45:14我有加载。
01:45:15样式完全不对。
01:45:16但就在最下面。
01:45:17我们可以看到。
01:45:18内容。
01:45:19所以。
01:45:20还有。
01:45:21标题。
01:45:22Markdown 博客引擎。
01:45:24也在这里。
01:45:25所以样式完全坏了。
01:45:26但是。
01:45:27它能用。
01:45:28我们可以看到那个路由。
01:45:31当然我们可以修复样式。
01:45:32但这现在不是最重要的事。
01:45:35所以是的。
01:45:36我们得到了视图。
01:45:37我们已经得到了所有这些。
01:45:39使用命名路由。
01:45:40那很有趣。
01:45:40是的。
01:45:41现在我们硬编码了 URL。
01:45:44而且我实际上没有设置任何链接。
01:45:47所以。
01:45:49当你在路由定义中使用控制器时。
01:45:51Adonis.js 会自动生成路由名称。
01:45:54基于控制器和方法名称。
01:45:56如果我们有 controllers.posts.index。
01:45:58那是。
01:45:59它会自动得到 posts.index 这个名字。
01:46:02所以我假设我们可以去。
01:46:05文章索引页面,我们在那里有所有那些文章。
01:46:08而不是像这样使用锚标签。
01:46:11我们可以使用。
01:46:12链接组件。
01:46:13它来自 inertia.js react。
01:46:17也在这里。
01:46:18然后我不确定。
01:46:19它有 to 属性吗。
01:46:21或者是。
01:46:21它是 ref。
01:46:23然后我们可以说类似这样的。
01:46:26路由。
01:46:29Posts show。
01:46:34看起来不像这样。
01:46:42Posts show。
01:46:49是的。
01:46:49我正在使用不同的方法。
01:46:50我想用这个链接组件。
01:46:53再一次。
01:46:55我们在哪里有它?
01:46:57Link route。
01:46:59哦好的。
01:46:59它只是。
01:47:01好的。
01:47:02所以是 link。
01:47:04Route。
01:47:08为什么我在这里得到一个路由错误?
01:47:11创建链接。
01:47:12链接组件创建导航链接。
01:47:14哦。
01:47:15导入错了。
01:47:16我需要从。
01:47:20这里。
01:47:20从 Adonis 包。
01:47:22现在我们有了对路由的支持。
01:47:24现在只是文章。
01:47:33它只是一个字符串。
01:47:34它只是一个字符串。
01:47:35好的。
01:47:35所以它是。
01:47:37Route。
01:47:38好的。
01:47:38现在我们在这里得到一些自动完成。
01:47:40现在我需要设置参数。
01:47:43路由参数。
01:47:45Posts.id。
01:47:48不。
01:47:49Post.id。
01:47:53所以那必须是一个对象。
01:47:57Id 设置为 post.id。
01:48:00那将为每篇文章动态生成一个不同的路由。
01:48:03并把那个动态占位符替换成帖子ID,我想。
01:48:07所以如果我现在刷新一下。
01:48:09不是这儿。
01:48:10而是这儿。
01:48:14为什么没更新呢?
01:48:16我需要重启开发服务器吗?
01:48:20哦。
01:48:21保存文件应该也有帮助。
01:48:24对。
01:48:24现在我可以点击这里导航到这些帖子了。
01:48:28样式当然还很丑。
01:48:30但它起作用了。
01:48:33当然了。
01:48:34这里使用路由名称的好处是,如果以后我们更改了路径。
01:48:38我们就不需要修改链接了。
01:48:40只要路由名称没变就行。
01:48:42所以这当然很棒。
01:48:44现在。
01:48:45你可能不会太频繁地更改路径。
01:48:47但还是挺好的。
01:48:48非常不错。
01:48:52嘿。
01:48:52是我的孩子。
01:48:53是的。
01:48:55它灵感来源于Laravel。
01:48:57绝对是。
01:48:58我想他们自己标榜为JavaScript版的Laravel。
01:49:07你非得用手写代码吗?
01:49:09那就像婴儿玩具一样。
01:49:12嗯。
01:49:17所以。
01:49:18当你学习某样东西时。
01:49:19我绝对会亲自动手。
01:49:21我肯定会亲手写代码。
01:49:23因为光靠阅读。
01:49:26我会采取混合方式。
01:49:28但当然,对于直播来说。
01:49:29如果只是让AI搞定一切。
01:49:30我觉得没什么意思。
01:49:33但几分钟前我确实用了它。
01:49:36但是。
01:49:37学习的话,你应该务必。
01:49:39亲自上手。
01:49:40单靠阅读。
01:49:44单靠阅读学不好东西。
01:49:46现在。
01:49:47我不是说你会写所有的代码。
01:49:50就像你四年前那样。
01:49:52甚至差得远呢。
01:49:54但至少要了解基础。
01:49:56找找感觉。
01:49:56我是说。
01:49:57你刚加入对吧。
01:49:58但我只是在复制。
01:49:59我写的那些基本不是。
01:50:01嗯。
01:50:03代码。
01:50:03是啊。
01:50:03是啊。
01:50:03我知道。
01:50:04我明白了。
01:50:05没冒犯的意思。
01:50:06只是。
01:50:07我觉得写代码。
01:50:09总的来说是个好建议。
01:50:10所以我才发表评论。
01:50:12我想。
01:50:14写代码对学习仍然很有用。
01:50:18但是。
01:50:19尽管那只是个玩笑。
01:50:21或者引用。
01:50:24这。
01:50:26这。
01:50:26情况显然在变化。
01:50:28我早些时候被问到了。
01:50:32关于新课程。
01:50:33有一个问题。
01:50:34或者我在尝试弄明白的。
01:50:35就是你如何。
01:50:37最好地学习现在的技术。
01:50:41这是否还有意义。
01:50:42我会说是的。
01:50:43它绝对还有意义。
01:50:45但是。
01:50:46正是那一点。
01:50:47你手动写多少代码?
01:50:48你复制多少?
01:50:50你让AI解释或生成多少?
01:50:52这就是。
01:50:53我只是在胡言乱语。
01:50:54我只是在胡言乱语。
01:50:54但这绝对是正在改变的事情。
01:50:57这是Adonis JS。
01:51:00所以。
01:51:00它基本上是JavaScript版的Laravel。
01:51:09好。
01:51:09所以。
01:51:14是。
01:51:15评论。
01:51:16我不认为我会那样做。
01:51:18我得在10分钟内走。
01:51:20所以我们可能会让它保持原样。
01:51:22简单看一眼剩下的几个部分。
01:51:28页面。
01:51:29这个指南里没剩多少东西了。
01:51:31我们已经讲得相当不错了。
01:51:33表单和验证。
01:51:35这里的想法是。
01:51:37当然,我们也可以有控制器功能。
01:51:40在POST请求时调用的控制器方法。
01:51:43我们可以看到。
01:51:45我们在那个里可以看到。
01:51:48我们没有用户控制器吗?
01:51:52用户。
01:51:54这个。
01:51:54新的账户控制器。
01:51:56这儿。
01:51:57存储。
01:51:57那是。
01:51:59一个控制器方法。
01:52:00被调用。
01:52:02在POST请求时。
01:52:04这个。
01:52:05并且。
01:52:06因此。
01:52:07它只是一个普通的方法。
01:52:08但我们在那儿可以看到。
01:52:10你可以使用验证工具。
01:52:13方法。
01:52:13定义一个验证器。
01:52:14最后。
01:52:16只是一个小工具对象。
01:52:18用那里的葡萄藤工具创建的。
01:52:21你可以在那里定义一些规则。
01:52:22你想接受什么样的数据。
01:52:24然后。
01:52:24错误会自动生成。
01:52:26为你如果。
01:52:27如果没有满足验证条件。
01:52:29所以整个想法。
01:52:30在Laravel背后。
01:52:30和Adonis。
01:52:31是他们为你做了很多工作。
01:52:33你可以。
01:52:33定义你的逻辑。
01:52:34在相当高的层面上。
01:52:36而在。
01:52:37Next。
01:52:37JS。
01:52:37等等。
01:52:38你经常不得不变得非常具体。
01:52:41显然。
01:52:41你得带上你自己的验证库。
01:52:43以及所有那些有趣的东西。
01:52:52所以我们在做的是。
01:52:54正在注册路由。
01:52:56我们已经看到了。
01:52:59在这种情况下创建一个新视图。
01:53:00这里他们正在创建一个表单。
01:53:02现在这些都是特定的。
01:53:05代码片段。
01:53:06但当然。
01:53:08我们这里也有那个。
01:53:12它在哪儿?
01:53:13登录。
01:53:14对。
01:53:14如果我们使用inertia,我们就能看到。
01:53:17我们这里有这个表单组件。
01:53:19它来自Adonis.js inertia包。
01:53:22它也链接到一个路由。
01:53:24应该被触发。
01:53:25如果表单被提交。
01:53:26这就是那个存储路由。
01:53:27我们刚看到的。
01:53:28或者那个存储控制器动作。
01:53:30最后。
01:53:31被那个路由调用。
01:53:33数据。
01:53:34我想是。
01:53:36打包进请求里。
01:53:37并自动提交到后端。
01:53:39这当然很方便。
01:53:40因为你不需要自己写代码。
01:53:42或者让AI帮你写。
01:53:45如果你使用Edge。
01:53:46你可以像这样渲染你的表单输入。
01:53:48可以说。
01:53:50这样更容易理解。
01:53:51也更容易阅读,我想。
01:53:56然后我们可以创建验证器。
01:53:57我们之前已经见过一个了。
01:53:58用那个酒工具。
01:54:00你只需定义。
01:54:01对于数据。
01:54:02你准备接收的。
01:54:04你的规则是什么。
01:54:05比如最小长度。
01:54:06最大长度。
01:54:07数据类型。
01:54:08等等。
01:54:10获取存储方法。
01:54:11然后再次。
01:54:11我们可以使用 ORM。
01:54:13使用我们的类。
01:54:14调用静态 create 方法。
01:54:16来存储数据。
01:54:19评论。
01:54:20做同样的事情。
01:54:23还要将其链接到用户。
01:54:24通过 ID。
01:54:26我们得到该用户。
01:54:28通过那个 auth。
01:54:30属性。
01:54:31我们可以自动获取它。
01:54:32再说一次。
01:54:33就像。
01:54:33Adonis 在做它的工作。
01:54:35并为你完成身份验证。
01:54:37多亏了中间件。
01:54:39这是注册好的。
01:54:40对吧。
01:54:40所以在路由中。
01:54:40我们得到一些。
01:54:41身份验证中间件。
01:54:42例如。
01:54:43这里的任何路由。
01:54:44都需要身份验证。
01:54:46在那里的任何路由。
01:54:48因此。
01:54:48也一样。
01:54:51也可以获取这个 auth。
01:54:53属性。
01:54:54你可以使用它。
01:54:55来获取数据。
01:54:56关于当前。
01:54:57经过身份验证的用户。
01:54:58或者你可以使用它。
01:54:59来注销用户。
01:55:00比如这种情况下。
01:55:01等等。
01:55:02所以就是这样。
01:55:03非常方便。
01:55:04一旦设置好了。
01:55:06我至少是这么认为的。
01:55:07并不是说。
01:55:08我以前处理过那个。
01:55:09但是是的。
01:55:11我学到了。
01:55:11从你那里学到了很多。
01:55:12并将原则转发。
01:55:14给 Codecs。
01:55:14比如 Codecs。
01:55:15写高质量的。
01:55:16所以你的课程。
01:55:16仍然很有价值。
01:55:17对于好的提示词。
01:55:18是的。
01:55:18我正在探索的一件事。
01:55:20也是。
01:55:20你如何。
01:55:22为未来的课程。
01:55:23包含一些东西。
01:55:24像代理技能。
01:55:25或额外的文档。
01:55:26你可以喂给。
01:55:27你的代理。
01:55:28来帮助他们。
01:55:31编写更好的代码。
01:55:32当然。
01:55:33这永远无法保证。
01:55:34因为代理。
01:55:37总是可以做。
01:55:38它们自己的事情。
01:55:39等等。
01:55:40但那是某事。
01:55:40我也在探索。
01:55:41当然。
01:55:42我更喜欢。
01:55:43Adonis 还是 Vue 3。
01:55:45所以 Adonis。
01:55:46我真的只是。
01:55:47刚开始。
01:55:48我以前没用过。
01:55:49一点都没。
01:55:50而 Vue 3。
01:55:52并不是。
01:55:52一个替代品。
01:55:53因为 Adonis。
01:55:54是全栈的。
01:55:55而 Vue。
01:55:55是客户端。
01:55:57前端。
01:55:58用 Nuxt。
01:55:59它又是全栈了。
01:56:00但。
01:56:00那时候。
01:56:01我们仍然会有。
01:56:02不同的哲学。
01:56:03Adonis。
01:56:04是开箱即用的。
01:56:06所以。
01:56:06所有那些东西。
01:56:07比如身份验证。
01:56:08是内置的。
01:56:09而 Nuxt。
01:56:11就像。
01:56:11就像 Next。
01:56:12你必须带来。
01:56:13你自己的。
01:56:14库。
01:56:15归根结底。
01:56:16我不知道。
01:56:17我更喜欢什么。
01:56:17我喜欢。
01:56:18Adonis 的方式。
01:56:19我绝对。
01:56:21能看出。
01:56:22它的价值。
01:56:23如果你正在构建。
01:56:25一个全栈。
01:56:27应用程序。
01:56:28现在。
01:56:28我。
01:56:29太习惯了。
01:56:30Next。
01:56:31JS。
01:56:31栈启动。
01:56:32还有所有那些东西。
01:56:33等等。
01:56:34我。
01:56:35我不知道。
01:56:36如果我会切换。
01:56:37到 Adonis。
01:56:37仅仅因为。
01:56:38我可能。
01:56:39更快。
01:56:40使用那些。
01:56:41其他。
01:56:41技术栈。
01:56:43但是。
01:56:44但话说回来。
01:56:45有很好的理由。
01:56:46比如。
01:56:46拥有。
01:56:47更少。
01:56:48外部。
01:56:49依赖。
01:56:50除了 Adonis。
01:56:51等等。
01:56:51所以。
01:56:52我可能会。
01:56:53继续。
01:56:54玩转它。
01:56:55然后。
01:56:55评估。
01:56:56它在哪里有用。
01:56:57对我来说。
01:56:58在哪里没用。
01:56:58如果我想构建更多。
01:57:00用它。
01:57:02是的。
01:57:03那是。
01:57:03基本上。
01:57:04计划。
01:57:05我想。
01:57:07嗯。
01:57:07不。
01:57:08将不会。
01:57:08然而。
01:57:09我不太在乎。
01:57:10关于样式。
01:57:10那只是 CSS。
01:57:12但关于身份验证
01:57:13我想深入研究一下
01:57:16所以有另一个包。
01:57:18我们可以安装。
01:57:19那是 Adonis 的一部分。
01:57:20生态系统。
01:57:21Balancer 包。
01:57:22它是为了。
01:57:24定义一个名称。
01:57:25这一章的名字。
01:57:27我会说。
01:57:27它是。
01:57:28它负责。
01:57:29阻止人们。
01:57:30不允许。
01:57:31做某事。
01:57:33当然了。
01:57:35授权。
01:57:36与身份验证不同。
01:57:37是关于。
01:57:39阻止用户。
01:57:40做某些事情。
01:57:41他们不允许。
01:57:42做。
01:57:43所以例如。
01:57:44用户可能已经登录。
01:57:46但当然。
01:57:46不应该能够。
01:57:47去编辑帖子。
01:57:48由其他用户撰写的。
01:57:49很有道理。
01:57:51所以。
01:57:52单靠身份验证。
01:57:52是不够的。
01:57:53通常你需要。
01:57:54授权。
01:57:55此外。
01:57:57这就是。
01:57:57这个 Bouncer 包。
01:57:58似乎很有用的地方。
01:58:01因为这样。
01:58:01你可以创建。
01:58:02所谓的策略。
01:58:03看起来是这样。
01:58:04让我们看看。
01:58:05策略是一个类。
01:58:07它扩展了。
01:58:07一个类。
01:58:08来自 Adonis。
01:58:09来自 Bouncer。
01:58:10包。
01:58:11然后我们可以定义。
01:58:13好。
01:58:13所以我们基本上。
01:58:15在这里定义权限。
01:58:16例如。
01:58:17允许编辑。
01:58:18如果用户 ID。
01:58:20与 ID 匹配。
01:58:21用户的 ID。
01:58:22帖子的作者。
01:58:23我们正在尝试编辑的。
01:58:24删除也是一样。
01:58:26然后我们可能。
01:58:27应用这些策略。
01:58:29让我们看看。
01:58:30我们。
01:58:30我们有它们。
01:58:34添加控制器方法。
01:58:43我们在哪里应用它。
01:58:44噢对。
01:58:45这里。
01:58:45所以。
01:58:46在我们的控制器中。
01:58:48我们。
01:58:48这里。
01:58:49他们添加了一个新方法。
01:58:50一个编辑方法。
01:58:51显然应该被触发。
01:58:52显然。
01:58:52如果你喜欢。
01:58:53尝试提交表单。
01:58:54你想要编辑一个帖子。
01:58:56或者你。
01:58:57可能想要加载页面。
01:58:58用于编辑帖子。
01:58:59现在我回过头来看。
01:59:01在这个方法里。
01:59:02我们。
01:59:03找到那个帖子。
01:59:04你想要编辑的。
01:59:05但是呢。
01:59:06在返回视图之前。
01:59:08这个 Bouncer 包。
01:59:09给我们一个 Bouncer 对象。
01:59:11我们可以在这里加载。
01:59:12一个策略。
01:59:13我们想要应用它。
01:59:14基本上。
01:59:15通过该策略进行检查。
01:59:16看看用户。
01:59:17尝试编辑这个的人。
01:59:19是否被允许这样做。
01:59:23有道理。
01:59:25好的。
01:59:27所以让我们看看。
01:59:29聊天。
01:59:30AI 可以构建一个应用程序。
01:59:31用 10 种不同的方式。
01:59:32在同一个库中。
01:59:33所以也许专注于。
01:59:34编程范式。
01:59:35关于如何组织代码。
01:59:37是的。
01:59:38当然。
01:59:38我认为。
01:59:39这些是重要的技能。
01:59:41对于开发人员。
01:59:42和 AI 来说。
01:59:44通过文档。
01:59:45通过技能。
01:59:46你教授的。
01:59:47特定的模式。
01:59:48你知道的。
01:59:49你的基础知识。
01:59:50等等。
01:59:51这就是为什么。
01:59:52我也在计划。
01:59:53编程基础课程。
01:59:54系统设计。
01:59:55以及所有这些有趣的内容。
01:59:55这是更好的选择。
01:59:57去做后端 Web API。
01:59:58使用强类型语言。
02:00:00使用强类型语言
02:00:01例如 C#
02:00:02不确定。
02:00:03为什么在这里被审查了。
02:00:03或者依赖 Node.js。
02:00:05使用 TypeScript。
02:00:06其中的类型会被擦除。
02:00:07而且有大量的漏洞。
02:00:08在 NPM 包中。
02:00:09所以最终。
02:00:12关于后端语言。
02:00:13我可以谈上一个小时。
02:00:15只谈这些。
02:00:16但我没有一个小时。
02:00:17但是。
02:00:18Node.js。
02:00:20以及整个 JavaScript。
02:00:21生态系统的。
02:00:22巨大优势。
02:00:23当然是。
02:00:24AI 非常了解它。
02:00:24如果你打算使用 AI。
02:00:26进行开发。
02:00:28那里有大量的包。
02:00:28但当然。
02:00:30存在巨大劣势。
02:00:31但当然。
02:00:32有着严重的供应链攻击风险。
02:00:33当然。
02:00:34其他语言。
02:00:35也能给你更好的性能。
02:00:36我绝对。
02:00:38会考虑使用。
02:00:39Go。
02:00:41Rust。
02:00:41C#。
02:00:42任何你感兴趣的。
02:00:42后端开发也是如此。
02:00:44但我会说。
02:00:45我的经验是。
02:00:46你有很高的效率。
02:00:47当使用。
02:00:48TypeScript 时。
02:00:49因为有 AI。
02:00:50它非常了解它。
02:00:51当然。
02:00:52对我来说。
02:00:53我非常了解它。
02:00:55所以我。
02:00:56可以快速看到。
02:00:56AI 出现错误方向的部分。
02:00:57等等。
02:00:58所以。
02:00:59是有这个原因的。
02:01:01但是。
02:01:02是的。
02:01:02当然。
02:01:04有。
02:01:05很大的价值。
02:01:06在使用。
02:01:06其他后端语言。
02:01:06方面。
02:01:07并不一定。
02:01:08是 JavaScript。
02:01:09做一切事情。
02:01:10为什么 NestJS。
02:01:11更受欢迎。
02:01:12非得什么都用 JavaScript。
02:01:14我从来没有完全理解。
02:01:16为什么不。
02:01:17为什么 Adonis。
02:01:19是。
02:01:19而且如此小众。
02:01:22我。
02:01:23我。
02:01:24我没有好的解释。
02:01:24对于这一点。
02:01:25我对此并无确切的解释。
02:01:26原因就在于此。
02:01:28必须离开了。
02:01:29所以。
02:01:30这很有趣。
02:01:31那很有趣。
02:01:32绝对很有趣。
02:01:32去深入了解 Adonis。
02:01:34并看一看它。
02:01:35希望你也喜欢它。
02:01:36带大家看一看。
02:01:37希望你们也喜欢。
02:01:39感谢收看。
02:01:40每周四回来。
02:01:40下周。
02:01:41我将无法参加。
02:01:42再下一周。
02:01:43我得看看。
02:01:45但是是的。
02:01:46我总是会宣布它。
02:01:47在我的 Discord 中。
02:01:48所以如果你还没有。
02:01:48参与其中的话。
02:01:49你可以前往。
02:01:50akadamind.com
02:01:51/community。
02:01:51加入 Discord。
02:01:53我会在那里宣布。
02:01:54或者当然。
02:01:54简单地订阅。
02:01:56关注我。
02:01:56然后你就会看到它。
02:01:57但是是的。
02:01:58周四。
02:01:59下午 5 点。
02:02:00欧洲中部。
02:02:01夏令时。
02:02:03我通常结束。
02:02:04在现在。
02:02:05我结束的时间。
02:02:06是的。
02:02:07所以谢谢大家。
02:02:09祝大家。
02:02:09度过愉快的一天。
02:02:11晚上。
02:02:13早上。
02:02:13无论你那里是什么时间。
02:02:14无论你那里是何时。
02:02:15希望能与你们在未来相见。
02:02:17是的。
02:02:17谢谢大家加入。

Key Takeaway

AdonisJS 提供了一个功能完备、依赖集有限且后端优先的开发环境,通过内置的 ORM、身份验证和模板引擎,为构建全栈 Web 应用提供了一种整合度极高的替代方案。

Highlights

  • AdonisJS 是一个后端优先、类型安全的全栈框架,其理念类似于 JavaScript 生态中的 Laravel。

  • AdonisJS 内置了身份验证、文件上传、缓存、速率限制和对象关系映射(ORM)功能,减少了对第三方依赖的需求。

  • 该框架支持三种前端构建方式:Edge 模板引擎驱动的超媒体应用、配合 React 使用的 Inertia.js 以及纯 REST API 开发。

  • 使用 Lucid ORM 可以通过模型类(Class)来表示数据库表,从而避免手动编写 SQL 查询。

  • AdonisJS 项目的配置由 config 目录、.env 环境变量和 adonisrc.ts 框架配置文件三部分组成。

Timeline

AdonisJS 框架概述与核心设计理念

  • AdonisJS 是一个后端优先且具备全栈能力的 JavaScript 框架。
  • 框架的设计哲学强调“内置电池”理念,自带身份验证、ORM 和缓存等功能。
  • AdonisJS 极力精简外部依赖集,旨在降低供应链攻击的风险。

AdonisJS 被定位为 JavaScript 界的 Laravel。它不仅能渲染视图,还为构建 Web 应用程序提供了后端核心构建块。与专注于服务器端渲染的 Next.js 不同,AdonisJS 提供了完整的开箱即用体验,无需配置大量第三方库。

前端构建模式与路由处理

  • 框架支持三种前端构建路径:超媒体、Inertia.js 和纯后端 API。
  • Edge 是 Adonis 自带的服务器端模板引擎,适用于构建多页应用程序。
  • Inertia.js 充当了单页应用与全栈后端之间的桥梁,允许使用 React 构建前端。

开发者可以根据应用需求选择渲染方式。超媒体路径通过 Edge 模板引擎发送完整 HTML,交互性可通过 Alpine.js 或 HTMX 增强;而 Inertia.js 允许开发者在服务端渲染 React 组件,无需手动处理复杂的客户端数据获取。

项目结构探索与初始设置

  • npm create adonisjs@latest 命令用于快速创建项目结构。
  • config 文件夹用于存储应用配置,start 文件夹用于存放启动生命周期文件。
  • routes.ts 文件显式定义了路由与控制器方法的对应关系。

项目结构模仿了 Laravel 的设计模式。resources 目录存放资源,database 目录包含迁移脚本,Ace 命令系统则用于运行迁移和生成模型。框架通过服务提供程序处理依赖注入和应用启动时的资源初始化。

数据模型、工厂与迁移

  • 迁移(Migrations)是数据库架构变更的蓝图,模型(Models)定义数据交互方式。
  • Ace 工具可以自动生成模型及其对应的迁移文件。
  • 工厂(Factories)和填充程序(Seeders)用于快速生成模拟数据以供测试。

开发者通过 node ace make:model 命令生成模型和迁移。数据结构定义在迁移文件中,通过“向上”和“向下”操作来创建或回滚表结构。模型利用装饰器定义表间一对多的关联关系,实现数据的查询与管理。

控制器逻辑与访问控制

  • 控制器处理 HTTP 请求上下文,并与 ORM 模型进行交互。
  • Bouncer 包提供了基于策略的授权机制,用于限制用户权限。
  • 验证工具使用 Vine 库定义输入数据规则,错误提示会自动处理。

控制器方法接收 HTTP 上下文,处理请求数据并渲染视图。通过应用策略,框架可以精细控制用户对特定资源的读写权限。验证逻辑被高度抽象,确保了后端数据的一致性和安全性。

Community Posts

No posts yet. Be the first to write about this video!

Write about this video