你永远不会知道所有事物是如何运作的,但你应该理解系统。— Sviat Kuzhelev

Next.js 是一个强大的用于构建React应用的框架,当与TypeScript结合使用时,它提供了强大的类型检查和工具,可以显著提升我们的开发体验。

多年来我一直在使用 Next.JS,在开发大型可扩展web应用时,我发现它是一个出色的工具,甚至比Create React App 还要好。

在过去几年里,NextJS 不断发展,同时保持了其内部的可扩展抽象。在构建众多应用时,我发现最有价值的是如何正确地抽象你的代码。

找到一个关于构建精通的"Hello World"应用的手册并不是特别困难。尽管它们可能解释得很好,但这类故事中最大的瓶颈是当你需要开始处理真实世界的事物时。

一旦你被要求构建一个带有身份验证、多个API、服务和数据结构的NextJS应用,它与"Hello World"完全不同。

在本文中,我们将介绍如何使用 TypeScript 设置 Next.js 项目,处理导航,管理注册/登录/登出,以及有效地使用SVG资产,这些都将以你编写真实的、生产就绪的应用的方式进行描述。

先决条件

在本文中,我们将尝试复制一个全栈社交阅读俱乐部应用。虽然我们不会涉及任何后端内容,但我们将使用浏览器的 LocalStorageSessionStorage 来假装我们在使用浏览器。

因此,一旦你需要携带后端,你将能够轻松地切换到nodeJS + MongoDB/PostgressDB。无论如何,让我们开始吧。

让我们想象一下,你被要求:

  • 创建必要的页面://feed/signup/signin,/signout/:username/friends
  • 创建导航菜单,考虑响应式设计。
  • 集成用户认证/登出流程,以便我们不会暴露内部社交数据。
  • 添加CRUD操作以管理用户帖子和好友列表。
  • 用基本的颜色主题和整体样式来设计应用;

听起来相当简单?确实如此。我们不需要使用过于复杂的例子来真正触及架构设计。上述需求集是你在真实世界的应用中需要覆盖的内容,所以我们尝试掌握它。

使用TypeScript设置Next.js项目

首先,我们使用TypeScript初始化一个新的 Next.js 项目。打开你的终端并运行以下命令来创建一个新的Next.js项目并进入项目目录:

npx create-next-app@latest my-nextjs-app --typescript
cd my-nextjs-app

通过这个,我们将初始化NextJS框架并在其中添加typescript。它将创建一个Starter Kit环境来开发我们未来的应用。

package.jsontsconfig.json 文件方面没有什么特别有趣的内容需要覆盖,因为它们是开箱即用的,你可以在网上找到关于它们如何工作的必要信息,所以让我们假设你已经知道它们是如何工作的。

我们开始架构设计。

设计应用架构

所以这个故事的第一步是从选择正确的文件夹结构开始。

虽然一些文件夹(如/pages)默认已定义,但我们需要自己设计其余的必要项。但是,我们先讨论pages。

📁 ./app

这是NextJS的核心文件夹,在大多数情况下,它可以保持尽可能简单。大多数时候,这是你想设置核心逻辑的地方。

image.png

  • favicon.ico — 用于浏览器标签视觉设计的.ico文件。
  • layout.tsx — 一个内部NextJS应用,将作为SSR或SSG渲染的入口点,但这不是我们的情况,因为我们现在的目标是CSR。
📁 ./pages

默认情况下,NextJS为我们提供了一个漂亮的页面开发抽象。你需要做的只是用必要的内容填充文件夹。

所以这是我们的设计:

image.png

_app.tsx — 是NextJS项目的主要入口点。它将拥抱整个应用,以便在运行时可以服务。
feed/friends/home/signin/signout/signup.tsx — 是静态页面,将代表需要在屏幕上显示的页面,没有任何动态行为。

[username].tsx — 是我所说的"动态行为"。一旦你有一个或多或少复杂的应用逻辑,你会想要通过相同的静态页面显示动态内容,但考虑到一些用户的ID或其他动态值。

📁 ./components

这里我们可以存储我们应用的每个可重用组件。这是最适合那些可以在应用的几个区域/页面中使用的东西的地方。

image.png

AddPostModal.tsx — 我们稍后将在我们的应用中使用它,允许我们的用户按需添加新帖子。

Header.tsx — 我们可重用的头部,将在web应用设计中扮演关键角色。

NavBar.tsx — 另一个可重用的组件,将代表应用的导航。

📁 ./auth

这个文件夹将拥抱应用的核心逻辑,这将帮助我们识别我们的用户是否登录。

image.png

AuthContext.tsx — 用户的认证信息将存在的地方。我们将使用React Context来管理用户登录系统后的静态数据。

withAuth.tsx — 是一个HOC函数,将帮助在应用中共享用户数据。

📁 ./api

在处理应用的不同层(表现/业务等)时,始终重要的是不要将它们混在一起。

此外,将你的API相关代码移到它自己的文件夹是一个好的做法。

image.png

  • getCurrentUser.ts — 从SessionStorage检索登录用户数据的GET请求。
  • getPosts.ts — 检索用户发布的可用帖子的GET请求。
  • getUsers.ts — 检索系统中所有注册用户的GET请求。
  • setCurrentUser.ts — 用户成功登录/注册后设置用户数据的POST请求。
  • setUsers/setPosts.ts — 用于填充LocalStorage以初始化应用的POST请求。

📁 ./hooks

在React的世界中(特别是说到hooks时),最佳实践始终是不要将UI逻辑与业务逻辑混在一起。在设计你的组件时,总是想办法保持你的UI层尽可能的傻瓜化。

在这种情况下,我们可以将任何异步请求切分下来,将它们放入它们自己的封装作用域中,这样UI组件将保持小巧和清洁。

image.png

  • useDataInitialize.ts — 我们的主要工具,用于初始化应用运行的一组模拟数据。
  • usePosts/useFriends.ts— 这些hooks将使我们有机会以封装的方式检索朋友/帖子API响应。
  • useSignup/useSignin/useSignout.ts — 将帮助我们功能性地管理用户认证流程。
📁 ./helpers

这是一个小工具,可以为任何类型的操作服务于我们的自定义需求。在这个文件夹中,总是好放一些只用于特定用例的东西,比如数据随机化等。

image.png

  • dataRandomizer.ts — 这是我们的主要helper,用随机帖子/用户数据填充应用。
📁 ./utils

这是一个相当标准的文件夹,将通过组合可以在整个应用中多次有用和可重用的东西来帮助提高应用的可重用性。

image.png

  • sortByDate.ts — 帮助按特定时间戳对数据数组进行排序。
📁 ./interfaces

在谈到TypeScript时,你将需要处理类型和接口。在大多数Hello World应用的情况下,它们应该足够小,可以自然地保留在代码中。

然而,在真实世界的应用中,你可能会将它们分开,这样行数会比包含它们时显著减少。

image.png

  • data.ts — 我们的数据结构接口,目前只有一个,当然将来会有更多。
📁 ./static

这个小文件夹将代表你想包含在应用中的任何类型的静态数据,如图标/图片/gif/视频等。

image.png

  • logo.svg — 主应用logo。
📁 ./styles

最后但并非最不重要的是我们熟知的样式文件夹。我鼓励你使用CSS Modules,这样你的代码将保持隔离和不重复。尽管如此,一些代码应该保持集中并在整个站点范围内初始化。这就是存储这些东西的地方,比如globals等。

image.png

  • global.css — 应用的核心可共享样式。

核心架构原则

好的,所以我们在设计应用架构方面做得很好,现在我们可以专注于其他重要的事情,如代码的可重用性和可维护性。

更少的耦合,更多的内聚

image.png

内聚指的是模块/类的元素属于一起的程度,建议相关的代码应该尽可能靠近,所以我们应该争取高内聚,并将所有相关的代码尽可能紧密地绑定在一起。这与模块/类内的元素有关。

耦合指的是不同模块/类之间相互依赖的程度,建议所有模块应尽可能独立,这就是为什么要低耦合。这与不同模块/类之间的元素有关。

将业务逻辑从表示层中分离

很多时候,我看到人们试图在UI组件的范围内混合所有东西。无论是单个还是几个被切分下来,瓶颈仍然是无论你想更新UI还是业务逻辑,你都需要同时处理两者。

只要组件一次包含多于1个事物,你就需要检查/审查/测试每一个。

React带给我们的一个更好的方法来简化代码可维护性是Hooks。当考虑代表组件调用一些API请求时,问问自己是否最好将这段代码拿出来放到不同的文件夹/范围并将其作为隔离的管理?

在那种情况下,我们将有一个看起来更好的UI层:

image.png

...以及业务层:

image.png

现在,注意这里你处理的是API调用 — getUsers()。想象一下,如果你把它的逻辑直接放在这里...呃,在一个地方维护所有这些会有点混乱。

这就是为什么我们之前设计了一个专门的API文件夹来保存我们的核心API逻辑:

image.png

最后是接口...这里你可以看到我们将它们从代码中移出到我们可共享的./interfaces文件夹,这使得API代码现在更加清晰、易于理解和可扩展。

这是编写干净和高效代码的4步最佳实践,一旦你为你的NextJS项目设计了精心设计的抽象。我希望现在你更明白为什么我们需要按照上面讨论的架构进行。

总结

仍然有许多事情需要涵盖,因为设计应用架构不是一天就能完成的任务。一些公司花费数月时间仅仅讨论可能的架构方式,然后才开始设计应用架构。

然而,我希望这个小小的真实世界例子能教会你如何更好地设计你未来的应用,并在开发web应用时给你一种正确抽象的感觉。

首发于公众号 大迁世界,欢迎关注。📝 每周一篇实用的前端文章 🛠️ 分享值得关注的开发工具 ❓ 有疑问?我来回答

本文 GitHub https://github.com/qq449245884/xiaozhi 已收录,有一线大厂面试完整考点、资料以及我的系列文章。


王大冶
68.1k 声望105k 粉丝