时不时地,出现熟悉技术的新竞争者。 当然,这也发生在 Node.js 上。 最有希望的例子之一是 Deno,老实说,它似乎是正确的例子。
在本文中,我们将讨论 Deno 是什么,将其与 Node.js 进行比较,并使用 Deno 和 Fresh 构建示例 Web 应用程序。 Web 应用程序将包含 REST API 和 Web UI 的设计。 我们将描述 Fresh 的一些机制和数据库的(穷人)解决方案,创建一个前端,并展示正在运行的应用程序。
此演示示例 可在 GitHub 上获得 ,其灵感来自 Angular Tour of Heroes 示例。
什么是德诺?
比较 Deno 和 Node.js
什么是新鲜?
比较基于 Node 和 Deno 的架构
使用 Fresh 构建简单的 Web 应用程序
了解 REST API
设置数据库
创建我们的前端
Deno 是一个 TypeScript 和 WebAssembly 运行时,它为 Node.js 的一些老问题提供了革命性的方法。 Deno 的开发特别开始克服 Node 的一些限制,这些限制由其设计师 Ryan Dahl 确定,并在他 的 2018 年关于 Node.js 的 10 件事 中进行了描述。
Ryan 开始使用 Rust(Node 是用 C++ 编写的)从头开始构建 Deno。 Deno 使用 Google 的 V8 JavaScript 引擎,类似于 Node.js。
很明显,这两种产品都有共同的根源和相似的名称 ——你注意到“Deno”只是“Node”的字母混杂了吗? Deno 也可能意味着“DEstroy NOde”。
由于 Deno 原生执行 TypeScript 代码,因此比较 Node 和 Deno 有点不公平。 最好将使用 ts-node 模块的 Node 与 Deno 进行比较,以实现类似的功能。 另一方面,Node 和 ts-node 将需要更多的模块安装和转译时间( .ts至 .js在 Node 中执行)。
Deno 模块被设计成没有外部依赖,破坏了著名的 依赖地狱 。 举个例子, 这个工具 可以帮助我们了解我们需要下载多少个包 node_modules使用非常标准技术的项目的目录。
Node 和 Deno 的另一个显着区别是 Deno 不使用 npm 生态系统。 在 Deno 中,依赖关系只是通过寻址存储我们想要包含在代码库中的 TypeScript 文件的存储库来处理的。
了解这两个生态系统之间的许多其他细微差异。 您可以在此 Stack Overflow 答案中
超过 20 万开发人员使用 LogRocket 来创造更好的数字体验 了解更多 →
在 Deno 提供的其他标准库中,我们有 Fresh 。
Fresh 是一个全栈的现代 Web 框架,旨在与其他广泛使用的 Web 框架(如 Angular、Next.js 等)相媲美。 它提供基于文件系统的路由(类似于 Next.js),并有一个模板引擎,允许我们快速组装 Web 应用程序 UI。
Fresh 使用 Preact 在服务器和客户端渲染页面,这取决于我们需要的组件类型。
网页由交互岛屿组成。 对于这些岛屿,渲染(及其背后的逻辑)在浏览器中执行。 这实现了最大的交互性,而我们 Web 应用程序的其余组件都在服务器上呈现。 只有最简单的 HTML 会传递给浏览器。
上图显示了 Node、Deno、ts-node、Angular 和 Fresh 之间的架构比较,以获得更好的上下文。 请注意,这有更多的层次,甚至在浏览器中运行的代码和在服务器上运行的代码之间的分离也可能是模糊的。 这纯粹是比较不同组件的简化版本。
在接下来的几节中,我们将分析 来自此存储库的代码 。 在架构上,代码由三部分组成:REST API、一些数据和前端(有人说是 MVC 吗?)。
该项目开始于 deno task start我们可以导航到 http://localhost:8000查看示例应用程序。
不要错过 The Replay 来自 LogRocket 的精选时事通讯
了解 LogRocket 的 Galileo 如何消除噪音以主动解决应用程序中的问题
使用 React 的 useEffect 优化应用程序的性能
之间切换 在多个 Node 版本
了解如何 使用 AnimXYZ 为您的 React 应用程序制作动画
探索 Tauri ,一个用于构建二进制文件的新框架
比较 NestJS 与 Express.js
UI 并不是很重要,它足以解释 Deno 和 Fresh 中的一些有趣点。 UI 还允许我们将英雄添加到列表中并滚动浏览。 添加新英雄时,列表会动态更新。
如上所述,REST API 由两个文件提供( /routes/heroes.ts和 /routes/heroes/[id].tsx)。 我们可以比较下表中的功能差异:
功能说明 | 请求类型 | 在哪里实施 |
---|---|---|
获取数据库中所有英雄的列表 | 得到 | /routes/heroes.ts |
使用请求正文中传递的名称创建一个新英雄 | 邮政 | |
获取给定英雄的详细信息 | 得到 | /routes/heroes/[id].tsx |
更新给定英雄的详细信息 | 放 | |
删除给定的英雄 | 的 |
您可能会问是否有理由使用两个不同的文件来实现类似的功能。 就在这里!
例如,两个 GET 可能会或可能不会接受查询参数。 如果未指定查询参数,则该方法的调用将返回整个英雄列表。 通过为 id参数(即 id英雄),GET 方法将只返回单个对象。
在同一个文件中可以有相同的逻辑。 然而,为了清楚起见,我将它们分成两个文件来展示 Fresh 中动态路由的机制。
文件名 /heroes/[id].tsx看起来有点奇怪。 这是因为它不仅代表了匹配单个静态路径的可能性,还代表了基于模式匹配一大堆不同路径的可能性。 这是从 Next.js 借来的机制。
这样,我们就有了一个代码,它将为所有匹配的 URL 提供服务 http://localhost:8000/heroes/:id图案。
当我们不指定参数时 http://localhost:8000/heroes,我们得到了存档中当前英雄的完整列表。 此特定 URL 在文件上路由 /routes/heroes.ts.
在存储库中,我们还可以找到 deno.postman_collection.json包含所有请求的文件。 在每个请求中都有一个示例,用于测试 REST API 的每个调用,而无需编写代码(该文件为 Postman 格式,但易于理解)。
为了保持 UI 简单,并非 REST API 的每个功能都绑定到一个按钮。 在我们的例子中,我们喜欢有一个完整的 REST API 来提供一个完整的例子。
为了使这个项目尽可能简单,我们采用了最简单的数据保存解决方案:将数组作为全局变量。
这意味着一旦我们重新启动应用程序,我们将丢失数据的状态。 对象 hero 和全局对象数组的接口位于 /data目录。 没有什么非常令人兴奋的讨论,但一般来说,这将是初始化与适当数据库的连接的地方。
我们项目中最重要的部分是前端! 它由文件实现 index.tsx(这实际上将使用岛屿内的组件 islands目录)和 [serverparam].tsx.
首先,我们来讨论一下 [serverparam].tsx文件。
从名称中,我们可以猜测它会提供动态路由。 在 URL 的路径部分成为参数之后立即传递所有内容 serverparam.
代码本身并不复杂——它只会打印一些字符串。 代码在服务器上执行,只有最终的 HTML 返回给客户端:
return ( <div> Hello {capitalizeFirstLetter(props.params.serverparam)}!<br /> The server time is: {new Date().toDateString()}<br/> The server platform is: {platform() } </div> );
要测试它,只需转到 http://localhost:8000/rosario并接收消息:
该页面在服务器上呈现的证据是,通过从另一台具有另一个操作系统的计算机在同一页面上导航,它将产生完全相同的字符串(特别是关于服务器操作系统的字符串)。
上图显示了我家庭网络上 Linux 机器的准确输出。
现在让我们专注于 index.tsx文件,应用程序的主界面。 它使用 REST API 和数据。 正如预期的那样,代码非常简单:
export default function Home() { return ( <div class={tw`p-4 mx-auto max-w-screen-md`}> <h1 class={tw`text(3xl blue-500)`}>Tour of Heroes</h1> <HeroesScroller start={1} /> <br/> <Create></Create> </div> ); }
来源是 .tsx,因此它将在需要时使用与 HTML 混合的 TypeScript 语法。
在上面的片段中,我们可以看到 index.tsx文件是结合一个标题和两个组件: HeroesScroller,它也接受一个参数,另一个组件称为 Create.
在讨论岛屿的概念之前,请注意样式是如何使用的 twind. 当我们使用以下命令在 Fresh 中创建新项目时,默认使用此功能:
> deno run -A -r https://fresh.deno.dev NameOfTheProject
现在是时候谈谈这些岛屿了。 这是在浏览器上实际执行的代码。 项目中有一个特殊的目录叫做 /island并且该目录中的文件的处理方式不同。 我们可以通过运行项目看到这一点:
> deno task start Task start deno run -A --watch=static/,routes/ dev.ts Watcher Process started. The manifest has been generated for 4 routes and 2 islands. Server listening on http://0.0.0.0:8000
我们可以看到Fresh已经扫描了根目录,发现了四个路由: index文件,我们上面讨论的 REST API 的两个文件,以及 [serverparam].tsx文件。 连同文件,它还发现了两个孤岛(文件在 /islands目录)。
选择“岛”名称是为了强调我们想要在浏览器中实现和运行的交互性实际上是如何隔离在这些特定文件中的。 从技术的角度来看,我们编写的 TypeScript 文件包含在 islands目录被当场转译为 JavaScript 以在浏览器中执行。
这在官方文档中被称为“在客户端上重新水化岛屿”。 总体来说对发展影响很大。
Deno 和 Fresh 的结合是开始享受开发中小型 Web 应用程序项目的绝佳机会。
npm 的缺乏及其(臃肿的)生态系统与整个开发过程的速度一起是一个明显的优点。
另一个巨大的好处是将 TypeScript 选择性地转换为 JavaScript。 在交互性的岛屿上,它是开发生命周期中的一股新鲜空气(你看到双关语了吧?)。
Deno 模块利用了开发者社区的丰富经验。 他们可以真正无所畏惧地使用它们。 最后,新模块由 Deno 核心团队审核,这在一定程度上是质量的保证。
LogRocket 是一个前端应用程序监控解决方案,可让您重现问题,就好像它们发生在您自己的浏览器中一样。 无需猜测错误发生的原因,或要求用户提供屏幕截图和日志转储,LogRocket 可让您重播会话以快速了解问题所在。 无论框架如何,它都可以完美地与任何应用程序配合使用,并且具有用于记录来自 Redux、Vuex 和 @ngrx/store 的附加上下文的插件。
除了记录 Redux 操作和状态之外,LogRocket 还记录控制台日志、JavaScript 错误、堆栈跟踪、带有标头 + 正文的网络请求/响应、浏览器元数据和自定义日志。 它还检测 DOM 以记录页面上的 HTML 和 CSS,即使是最复杂的单页和移动应用程序也能重新创建像素完美的视频。