介绍 Oats~i — 开放 Web 框架
Oats~i 允许您在一个开放的生态系统中构建强大的 Web 应用程序。

我是一名活跃的Web开发者,至今已有大约五年时间。早在2019年底,当我正式开始Web开发时,我身边就围绕着一个庞大的Web开发工具生态系统,我可以利用它们为客户和我的任何个人项目创建网站。
但我发现自己走了“自己动手”的路线。不是因为我习惯了痛苦和挠头时刻,而是因为我想从基础学习Web开发,而不是直接跳入一个框架并在此基础上建立我的知识。
此外,这也是大多数经验丰富的Web开发者所建议的。学习HTML、CSS和原生JavaScript,在此之上的任何东西都将是小菜一碟(差不多)。
嗯,五年过去了,不知怎的,我最终还是制作了自己的Web框架。最初只是一个简单的学习Web和Web API如何工作的练习,最终却变成了一个成熟的项目,其中包含了无数的挠头时刻、失望和顿悟。
隆重推出 Oats~i,一个开放的Web框架,它还能让你回归基础。Oats~i 提供了一个结构,允许你使用 HTML、CSS 和原生 JavaScript 创建 Web 应用程序,并具有强大的可扩展性、服务器端渲染、基于同意的路由、通过数据管理器、视图管理器和钩子实现的响应性、基于片段的视图系统、支持额外布局、弹出窗口和基于片段的自定义视图的视图面板、支持“原生”Web 浏览功能(如参数、查询和目标)、分页、代码分割以及 JavaScript 和视图包的延迟加载。
所有这些都是框架原生提供的,运行在 Webpack 之上,作为首选的模块打包器。
Oats~i 不关心你的服务器环境,因为它是一个纯粹基于客户端的系统。服务器上没有运行 JS,因此部署 Oats~i 应用程序不需要额外的特殊服务器设置。
但在我们深入细节之前,它现在在哪里运行呢?
在这里:https://www.vertesolutions.com
该网站是一个生产网站,为一个从事生态咨询和生态业务的客户提供服务。这家公司名为 Verte Environmental Solutions,所以如果我提到“Verte的网站”,我指的就是这个网站。
Oats~i 目前只在这一处运行。
编辑:源代码也已公开。https://github.com/Oats-i/Oats-i
在此之前,我一直在网站的管理员面板(这是定制的)上开发该框架,多年来一直在测试、更新和添加新功能。所以,如果客户端网站上缺少我在这里谈论的一些内容,请务必相信它们正在管理员面板上运行。
此外,我正在介绍的是一个相当完善的框架,有几个功能需要讨论。所以请注意,这将是一篇很长的文章。我已经尽力编辑它以使其更短。然而,我通过这篇介绍触及了其核心。其余部分只是浅尝辄止,我们将在未来的文章中深入探讨。
所以,介绍到此为止。
让我们更深入地了解 Oats~i,以及我目前为止的构建方式,看看它开箱即用的一些功能以及我未来的计划。
一个开放的Web框架
称 Oats~i 为开放式 Web 框架,我的意思是 Oats~i 是一个简单但可扩展的框架,其代码可以用简单的 HTML 和原生 JavaScript 编写,当然 CSS 是事实上的样式工具。通过这种简单的设置,您可以添加自己的、第三方或自定义的模板引擎、CSS 库和其他工具,只要 Webpack 允许并且您可以配置它。
基于片段的系统
Oats~i 通过一个构建系统工作,该系统将片段生成为 Web 应用程序的“组件”或核心部分。以这些简单的视图为例


两张图片都包含应用程序的“根视图”,这是用户始终会看到的主要视图。然后,其他视图(片段)将动态地在其中渲染。


根视图可以包含主要导航链接或按钮,以及用户始终会在应用程序上看到的其他视图,并且通常不会改变。
根视图中的其余视图将根据用户的路由进行更改,这将基于片段的加载和卸载。片段经历一个构建过程,该过程主要获取要渲染的视图,将其放置在目标父节点中,然后允许您连接应用程序和业务逻辑的其余部分。
Oats~i 构建过程通常会在您的片段中触发以下核心方法
//gets the view for your fragment async initializeView(cb){ } //triggers after the fragment view has been attached to the DOM onUIBind(serverSideRendered){ } //triggers after the fragment has completed building and anytime its rebuilt for the same route async onQueryParamsDataUpdate(changedParams, data, savedState, routeParams, isServerSide){ }
基本上就是这样。
有了这样的骨架结构,您就拥有了几个灵活性,例如:
渲染简单的HTML或使用模板引擎(您选择的)加载复杂视图
您重写的第一个方法(initializeView())可以这样完成:
async initializeView(cb){ const viewAsString = `<p class="text">My view</p>`; this.onViewInitSuccess(viewAsString, cb); }
我们以 HTML 字符串的形式获取视图,并将其传递给内部方法 (onViewInitSuccess()),该方法也接受传递给原始方法的回调。
调用 onViewInitSuccess() 会触发构建过程继续执行后续步骤。
在 JS 中将 HTML 写入字符串很简单,Oats~i 也允许这样做,但它常常会变得有问题。然而,Oats~i 不会为编写 Oats~i 的视图构建新的语法或系统,而是允许您插入最适合您用例的任何模板引擎,在您的 webpack 配置中将其连接起来,并让它发挥作用。
以 Verte 的情况为例,我使用 handlebars,结合 handlebars-loader 来编写 hbs 格式的独立视图文件,并简单地在我的代码中引用它们。
所以,不是这样
const viewAsString = `<p class="text">My view</p>`;
我的视图现在提供如下
const viewAsString = require("./relative/path/to/view.hbs")(myTemplatingData);
例如,如果我想改用 ejs,我只需更新我的 webpack 配置并为该用例使用正确的导入语法。
Oats~i 只关心传递给它的视图是一个 HTML 字符串。
网络源视图
Oats~i 甚至允许您通过网络获取视图。这部分解释了 initializeView() 方法中存在 async 的原因。
Oats~i 还期望您在此阶段可能会进行网络调用,无论是基于用户类型或其他因素的完整视图,还是根据您的视图和业务逻辑获取模板数据。
您在这里做什么完全取决于您的业务和技术原因。
注意:构建系统不等待构建阶段的 promise 使用 await 或 then() 来解决,而是使用传递给相关方法的回调,这有一个很好的理由。当我们深入研究 Oats~i 如何工作时,在后面的文章中会很清楚。
Vanilla JavaScript 或兼容的 JS 库用于应用程序或业务逻辑
Oats~i 代码是用原生 JavaScript 编写的,这是 Web 浏览器理解的“原生”语言。但是,在编写业务逻辑时,您可以拥有一些灵活性。
例如,出于任何原因,您可以在项目中引入 jQuery,并用它来编写部分逻辑。我很久以前就这样做过,甚至在 Oats~i 构建到目前状态之前,为 Verte 网站的平滑滚动效果编写了大约五行代码。(TLDR,我懒得思考 Stack Overflow 之外的东西,哈哈)。
理论上,您可以在 TypeScript 环境中使用 Oats~i,但我尚未测试。我唯一使用 TypeScript 的地方是它的类型系统,结合 JSDoc,用于在框架内记录类型,这种方法我在一段时间前已经记录过。
您可以在此处阅读有关将 JSDoc 和 TypeScript 集成用于类型目的,而无需构建过程。
代码分割和按需加载
Webpack 是一个强大的 Web 开发工具,它允许进行大规模复杂的项目配置,为开发团队提供了构建满足其独特规范的项目所需的灵活性。Oats~i 运行在 Webpack 之上,框架主要依赖 Webpack 的代码分割和延迟加载功能来支持异步片段块和捆绑包。
这意味着您的片段可以加载在一个捆绑包中,或者使用 webpack 分割成多个块,从而优化 Oats~i Web 应用程序的初始加载速度。如果您的应用程序需要,可以将其与网络源视图结合使用,通过多种方式优化 Oats~i 中的应用程序,以确保在加载时间方面获得最佳用户体验。
使用 Webpack 进行高级项目配置
也许将 webpack 作为 Oats~i 基础的最大优势在于它提供了大量的配置,让您可以根据需要定制您的应用程序。
这就是为什么您可以设置适合您视图渲染过程的模板引擎,为您的应用程序配置 babel 和其他加载器/插件,并简单地构建一个完全符合您的项目特定要求的应用程序。
Oats~i 运行一个简单的基本 webpack 配置,该配置设置了 handlebars-loader、html-loader、css loader、asset loader 和 HTMLWebpackPlugin 来创建您的服务器端视图或模板。使用 webpack-merge,您可以扩展这些配置并按照您希望的方式架构您的 Web 应用程序。
这使得 Oats~i 的工作方式很像一个即插即用系统。它给您一个骨架,您可以根据需要围绕它封装和配置您的应用程序。
路由
路由是 Oats~i 中的一个默认功能。事实上,要运行应用程序,您必须提供应用程序将用于初始化自身以及管理用户导航和片段渲染的路由信息。
简单的路由信息如下所示
Const MyRoutingInfos = [ { route: "/my-route", target: myMainFragmentBuilder, nestedChildFragments: [ myNestedChildFragmentBuilder ] } ]
当 Oats~i 从服务器加载时,它会检查当前 URL 并在提供的路由信息中找到匹配项。在 Verte 的案例中,当您加载“/”时,Oats~i 会搜索具有该路由匹配项的路由信息,然后按从“target”到每个嵌套子片段的顺序展开片段。
您还可以提供一个默认路由,Oats~i 将尝试从该路由启动应用程序,除非客户端已从您的路由信息中给出的有效路由获取页面。
路由中的参数
Oats~i 还支持在路由中使用参数,使用 Express 中常用的冒号语法。
因此,定义为 /:myParams 的路由是有效的,并且将映射到 /user-1、/user-2、/user-3 等路由。
Oats~i 更进一步,为您解析这些参数。
在设置片段时,您可以选择设置它应该注意的参数。参数的名称应与您的路由信息中使用的名称完全匹配。
构建片段时,Oats~i 将解析值,注意任何更改,并向您的 onQueryParamsDataUpdate() 方法传递两个参数。它们是所有已更改的被监视参数的对象,以及所有被监视参数的当前值。
因此,如果您有一个显示用户信息且定义在 /:userID 路由下的片段,并且客户端首先导航到 /user-xyz,您将能够读取 userID 的值为 user-xyz。如果客户端再次路由,这次路由是 /user-abc,您将立即知道 userID 的值已更改为 user-abc,并且您可以适当地响应。
查询支持
查询也是 Web 浏览和 URL 的核心部分。Oats~i 也会为您解析查询,只要您告诉片段使用它们的键来监视它们。
例如,如果您的路由 /:userID 映射到 /user-3?promptUpgrade=true,并且您在片段中指定要监视键为“promptUpgrade”的查询更新,那么这些查询也将被解析并发送到 onQueryParamsDataUpdate() 方法。
但是
您不能在路由信息中使用查询来编写路由。只支持参数。Oats~i 会在截断任何查询和目标后,查找给定 URL 的有效路由信息。解析将在之后完成。
Verte 的网站在博客文章页面渲染博客文章视图时已经使用了这种机制。每篇文章的路由都是参数化的,我们只响应被监视参数的更改。
基于同意的路由
这可能是 Oats~i 一个非常独特的功能。基于同意的路由赋予您对用户体验的控制权,允许您在有任何待处理进程的情况下,警告用户离开关键页面,所有这些都由应用程序内控制。
Oats~i 不使用提供的弹出对话框的标准浏览器 API,而是结合 History API 和状态管理来检测弹出或导航,请求当前渲染的片段同意,暂停后续导航尝试,并仅在用户授予权限后继续。
如果用户选择停留在当前视图,Oats~i 会将浏览器的导航路径恢复到原始状态。
当然,每次用户想要在您的应用程序中导航时都让他们点击“确定”是一个糟糕的主意。因此,默认情况下,Oats~i 片段和视图面板(稍后会详细介绍)默认同意导航尝试。
Verte 内部使用此功能在策划博客内容时保护管理员,以防当前草稿尚未在自动保存脚本的时间间隔内被拾取。如果管理员想要离开博客编辑器并且有未保存的草稿,他们将通过对话框收到警告,并选择是继续导航离开还是留在页面上并手动保存他们的工作。
使用视图面板实现弹出窗口、对话框和更多布局
在 Oats~i 中,框架将主要通过片段渲染路由。但是,还有一个名为视图面板的额外实用程序,允许您动态渲染片段可能需要的其他视图。这些包括对话框、汉堡包面板,甚至是带有用户可能需要的定制信息的加载屏幕。
要生成一个视图面板,您必须通过视图面板管理器请求它。Oats~i 会自动管理片段和视图面板的视图,这意味着您无需编写逻辑来将您的主要片段视图绑定到 DOM,或者在视图面板或其关联片段因导航更改而被销毁时将其移除。
由视图面板管理器生成的视图面板也会自动连接到片段的同意路由过程,允许您扩展片段功能。
视图面板还可以监听参数和查询。
路由触发和直接触发的视图面板
视图面板可以通过路由更改触发,也可以通过调用片段的视图面板管理器直接触发。对于前者,这正是您的路由中包含查询并将其链接到片段内的视图面板可以派上用场的地方。
如果您的路由“/:post-id”当前在浏览器中表示为“/nice-post?showComments=true”,您可以使用片段内的路由触发视图面板自动弹出一个侧面板,加载帖子评论并允许用户阅读它们。
此功能通常通过 onQueryParamsDataUpdate() 方法访问。调用 super(如果您已覆盖它)将调用片段的视图面板管理器来尝试渲染任何路由触发的视图面板。
这种设置的最大优点是,您的视图面板的渲染和行为现在与导航绑定,使得用户体验更加自然。
因此,根据我们的示例,如果用户导航到“/nice-post?showComments=true”,阅读了评论,然后点击返回,路由将更改回“/nice-post”,视图面板管理器将注意到此更改,并只要已授予同意,便会自动触发视图面板的销毁过程。
就像片段一样,视图面板默认也授予同意。因此,您应该仅在必要时覆盖同意方法。
响应性和数据管理
一个现代的Web框架,如果没有很好的响应性和数据管理,就不算完整。而这正是Oats~i与其他Web框架之间最关键的区别所在。
Oats~i 不会自动将视图与一段数据或状态耦合。相反,这完全由开发人员根据其应用程序或业务逻辑来完成。
就目前而言,您可以使用 Oats~i 构建一个包含多个静态页面的 Web 应用程序,这些页面在片段和视图面板下渲染,仅此而已。应用程序将正常工作。如果您想添加数据、网络调用和响应性,数据管理器实用程序将涵盖所有内容,并且只在您确定的范围内,而不会影响任何周围的视图或数据。
让我们看看数据管理器及其支持工具:网络接口和视图管理器。
数据管理器
数据管理器是一个 Oats~i 实用程序,它允许您将数据、服务器资源和客户端视图绑定在一起。数据管理器持有一个模型数组,模型是与您的应用程序部分及其选定视图相关联的核心数据块或数据类型。
目前,我将其设计为以嵌套数组的对象形式接受模型,因为这是在客户端和服务器资源之间传递数据的最常见格式(作为 Json)。
因此,一个简单的模型可能看起来像这样
{ my: string, simple: number, obj: { ofArrays: number[], objArrays: { objKey: string }[] } }
数据管理器通过对其模型进行作用域划分来工作。这意味着模型的每一位都可以被视为一个单元,创建一组点分隔的键来定义数据中的特定值或类型。
例如,在上面的示例中,数据管理器将模型分解为以下作用域:“MODEL_ROOT | my | simple | obj | obj.ofArrays | obj.objArrays | obj.objArrays.array.objKey”
这些范围代表
MODEL_ROOT -> { my: string, simple: number, obj: { ofArrays: number[], objArrays: { objKey: string }[] } } my -> string, simple -> number obj -> { ofArrays: number[], objArrays: { objKey: string }[] } obj.ofArrays -> number[] obj.objArrays -> { objKey: string }[] obj.objArrays.array.objKey -> string
您可以将这些作用域视为通往不同数据片段的点分隔路径。
有了这些作用域,数据管理器会为您(开发人员)提供对数据的精细控制,允许您将网络接口或视图管理器分配给这些数据中的任何一个。
让我们浅谈一下这两个是什么。

网络接口
在大多数应用程序(原生或 Web)中,显示给用户的数据来源于外部资源,即服务器。因此,内部模型通常需要一个位于自身和外部资源之间的 API 接口。
在 Oats~i 的案例中,网络接口将执行您所需的 CRUD 操作,以关联数据管理器持有的数据,并确保两端同步。
网络接口被定义为一个包含三个方法的对象
getReqBody()
此方法获取请求的正文和其他数据,例如方法、地址、标头等。
onDataLoadPostProcess()
由于响应数据类型和您的内部模型类型可能不同,网络接口允许您对响应进行后处理,并以数据管理器的模型类型提供最终数据。
onDataLoadError()
此方法允许您在网络调用失败时格式化错误响应。
网络接口作用域
API 设计多种多样,这意味着用于对给定数据执行 CRUD 操作的地址或路由可能不同。
例如,一个社交媒体应用程序可以有不同的 API 来加载所有帖子,并且每个帖子运行独特的 API 来转发、点赞或举报帖子。
假设有这样的架构,在数据管理器中使用作用域允许您为每个作用域指定唯一的网络接口。
例如,您可以为 MODEL_ROOT 网络调用(将加载帖子)、“repost”网络调用以及任何其他可以从数据管理器所持模型的结构中进行作用域划分的调用设置网络接口。
这为您提供了一种全新的数据查看方式,将其从一个具有公共端点的大型资源分解为可以通过数据管理器独立处理的多个数据单元的集合。
需要注意的关键一点是,每个作用域只能有一个网络接口,为模型中的每个作用域数据块创建一个单一的“端点”。
视图管理器
通过网络接口,数据管理器现在可以在其模型和服务器之间保持数据同步。那么,如何向用户显示数据,更重要的是,在数据更改时显示给用户呢?
这就是视图管理器发挥作用的地方。
视图管理器响应数据管理器所持数据发生的变异或更改,无论是通过网络操作还是直接的应用程序内更改。
Oats~i 目前支持两种类型的视图管理器——标准视图管理器和列表视图管理器。
标准视图管理器适用于没有在列表中重复的组件的简单视图。另一方面,列表视图管理器最适合在列表中重复视图组件的“复杂”视图。
无论类型如何,视图管理器都会告知您模型或其作用域类型内的以下更改:
onMutate()
当作用域的数据类型正在改变时,此方法触发
onCommit()
当作用域数据类型的突变已完成并提交时,此方法触发。
onCancel()
当作用域的数据类型发生突变被取消时,此方法触发。
onError()
当作用域的数据类型突变遇到错误时,此方法触发,允许您重试。
还有构建器方法集,它允许您传入一个用数据填充的视图(作为 HTML 字符串)。这些方法还会根据突变情况,告知您视图何时已附加或即将分离。
这三个方法是
inflateRoot()
获取所提供数据的模板视图作为字符串
onViewAttach()
当视图已附加到 DOM 时调用
onViewDetach()
当视图即将从 DOM 分离时调用
您可以在 Verte 网站的博客页面上看到这些互动的结果。
结合构建器方法、根钩子和组件钩子,博客和博客文章片段的数据驱动动态视图可以在我们从网络获取数据时显示加载动画,在失败时显示错误消息,并在网络接口的新数据提交后填充视图。
视图管理器还将拥有组件钩子,可以实现更精细的响应性,以及每个视图节点的元素。
例如,使用模型
{ my: string, simple: number, obj: { ofArrays: number[], objArrays: { objKey: string }[] } }
假设作用域为“MODEL_ROOT”(因此是整个模型)的视图管理器,我们可以假设显示 MODEL_ROOT 作用域数据的主要视图组件内部包含可能显示“my”、“simple”、“obj”中特定数据或 MODEL_ROOT 子作用域中数据的组件。
因此,您可以设置视图的组件或元素来响应这些“子”值的更改。
所有这些钩子方法都会接收视图管理器传递给它们的 viewNode 参数,因此您始终可以参考这些数据更改与哪个视图节点相关联,并根据需要查询其组件。
但是,一旦不再需要这些核心视图元素,您就不必费心移除它们。视图管理器会为您处理。
没有虚拟DOM
Oats~i 不通过虚拟 DOM 运行。相反,片段、视图面板和视图管理器直接使用 DOM API 来插入或移除 DOM 元素。
在将您的视图组件插入 DOM 后,视图管理器将在构建器、根和组件钩子中为您提供其直接引用。因此,您可以直接添加监听器、更改属性或简单地使用直接 DOM API 操作 DOM 元素。
生命周期管理
复杂 Web 应用程序的核心部分是生命周期管理。Oats~i 拥有自己的片段和视图面板生命周期管理过程,其功能扩展到其他实用程序,如数据管理器、视图管理器和远程请求实用程序(数据管理器与网络接口结合使用以进行网络调用的实际实用程序)。
因此,开箱即用,使用 Oats~i 及其核心实用程序将自动为您管理生命周期。
例如,如果您在片段中使用数据管理器进行 CRUD 操作,并且用户导航离开了该片段,数据管理器和远程请求实用程序将能够取消所有网络操作,跳过更新视图管理器,并注销它们,因为您的片段或视图面板不再存在。
监听生命周期事件
作为 Oats~i 开发者,您可以利用片段或视图面板的生命周期管理来创建强大的、感知生命周期的库,这些库将在 Oats~i 环境中良好运行。
您只需使用内部方法获取生命周期对象,
getLifeCycleObject()
并为其附加监听器。这些监听器通常包含四个方法,用于:
onFragmentRunning()
当片段已创建并正在运行时调用
onFragmentCancelled()
当片段的构建被取消时调用
onFragmentDestroyed()
当片段被销毁时调用
onViewReady()
当片段视图已附加到 DOM 时调用
注意:“片段”在这里也适用于视图面板。
您需要关注的主要调用是 onFragmentRunning()、onViewReady() 和 onFragmentDestroyed()。如果您的库添加的功能与 UI 无关,您可以在收到 onFragmentRunning() 调用后启用该库。
如果库操作视图(例如动画库),您可以在收到 onViewReady() 调用后启用其功能。
一旦您收到 onFragmentDestroyed() 调用,就收拾好东西,停止一切。
基于OOP的核心
我们谈论了很多 Oats~i 的一些核心功能,但我们还没有谈论范式。您将如何编写核心 Oats~i 代码?
嗯,Oats~i 是一个基于 OOP 的 Web 框架。这意味着大多数实用程序都以类的形式提供。一个片段是从 AppMainFragment 或 AppChildFragment 类创建的。数据管理器是一个类。视图管理器是类,等等。
我选择 OOP 是因为它具有可重用性、垃圾回收以及在 Oats~i 中管理函数和过程的更清晰方式。
例如,无意冒犯,将片段作为一个类允许 Oats~i 做一些巧妙的事情。如果它确定片段正在被重用,它就不会重建片段类。相反,构建过程会直接触发 onQueryParamsDataUpdate(),并且不会重新渲染片段的主视图或更新 DOM 的该部分,因为这是不必要的。
这样做的一个另一个优点是,您的片段可以在相关的路由调用中保留其部分状态。
例如,在 Verte 的案例中,当您处于渲染博客文章的片段中时,点击“其他故事”列表下的另一篇文章不会重建片段。相反,原始视图保持不变,只有由数据管理器结合视图管理器运行的动态数据驱动视图,根据从 onQueryParamsDataUpdate() 获取的新参数值进行更新。
利用函数式编程
Oats~i 核心使用面向对象编程 (OOP) 并不意味着您完全受限于创建遵循 OOP 范式的库。仅仅让它们感知生命周期就足够了。
这将使它们能够在 Oats~i 渲染和销毁它们时,从片段中捕获和释放资源。
将 Verte 的客户端移植到 Oats~i 时,我正是使用了这种策略来重用我为原始网页编写的一些函数式脚本。
因此,我预计寻求在 Oats~i 项目中使用其函数式脚本的开发者遇到的瓶颈和范式严格性会非常少,只要它们能够感知生命周期。
服务器端渲染(视图和数据)
最后,现代 Web 框架的一个重要部分——服务器端渲染。
Oats~i 原生支持服务器端渲染,无需在服务器上运行 JavaScript。
使用 HTMLWebpackPlugin,您可以将 Oats~i 应用程序中每个片段使用的视图提取到它们自己的 .html/.hbs 文件中,您可以在客户端首次请求页面时将其发送给客户端。
唯一的要求是您从服务器获取的视图结构与应用程序渲染时的结构相同。

但我们还没完。
数据管理器水合
您从服务器渲染的视图很可能代表某种数据状态。Oats~i 如何处理这个问题并从服务器端渲染状态继续呢?
您将理想地使用数据管理器来管理 Oats~i 应用程序中的动态或数据驱动视图。现在,使用它,您可以利用服务器端水合,它使用在服务器的 head 标签中渲染的脚本,帮助数据管理器理解来自服务器的数据状态,保存它,并让附加的视图管理器也根据它更新其状态,然后从那里继续运行应用程序。
它的工作原理如下。
在标记的头部,在服务器端,您可以添加以下格式的脚本
<<span class="hljs-name">script id="hydrator_id"> const DataManagerHydrationInfo = { "info_key": { info: model[] extras: * } } window.DataManagerHydrationInfo = DataManagerHydrationInfo; </script></span>
此脚本为服务器的数据管理器提供了重要信息,使其全面了解或了解数据状态。
每个数据管理器都将有一个“info_key”,它将从中读取其数据状态。一旦您将数据管理器设置为从服务器端进行水合,它将找到您提供的 ID 的脚本,获取公开变量 DataManagerHydrationInfo,并读取“info_key”的值。
此值应为一个数组,理想情况下与数据管理器的模型类型相同。但是,它也可以不同。
这是因为数据管理器运行一个多步骤的水合过程。
验证
从脚本读取可能会有其自身的问题和漏洞。您可以在数据管理器提交数据之前,对水合脚本中存储的数据进行验证检查。
预处理
根据您的业务逻辑和Web应用程序设计,从服务器获取的数据格式可能与您在数据管理器中运行的模型不同。Oats~i的数据管理器在水合过程中运行一个可选的预处理步骤,允许您将水合器中的数据转换为模型的格式。
网络步骤
此步骤允许您谨慎处理在水合脚本中自由发布的数据,这些数据对网络爬虫、机器人和搜索引擎都是开放的。
您可以运行一个可选的网络步骤,获取您的数据管理器模型所需的私有或隐藏数据,但这些数据绝不能被网络爬虫或机器人抓取。
例如,如果您正在对购物车进行水合,您可以让服务器的水合脚本只包含产品的一般信息,以及公共 ID,这些 ID 传递给您的安全后端后,将返回更多秘密信息,您将使用这些信息来为用户结账。
因此,您的水合脚本可以包含与 HTML 中已渲染内容一样基本的信息,让数据管理器立即在内部提交该信息,然后从网络周期获取所有其他内容。
Oats~i的下一步是什么?
如果您能读到这里,干得好,您真棒!这是我尽力将大约四年的工作压缩到一篇小小的“介绍性”博客文章中的最好成果。
Oats~i 对我来说是一个巨大的学习项目,我既焦虑又兴奋地想让技术社区了解它。有很多东西需要解开、教导、学习和调试。
我目前的计划是开源 Oats~i。我正在研究具体细节,希望整个代码库能在未来几天内发布,然后我们就可以深入研究,通过框架构建实际的 Web 应用程序,并对其进行测试。
目前,如果您对 Oats~i 有任何反馈、评论和问题,我将不胜感激。
查看 Verte Environmental Solution 的网站,亲身体验其功能。
我可以在 LinkedIn 上联系到,所以请过来打个招呼。
再见,希望我们很快就能开始使用 Oats~i 进行构建。
编辑:源代码现已公开。https://github.com/Oats-i/Oats-i