前端 overlay js_Overlay如何通过设计器工具编译React和Vue.js组件?

前端 overlay js

Overlay is a design-to-code tool for creating production ready web components from designer tools. In short, it is a Sketch plugin (other design tools plugin are coming) that compiles design data as JSON and generates code out of it.

叠加是一种从设计到编码的工具,用于从设计人员工具创建可立即投入生产的Web组件。 简而言之,它是一个Sketch插件(即将提供其他设计工具插件),可将设计数据编译为JSON并从中生成代码。

In this article, we will explain how the Overlay engineering team has designed and built Hyperion, the API responsible for building web components.

在本文中,我们将解释Overlay工程团队如何设计和构建Hyperion ,该API负责构建Web组件。

Before designing the solution, we chose to setup our technical constraints:

在设计解决方案之前,我们选择设置技术约束:

  • As we know, the web development world changes continuously , we want to be able to adapt our product to it. The architecture must allow us to easily add new designer tool inputs (Figma, AdobeXD) and code generation outputs (new JS framework, new UI framework, etc…).

    众所周知,Web开发世界在不断变化,我们希望能够适应我们的产品。 该架构必须使我们能够轻松添加新的设计器工具输入( FigmaAdobeXD )和代码生成输出(新的JS框架,新的UI框架等)。

  • Each framework-specific code generation algorithm must be independent of each other. We don’t want that React code generation can impact Vue.js code generation.

    每个特定于框架的代码生成算法都必须彼此独立。 我们不希望React代码生成会影响Vue.js代码生成。
  • We don’t want to have dependencies between a designer tool input and a code generation output, a team can choose either Sketch/React or Figma/React or Sketch/Vue.js.

    我们不想在设计器工具输入和代码生成输出之间具有依赖关系,团队可以选择Sketch / React或Figma / React或Sketch / Vue.js。

That’s what we want it looks like:

那就是我们想要的样子:

Image for post
A vision of the Overlay engineer’s mind
叠加工程师思想的愿景

With these constraints in mind, we decided to choose one of the most well-known programmer architecture: a compiler. But we didn’t have any clue about how and where to start, this is our journey in the compiler’s wonderful world.

考虑到这些限制,我们决定选择一种最著名的程序员体系结构:编译器。 但是我们对如何以及从何处开始一无所知,这是我们在编译器美好世界中的旅程。

To make the article concise, we will focus on compiler principles and how we implement these principles at Overlay. Further articles will deal with about styles/performance/automation and so many other tasty tech stuff.

为了使文章简洁,我们将重点介绍编译器原理以及如何在Overlay上实现这些原理。 更多文章将涉及样式/性能/自动化以及许多其他美味的技术资料。

少许编译器理论 (A little pinch of compiler’s theory)

A compiler is a computer program which translates code in a language A to code in a language B. There are many compiler types, the “compiler” term is primarily used to design programs that translate top-level code to lower level language source code.

编译器是一种计算机程序,可以将语言A的代码转换为语言B的代码。编译器类型很多,“编译器”一词主要用于设计将顶层代码转换为底层语言源代码的程序。

In our case, the source code is a JSON input sent by the designer tool input and our targets are JS Frameworks (React, Vue.js). Programs which translate between source codes and target code at the same level of abstraction (in this case high level), are usually called a transpiler or transcompiler.

在我们的例子中,源代码是设计器工具输入发送的JSON输入,而我们的目标是JS框架( ReactVue.js )。 通常以相同的抽象级别(在本例中为高级)在源代码和目标代码之间转换的程序称为transpilertranscompiler

Under the hood, a compiler is structured in three consecutive stages: the front-end, the optimizer, the back end.

在后台,编译器分为三个连续的阶段:前端,优化器,后端。

Image for post

To be able to generate several targets (components coded with a particular framework) from several sources (design tools), our compiler uses an abstract data structure called intermediate representation (IR). A good IR needs to be capable of representing source code without loss of information and be independent of any particular source or target code. At Overlay, we decided to take a rose tree mapped on HTML structure as IR because :

为了能够从多个来源(设计工具)生成多个目标(使用特定框架编码的组件),我们的编译器使用了称为中间表示( IR )的抽象数据结构。 一个好的IR需要能够代表源代码而不丢失信息,并且必须独立于任何特定的源代码或目标代码。 在Overlay,我们决定将映射在HTML结构上的玫瑰树作为IR,因为:

  • It’s straightforward to translate HTML structure to modern JS framework code.

    将HTML结构转换为现代JS框架代码很简单。
  • As we are web developers we know how to optimize layout and HTML for building clean and reusable components. Which is important for building the compiler optimizer part.

    作为Web开发人员,我们知道如何优化布局和HTML,以构建干净且可重用的组件。 这对于构建编译器优化器部分很重要。

Here is an example of component intermediate representation.

这是组件中间表示的示例。

Image for post

This component will be translated into this intermediate representation (IR).

该组件将转换为该中间表示形式( IR )。

Image for post

As you see a node in this IR is a Layer and a branch represents a parent/child relation. Let’s understand how we can transform this JSON input into a first simple React Component.

如您所见,此IR中的一个节点是一个Layer ,一个分支代表一个父/子关系。 让我们了解如何将JSON输入转换为第一个简单的React Component。

前端阶段:确保质量 (The Front-End stage: Ensure quality)

The front end stage task is very simple: analyse/validate the source code to build the compiler’s intermediate representation. At Overlay we use Symfony, a PHP framework, to build the backend APIs. To build our parser/analyzer we used Symfony Serializer component. These objects are meant to be used for translating JSON to PHP objects. So all we have to do is define our symbol’s intermediate representation as PHP Object.

前端阶段的任务非常简单:分析/验证源代码以构建编译器的中间表示形式。 在Overlay,我们使用PHP框架Symfony构建后端API。 为了构建解析器/分析器,我们使用了Symfony序列化器组件 。 这些对象用于将JSON转换为PHP对象。 因此,我们要做的就是将符号的中间表示形式定义为PHP Object。

First we defined the Layer PHP representation

首先,我们定义了Layer PHP表示形式

With the Serializer, this JSON input will be transformed into this PHP Object

使用序列化器,此JSON输入将转换为该PHP对象

Image for post

With this approach we created a strong coupling between our models and our API endpoint interface, which is a very good thing in this case because we want to ensure we don’t loose any source code information in our IR.

通过这种方法,我们在模型和API端点接口之间建立了牢固的耦合,在这种情况下,这是一件好事,因为我们要确保我们不会丢失IR中的任何源代码信息。

Now we have to ensure that our IR is correct. To do so, we used Symfony Validator component with attribute annotations. For example we ensured that each layer has a name.

现在我们必须确保我们的IR是正确的。 为此,我们使用了带有属性注释的Symfony Validator组件 。 例如,我们确保每个图层都有一个名称。

Layer class with name validation
具有名称验证的图层类

And that’s it for the front-end part ! As you can see, all the front-end stage “logic” is written in our models which are our IR and that’s a very good way to centralize our logic and make it easily maintainable for our team. Moreover, we used existing Symfony components to build this part and that helped our team to focus only on designing the IR and not implementing validation or parsing.

这就是前端部分! 如您所见,所有前端阶段的“逻辑”都写在我们的模型中,这就是我们的IR ,这是集中逻辑并使其易于维护的很好方法。 此外,我们使用现有的Symfony组件来构建此部分,这帮助我们的团队仅专注于设计IR ,而不执行验证或解析。

Image for post
Overlay Front end
覆盖前端

优化阶段:算法的力量在哪里发生 (The Optimizer stage: where algorithm’s power takes place)

The optimizer stage is where all the “magic” happens. This is the part responsible for optimizing the IR in order to improve the performance and the quality of produced code. We did multiple operation types on the IR like adding new data (padding, margin, flex behavior, etc…), changing the IR tree (removing useless layer, reordering layer children, etc…). We chose to create one optimizer for each optimization and chain them one after another. It is not the best choice from a performance point of view, but it avoid concurrency issues as all the optimizers are dealing with the same TSR tree. The Optimizer A optimizes the tree, then sends the new IR to the Optimizer B etc…

在优化器阶段,所有“魔术”都会发生。 这是负责优化IR以改善性能和所生成代码质量的部分。 我们在IR上进行了多种操作类型,例如添加新数据(填充,边距,弯曲行为等),更改IR树(删除无用层,对子层进行重新排序等)。 我们选择为每个优化创建一个优化器,并将它们一个接一个地链接。 从性能的角度来看,这不是最佳选择,但是由于所有优化器都在处理同一个TSR树,因此它避免了并发问题。 优化器A对树进行优化,然后将新的IR发送到优化器B等。

Image for post
Overlay optimizer
重叠式优化器

We fixed some rules for building our optimizer:

我们修复了一些构建优化器的规则:

  • An optimizer must do only one optimization, we chose this to avoid hard maintainable algorithms.

    一个优化器只能执行一个优化,我们选择这样做是为了避免难以维护的算法。
  • An optimizer can be dependent from other optimizers. For example re-ordering layer children is necessary to compute margin between the right siblings.

    一个优化器可以依赖于其他优化器。 例如,对子层进行重新排序对于计算正确的同级之间的边距是必要的。
  • An optimizer must be developed in test-driven development. We chose this approach because we want to make sure these algorithms work as expected with a set of IR.

    必须在测试驱动的开发中开发优化器。 我们选择这种方法是因为我们要确保这些算法在一组IR下能按预期工作。

To avoid regressions and improve maintainability we built unit tests for each optimizer. Moreover we create functional tests on the whole optimizers chain to check there is no conflict between the optimizers and if linked optimizers work as expected. We really put an emphasis on testing automatically this part because this is the heart of our application. For each optimizer we have between 10–20 unit test cases and we have more than 50 functional test cases on the chain. This series of tests save us many times from regressions, and help us a lot to update optimizers.

为了避免回归并提高可维护性,我们为每个优化器构建了单元测试。 此外,我们在整个优化器链上创建功能测试,以检查优化器之间是否没有冲突以及链接的优化器是否按预期工作。 我们确实强调自动测试这部分,因为这是我们应用程序的核心。 对于每个优化器,我们有10–20个单元测试用例,并且链上有50多个功能测试用例。 这一系列的测试使我们从回归中节省了很多时间,并为我们更新优化器提供了很多帮助。

后端阶段:指定并生成代码 (The Back-End stage: specify and generate code)

The back-end stage is responsible for target technology optimization and specific code generation. As we have to build and maintain several target technologies, we decided to use the same architecture for each technology. A three step architecture : First the Translator, is responsible for creating a target specific representation (TSR) from the intermediate representation, then Specific Optimizer is responsible for optimizing the TSR with language specific rules (Fragment for React, Slot for Vue.js). Finally Code Generator is responsible for building code from the TSR.

后端阶段负责目标技术的优化和特定代码的生成。 由于我们必须构建和维护多种目标技术,因此我们决定为每种技术使用相同的体系结构。 三个步骤的体系结构:首先是Translator,负责从中间表示形式创建目标特定表示( TSR ),然后Specific Optimizer负责使用特定于语言的规则(React的片段,Vue.js的Slot)优化TSR 。 最后, 代码生成器负责从TSR构建代码

Image for post

Why do we use a target specific representation (TSR) and not directly our intermediate representation (IR)?

为什么我们使用目标特定表示( TSR )而不直接使用中间表示( IR )?

To avoid coupling between target languages’ back-end part. That allows engineers to work on specific language features without risking to impact other languages.

为了避免目标语言的后端部分之间的耦合。 这样,工程师就可以使用特定的语言功能,而不必冒险影响其他语言。

To describe our TSR, we used an PHP interface. For React we called this interface ReactElement, here is an example with React, we created a Div symbol class:

为了描述我们的TSR,我们使用了PHP接口。 对于React,我们将此接口称为ReactElement ,这是React的示例,我们创建了一个Div符号类:

Then we created a React Translator, which takes a Layer object (our IR) as an argument and returns a ReactElement (our TSR), for the example we only return Div object.

然后,我们创建了一个React Translator,它使用Layer对象(我们的IR )作为参数并返回ReactElement (我们的TSR ),在该示例中,我们仅返回Div对象。

Finally we built the code generator. To do so we used the Strategy pattern on all React TSR object. To make it simple, we used the ReactElement interface to defined one method renderElement and implemented this method in all our TSR objects.

最后,我们构建了代码生成器。 为此,我们在所有React TSR对象上使用了Strategy模式 。 为简单起见 ,我们使用ReactElement接口定义了一个方法renderElement,并在所有TSR对象中实现了此方法

Div object implementing ReactElement interface
Div对象实现ReactElement接口

Then, we created our React code generator class which calls the renderElement interface method on the root tree ReactElement. We use Twig for templating the React component, it’s a lot easier to manipulate strings inside this template instead of using string concatenation.

然后,我们创建了React代码生成器类,该类在根树ReactElement上调用renderElement接口方法。 我们使用Twig来模板化React组件,在模板中操作字符串比使用字符串连接要容易得多。

And this is the result with our button with icon ! Our first generated component 👏

这是带有按钮的带有图标的结果! 我们第一个生成的组件👏

Button with Icon with basic layout
具有基本布局的带有图标的按钮

There are still improvements to make to get perfect React components: we only generate div tag but our button has a p and an img tag, we don’t have text content inside the text part. But with this approach and more time Overlay engineering team manage to built the first prod ready web component compiler: Hyperion.

为了获得完美的React组件,还需要做一些改进:我们只生成div标签,但我们的按钮具有pimg标签,文本部分内部没有文本内容。 但是,通过这种方法和更多的时间,Overlay工程团队设法构建了第一个支持产品的Web组件编译器: Hyperion

结论 (Conclusion)

Building a web compiler is a difficult thing to do and we learn a lot doing so:

构建Web编译器是一件困难的事情,我们在中学到很多东西:

  • When you design a software, think about what already exists. Scientists, mathematicians, and engineers work a lot on theoretical computing concepts. Don’t ignore their works, use them.

    在设计软件时,请考虑已经存在的内容。 科学家,数学家和工程师在理论计算概念上进行了大量工作。 不要忽略他们的作品,使用它们。
  • Always build your software thinking you are going to maintain it (even if it’s not the case). All the time spent maintaining your software is time where you don’t add any value to your product. Don’t waste your time.

    始终以要维护它为前提来构建软件(即使不是这种情况)。 花费在维护软件上的所有时间就是不为产品增加任何价值的时间。 不要浪费你的时间。

If you want to know more about compilation theory, feel free to post your questions. We recommend this book, Compilers: Principles, techniques and tools, which is quite difficult to read but where you can find the compiler state of the art.

如果您想进一步了解编译理论,请随时发表您的问题。 我们建议您阅读本书《 编译器:原理,技巧和工具》,这本书很难阅读,但是您可以从中找到最新的编译器。

If you want to know more about overlay go here, it’s free to try:

如果您想了解更多有关覆盖的信息,请点击此处免费尝试:

We will build a series of technical articles to explain how Overlay engineering team designs, builds and operates our systems and we will continue to post tech content every month, if you are interested in complex tech problems, follow Overlay blog.

我们将制作一系列技术文章来解释Overlay工程团队如何设计,构建和操作我们的系统,如果您对复杂的技术问题感兴趣,我们将继续每月发布技术内容,请关注Overlay博客

翻译自: https://medium.com/overlay-blog/how-overlay-compiles-react-and-vue-js-components-from-designer-tools-96592760a640

前端 overlay js

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值