无头CMS。 我为什么写我的

大家好!

最近的这篇文章促使我写这篇出版物(我昨天看过)。

讲述无头/内容优先/ api首先等的主要功能。 我不会使用CMS,因为资料已经足够,可能很多人已经熟悉了这种趋势。 我想告诉您为什么以及如何编写系统,为什么不能从现有系统中进行选择,我对以前遇到的其他系统的看法以及所有这些方面的前景。 小说将是很多的(两年之内的材料),但是我会尝试写更有趣和有用的东西。 谁在乎,请在猫下。

总的来说,这个故事很长,我会先讲。 要弄清楚创建自己的引擎的真正原因是什么,或者仅仅是因为没有它,就很难在实地解释我为什么这样做而不是某种方式。

但是首先,我将亲自为我简要写下现代Headless-CMS的主要选择标准,为什么我仍然无法为自己选择现成的解决方案。 只是为了使人们不会阅读很多山毛榉,也不了解最终会听到什么。

简要地说:我希望所有东西都放在一个地方:后面和前面(而不是这个或那个),以及GraphQL-API,以及对数据库进行管理等等,包括“ Make Beautiful”按钮。 我还没找到 我本人也没有这样做,但是总的来说,结果很多,最重要的是,它允许我执行实际的项目。

因此,我的方法很难被称为科学和合理的。 事实是,我通常经常写自己的东西。 我喜欢在这里编程。 两年前(在此之前的8年中),我坐在MODX CMF上(在此基础上,我还发明了许多拐杖)。 三年来,我们开始了一个相当大规模的项目,在我看来,在这个项目下,我可以使用MODX。 但事实证明,我不能...主要原因是这是一家没有任何技术要求的初创公司,每天(每天几次)都会改变和补充很多想法。 现在,每当有新想法时,有必要添加一些新实体,为现有实体注册/更改字段,创建/删除/更改这些实体之间的关系(分别是随着数据库结构的更改),更改这些实体开始需要花费几个小时。 实际上,除了必须在方案中注册这些更改之外,还必须更改数据库(几乎手动),更新API,重写程序代码等。 因此,必须在所有这些方面更新前台。 结果,我决定我们应该寻找一些新的,更方便的东西,这将以某种方式简化这一切。 我将再次澄清,当时我是php后端,所以不要惊讶或大笑,我开始发现各种前端构建器,较少处理器,npm等。 等 但是无论如何,在我们的项目中,渐渐地,react + less出现了前沿,GraphQL上的API以及express上的服务器。

但是,并非所有事情都像现在看起来那样乐观。 让我提醒您,这是两年多以前了。 如果您使用现代JS网络的时间不到两年,建议您阅读这篇文章: N个使用Create React App (habr)的理由 。 简而言之,这太懒了:随着react-scripts的出现,您就不必再去配置webpack了。 所有这些都进入了后台。 善良的人们已经配置了webpack,因此几乎可以保证大多数react项目都可以在其上运行,而最终开发人员则直接专注于对最终产品进行编程,而不是配置大量依赖项,加载程序等。 但这是以后。 在此之前,我只需要配置此webpack,跟踪随他飞奔而来的所有内容的更新,等等。 等 但这只是工作的一部分,本质上只是前端。 而且您还需要一台服务器。 而且您还需要一个API。 而且,您还需要SSR(服务器端渲染),就我所知,它仍然没有提供react-script。 总的来说,一切都比现在复杂得多,没有太多,每个人都竭尽所能。 那我怎么拐杖了...

试想一下:

  • 分别针对前端和服务器的本机Webpack配置。
  • 自己的SSR实现,以便异步可以与react-server一起正常工作,并且样式可以立即准备就绪,并且可以正常索引,并且可以为未找到的页面提供服务器状态。
  • 无redux。 好吧,我不立即喜欢redux。 我喜欢使用本机反应通量的想法(尽管我不得不自己重写一下)。
  • 手动指定的GraphQL方案和解析器,而无需自动部署数据库(API服务器用作MODX站点的中间设备)。
  • 没有react-apollo / apollo-client等 一切都是通过基于自定义通量的浏览器中的访存,获取和请求而独立编写的。

结果:到目前为止,该版本的第一个版本有一个项目,参加人数超过500,并且在这个季节(冬季)每天有1000-1700个独立学生。 正常运行时间2个月。 这是因为在预防性软件更新后,我手动重新引导了服务器。 在重启之前,正常运行时间还要再超过6个月。 但是最有趣的是内存消耗。 目前有将近700 MB的js进程。 是的,是的,我也在这里与您一起笑:)当然,很多。 在此之前,我做了一些预防并改进了该指标。 以前,每个进程总共有1000M +。尽管如此,它仍然有效并且可以忍受。 在11月份Google更改PageSpeed Insights算法之前,该网站的性能指标为97/100。 证明

基于该项目的中间结论,该结论基于没有该项目的情况下进一步开发的系统(该项目被遗忘了):

优点

  1. 通过使用GraphQL,项目API变得更加灵活,并且服务器请求的数量已大大减少。
  2. 该项目可以在npm上访问大量组件。
  3. 通过使用依赖项,git等,项目管理变得更加透明。
  4. 当您不知道可以从该动物园中删除哪些内容而又不带来任何后果(而且您经常在一个站点上看到多个版本的Bug)时,内置的脚本和样式肯定比旧站点上的一堆单独的脚本更令人愉悦。
  5. 该站点变得更具交互性,页面可以正常工作而无需重新启动,返回先前查看的页面不需要重复调​​用服务器。
  6. 按照“编辑您看到的内容和看到的位置”的原则,直接在页面上进行数据编辑,而无需任何单独的管理面板。

缺点(主要针对开发人员)

  1. 一切都很复杂。 真的 将一些第三方开发人员连接到该项目根本不现实。 我自己几乎无法弄清楚它的作用和方式以及我的腿从何而来。 如果您看一下第3页关于透明性的论述,那么透明性仅在于,如果您将某物钩在某个地方,您可以立即看到损坏的内容(脚本无法生成等),但是可以通过提交和差异来查看您可以在哪里找到它。 好吧,如果您设法添加了新的东西并且它起作用了,至少您清楚地知道,是的,一切都很好。 但总的来说,它仍然是地狱般的地狱。
  2. 缓存困难。 后来,我为自己发现了apollo-client。 在此之前,正如我所说,我编写了基于流量的存储。 由于具有这些存储,因此可以从不同的组件获取渲染所需的数据,但是客户端的缓存量非常大(每组典型实体都有其自己的存储库)。 结果,很难验证是否较早地请求了对象(即,向服务器请求以找到它是否值得),所有相关数据是否可用等。
  3. 模式,数据库结构和解析器(用于接收/修改数据的API函数)存在困难。 如我所说,我手动编写了方案,解析器也编写了。 在解析器中,我尝试提供缓存以及对嵌套请求和其他细节的处理。 在那一刻,我不得不深入了解本质和GraphQL程序代码。 好的一面是,我通常对GraphQL的工作原理,优缺点以及如何做得更好的方法非常了解。 不利的一面是,当然,您不能将像阿波罗这样的命令编写的所有便利设施和包子都写在其中。 结果,当我发现阿波罗时,我当然开始非常高兴地使用它们的组件(但主要是在前面,下面我会告诉你为什么)。

总的来说,这个使用过时技术的项目属于我个人的100%开采,因此我有能力放弃它,直到更好的时机。 但是还有其他项目需要我进一步研究和开发平台。 有几次,我不得不从头开始重写所有内容。 此外,我将更详细地讨论我遇到的各个任务,以及由此开发和应用的解决方案。

架构优先。 首先是电路,然后是其他所有电路

站点(Web界面,瘦客户端等)是所有信息的显示(以及信息管理,如果允许并且功能允许)。 但是首先,数据库(表,列等)都是一样的。 在遇到使用数据库的几种不同方法后,我最喜欢使用“模式优先”方法。 也就是说,您描述了实体和数据类型的架构(手动或通过接口),部署了架构,然后立即在数据库中描述了更改(创建/删除了表/列,以及它们之间的关系)。 根据实现的不同,您还将生成用于管理此数据的所有必需的解析器功能。 在这个方向上,最重要的是我喜欢prisma.io项目。

在您的允许下,由于即使在轮毂上我也没有看过任何有关棱镜的文章,因此我会引起他们的注意,因为该项目确实非常有趣,没有它们,我现在将没有一个让我如此高兴的平台。 。 实际上,这就是为什么我将平台命名为“ cornera-cms”的原因,因为prisma.io在其中扮演着非常重要的角色。

实际上,prisma.io是一个SaaS项目,但有一个很大的警告:他们几乎将所做的所有事情都放在github上。 也就是说,您可以以非常合理的价格使用他们的服务器(并在几分钟之内为自己配置自己的数据库和API),或者可以在家中完全部署所有内容。 在这种情况下,棱镜应在逻辑上分为两个重要的独立部分:

  1. Prisma服务器,即数据库也在旋转的服务器。
  2. 棱镜客户端。 它本质上也是服务器,但相对于数据源(棱镜服务器),它是客户端。

现在,我将尝试解释这种令人困惑的情况。 通常,棱镜的本质是使用单个API端点,您可以使用不同的数据源。 是的,在这里任何人都会说他们都提出了GraphQL,而在这里不需要不需要prisma。 总的来说,每个人都是对的,但是有一个重要的观点:GraphQL仅定义原理和整体工作,但是就其本身而言,它不能为最终数据源提供现成的工作。 他说:“您可以创建一个API来描述用户可以发送的请求,但是如何处理这些请求则取决于您自己。” 当然,棱镜也使用GraphQL(顺便说一下,还有很多其他东西,包括各种阿波罗产品)。 但是,对此的“棱镜”仅提供了与数据库一起使用的功能。 也就是说,描述方案及其部署后,将立即在指定的数据库中创建必要的表和列(以及它们之间的关系),甚至立即生成所有必要的CRUD函数。 也就是说,使用棱镜,您不仅可以获得GraphQL服务器,而且还具有可以立即使用数据库的成熟的有效API。 因此,Prisma服务器提供了一个数据库并与其进行交互,而prisma-client允许您编写解析程序并将请求发送到prisma服务器(或其他地方,甚至对于一些prisma服务器)。 因此,事实证明,您只能自行部署pyramida-client(而SaaS pyramida.io将用作棱镜a-server),并且您可以自行部署pyramida-server,并且通常不以任何方式依赖棱镜,仅此而已你的

在这里,我为自己选择了一个棱镜,作为我平台的基础。 但是后来我不得不自己旋转它以获得完整的平台。

1.合并方案


当时,棱镜无法合并电路。 也就是说,任务如下:

您在一个模块中描述了一个用户模型

type User { id: ID! @unique username: String! @unique email: String @unique } 

在另一个模块中

 type User { id: ID! @unique username: String! @unique firstname: String lastname: String } 

作为一个项目的一部分,您想要自动组合这两种方案以获取输出

 type User { id: ID! @unique username: String! @unique email: String @unique firstname: String lastname: String } 

但是后来这个棱镜做不到。 原来是使用merge-graphql-schemas库来实现这一点的。

与任意的prisma服务器一起使用。


在棱镜中,配置被写入一个特殊的配置文件中。 如果要更改使用的棱镜服务器的地址,则必须编辑该文件。 小事,不愉快。 我想让该URL可以在命令中指定,例如endpoint = http://端点地址yarn deploy(纱线起始)。 那被杀死了好几天...但是现在您可以将一个棱镜项目用于任意数量的端点。 顺便说一下,到目前为止,即使使用本地数据库,甚至使用SaaS棱镜服务器,prisma-cms都可以轻松工作。

模块/插件


通常这还不够。 正如我所说,棱镜的主要任务是为各种数据库提供工作。 他们在这方面做得很好。 他们已经支持使用MySQL,PostgreSQL,Amazon RDS和MongoDB,以及其他几种类型的源。 但是它们不提供任何模块化基础结构。 到目前为止,还没有市场或类似的市场。 只有几个典型的空白。 但是您不能从几个空格中选择两个或三个,并安装在一个项目上。 我们必须选择一个。 我希望这样,就可以在最终项目中安装不同数量的模块,并且在部署电路和分解器时会很高兴,并且可以得到具有整体功能的单个项目。 而且,尽管还没有图形界面,但是已经有两个以上的工作模块和组件可以组合到最终项目中。 在这里,我将立即就个人定义做出一些决定:模块是安装在背面的组件(扩展了数据库和API),组件是安装在背面的组件(添加了各种界面元素)。 到目前为止,还没有用于连接模块的图形界面,但是我写这种方式并不难(这通常不做):

  constructor(options = {}) { super(options); this.mergeModules([ LogModule, MailModule, UploadModule, SocietyModule, EthereumModule, WebrtcModule, UserModule, RouterModule, ]); } 

添加新模块后,只需再次使用一个命令即可执行部署,仅此而已,在这里,我们已经有了新的表/列和增强的功能。

5个前端,响应后端的变化


这根本是不够的。 接下来是题外话。 事实是,我看到的所有基于API的CMS都说“我们提供API很棒,您可以拧紧所需的前端。” 这就是他们“随心所欲”的意思,实际上是“随心所欲的人”。 与UI框架完全相同:“看看我们是什么很棒的按钮,并做所有这些,然后自己与后端混淆”。 那总是被杀。 我只是想找到一个使用javascript编写的,使用GraphQL并提供前后支持的综合CMS。 但是我没有找到那样的人。 我真的希望API更改能够立即在前端被察觉。 为此,完成了几个子步骤:

5.1生成API片段


在最前面,模式文件的片段被注册在请求中。 在服务器上重建API时,还将生成带有API片段的新JS文件。 并且在请求中这样写:

 const { UserNoNestingFragment, EthAccountNoNestingFragment, NotificationTypeNoNestingFragment, BatchPayloadNoNestingFragment, } = queryFragments; const userFragment = ` fragment user on User { ...UserNoNesting EthAccounts{ ...EthAccountNoNesting } NotificationTypes{ ...NotificationTypeNoNesting } } ${UserNoNestingFragment} ${EthAccountNoNestingFragment} ${NotificationTypeNoNestingFragment} `; const usersConnection = ` query usersConnection ( $where: UserWhereInput $orderBy: UserOrderByInput $skip: Int $after: String $before: String $first: Int $last: Int ){ objectsConnection: usersConnection ( where: $where orderBy: $orderBy skip: $skip after: $after before: $before first: $first last: $last ){ aggregate{ count } edges{ node{ ...user } } } } ${userFragment} `; 

5.2所有组件的一个上下文


React 16.3引入了新的上下文API 。 我这样做是为了在任何级别的子组件中您都可以访问单个上下文,而无需从上下文中列出预先需要的类型,而只需指定静态contextType = PrismaCmsContext并通过this->上下文(包括API客户端,方案,请求等)。

5.3动态过滤器


我也很想。 GraphQL允许您使用嵌套结构构建复杂的查询。 我也希望过滤器也能动态化(由API方案构成),并允许我们创建嵌套条件。 这是发生了什么:


5.4网站建设者


最后,我所缺少的是外部站点编辑器,即设计师。 我希望服务器仅执行最少的操作,所有最终设计都应在前端完成(包括设置路由,生成选择等)。 这是另一篇文章的主题,因为除其他外,我还为此在纯contentEditable上编写了我的粗俗的所见即所得编辑器,并且有很多细微之处。 如果我恢复了自己的权利,并且对谁感兴趣,我将撰写另一篇文章。

好吧,最后,是设计师实际操作的简短演示视频。 仍然很原始,但是我喜欢它。


我会就此结束。 我还没有写很多我想写的东西,但是发生了很多事情。 我很乐意发表评论。

PS:所有源代码,包括网站本身的源代码, 都在这里

Source: https://habr.com/ru/post/zh-CN448982/


All Articles