从Figma到React:介绍
2024 年 5 月 19 日

最近,在我们的团队中,关于将在Figma中制作的设计转换为React代码的讨论出现了。这次谈话是由于开发人员可用工具不断发展而引发的。虽然将设计转换为代码的可能性已经存在一段时间,但其实施实际上是不太理想的。在这场辩论之后,我决定探索截至2024年4月的设计转换为代码的当前状态。这个主题本身似乎非常有趣,并有潜力显著加快前端开发人员的工作。但这种实践的现实是什么呢?

过去设计转换为代码的做法 #

从历史上看,诸如Figma和Adobe XD之类的工具被广泛用于设计和实现网站。这些工具提供了大量功能,可以为设计师创建网页布局过程带来便利。当然,使用Photoshop、Indesign或者其他设计软件也可以实现类似的效果。 随着软件在设计领域的进步,专用工具不仅可以加速特定领域的工作,还可以促进公司内不同部门之间的协作。这些工具通常允许设计师创建一个页面,这个页面与需要编码的页面非常相似。例如,设计元素具有与前端编程中使用的边距、颜色和阴影等属性相似的特征。像Figma和Adobe XD这样的工具提供了几种选项,可以直接将元素样式复制为CSS样式,无论是从程序内的组件还是通过各种模式或共享选项。利用这些工具查看和复制样式已经成为常见做法,略微加快了前端开发人员的工作,可能还消除了在尝试复制设计时的一些错误。 最近,基于人工智能的工具变得越来越受欢迎,能够执行各种任务以协助各个领域。在这里,举例说明了人工智能工具如何帮助程序员编码,并概述了它们的优缺点。类似的工具也出现在像Figma和Adobe XD这样的平台上。它们通常作为插件运行,目前许多公司提供人工智能辅助将Figma设计转换为代码的服务(不仅限于React),例如Quest的Figma到React,Anima,Builder.io,Figma到Code等。有趣的是,这些插件在输出方面提供了不同的选项。在某些情况下,可以选择为哪个框架生成代码,甚至可以选择样式处理器,例如SCSS,styled-components或Tailwind。

将Figma设计转换为代码的人工智能插件 #

使用这些插件通常可以实现 在这个过程中,可以通过两种方式完成,以 Figma 为例。在开发者模式之外使用时,通常会将我们重定向到服务提供商的页面,而使用开发者版本则允许我们在 Figma 中预览生成的代码。

要使用开发者模式,您必须在 Figma 中订阅该项目。此外,我测试过的几乎所有插件都有其订阅模式,一些免费版本具有一定的功能。

简单设计:带有嵌套 DIV 和 Tailwind 样式的 React JSX 的基本结构 #

以一个不包含嵌套组件的非常简单设计为例,乍一看结果似乎相当有趣……发生了什么!我们收到了带有嵌套 DIV 和 Tailwind 样式的 React JSX 的基本结构。

Figma 开发模式中的 Anima 插件。 Code

在Figma开发模式中的Figma到代码

Builder.io网站上生成的Builder.io代码。

这些工具之间有一些细微的差异。乍一看,一切看起来都很不错,作为开发者,你可能会开始考虑在项目中使用这个工具... 但稍等片刻!

更复杂的项目:以Figma的Antd和Tailwind模板为例 #

对于更复杂的项目,生成的代码结果看起来不同。我将使用Figma的Antd和Tailwind模板中的组件作为示例,因为它们在Figma中已经有了更好准备的元素结构和更复杂的嵌套组件。对于更复杂的项目,来自不同插件的结果差异更显著。 代码

Anima中的字面组件。

一些库,尽管具有嵌套组件,生成一个平面DIV结构,但几乎所有的库都难以生成与所使用库API相匹配的内容。在Antd的情况下,嵌套组件确实对应于组件结构中准备的内容。

然而,这种结构与库的API不匹配...而且不匹配得很明显。即使我们自己创建的组件具有我们所需的结构,我们也不能指望从Figma模板中为特定库正确生成组件。当涉及到Tailwind组件和其他设计时,AI工具顽固地绕过许多不同的样式,比如颜色或阴影。

以下是Anima从粉色调Tailwind模板生成的JSX代码示例:

import PropTypes from "prop-types";
import React from "react";
{ Documentation import { 颜色 } from "./DocumentationColor";

  export const DocumentationColorWrapper = ({
    text = "灰色(Cool Gray)",
    documentationColorSwatchClassName,
    documentationColorSwatchClassNameOverride,
    documentationColorDivClassName,
    documentationColorDivClassNameOverride,
    documentationColorSwatchClassName1,
    documentationColorSwatchClassName2,
    documentationColorSwatchClassName3,
    documentationColorSwatchClassName4,
    documentationColorSwatchClassName5,
    documentationColorSwatchClassName6,
  }) => {
    return (
      <div className="inline-flex flex-col items-center gap-[24px] relative">
        <div className="relative w-fit mt-[-1.00px] font-text-xl-leading-7-font-normal font-[number:var(--text-xl-leading-7-font-normal-font-weight)] text-defaultgray-900 text-[length:var(--text-xl-leading-7-font-normal-font-size)] text-center tracking-[var(--text-xl-leading-7-font-normal-letter-spacing)] leading-[var(--text-xl-leading-7-font-normal-line-height)] whitespace-nowrap [font-style:var(--text-xl- ```markdown
<div className="leading-7-font-normal-font-style">
  {text}
</div>
<div className="inline-flex flex-col items-start relative flex-[0_0_auto]">
  <DocumentationColor position="top" swatchClassName={documentationColorSwatchClassName} />
  <DocumentationColor position="middle" swatchClassName={documentationColorSwatchClassNameOverride} />
  <DocumentationColor position="middle" swatchClassName={documentationColorDivClassName} />
  <DocumentationColor position="middle" swatchClassName={documentationColorDivClassNameOverride} />
  <DocumentationColor position="middle" swatchClassName={documentationColorSwatchClassName1} />
  <DocumentationColor position="middle" swatchClassName={documentationColorSwatchClassName2} />
  <DocumentationColor position="middle" swatchClassName={documentationColorSwatchClassName3} />
  <DocumentationColor position="middle" swatchClassName={documentationColorSwatchClassName4} />
  <DocumentationColor positi
``` ```jsx
on="middle" swatchClassName={documentationColorSwatchClassName5} />
<DocumentationColor position="bottom" swatchClassName={documentationColorSwatchClassName6} />
</div>
</div>
);

DocumentationColorWrapper.propTypes = {
text: PropTypes.string,
};

The code looks strange, but it is capable of generating Tailwind CSS files:

/** @type {import('tailwindcss').Config} */
module.exports = {
content: ["./src/**/*.{html,js,ts,jsx,tsx}"],
theme: {
extend: {
colors: {
"defaultgray-100": "var(--defaultgray-100)",
"defaultgray-200": "var(--defaultgray-200)",
"defaultgray-300": "var(--defaultgray-300)",
"defaultgray-400": "var(--defaultgray-400)",
"defaultgray-50": "var(--defaultgray-50)",
"defaultgray-500": "var(--defaultgray-500)",
"defaultgray-600": "var(--defaultgray-600)",
"defaultgray-700": "var(--defaultgray-700)",
"defaultgray-800": "var(--defaultgray-800)",
``` ```json
{
  "defaultgray-900": "var(--defaultgray-900)",
  "defaultpink-100": "var(--defaultpink-100)",
  "defaultpink-200": "var(--defaultpink-200)",
  "defaultpink-300": "var(--defaultpink-300)",
  "defaultpink-400": "var(--defaultpink-400)",
  "defaultpink-50": "var(--defaultpink-50)",
  "defaultpink-500": "var(--defaultpink-500)",
  "defaultpink-600": "var(--defaultpink-600)",
  "defaultpink-700": "var(--defaultpink-700)",
  "defaultpink-800": "var(--defaultpink-800)",
  "defaultpink-900": "var(--defaultpink-900)",
  "variable-collection-some": "var(--variable-collection-some)"
},
fontFamily: {
  "text-xl-leading-7-font-normal": "var(--text-xl-leading-7-font-normal-font-family)"
}
}

Builder.io生成的代码:

import * as React from "react";

function MyComponent() {
  return (
    <div className="flex flex-col text-xl leading-7 te ```jsx
<div className="text-center text-gray-900 whitespace-nowrap max-w-[160px]">
  <div className="self-center">粉红色</div>
  <div className="mt-6 w-full bg-pink-900 rounded-none min-h-[80px]" />
  <div className="w-full bg-pink-800 min-h-[80px]" />
  <div className="w-full bg-pink-700 min-h-[80px]" />
  <div className="w-full bg-pink-600 min-h-[80px]" />
  <div className="w-full bg-pink-500 min-h-[80px]" />
  <div className="w-full bg-pink-400 min-h-[80px]" />
  <div className="w-full bg-pink-300 min-h-[80px]" />
  <div className="w-full bg-pink-200 min-h-[80px]" />
  <div className="w-full bg-pink-100 min-h-[80px]" />
  <div className="w-full bg-pink-50 rounded-3xl min-h-[80px]" />
</div>

这次看起来很好,没有一堆奇怪的附加项。

扩展AI“提示” #

一些已经提到的工具包含向代码生成添加额外提示的选项。
例如Anima:

为了代码生成,这值得努力吗?

## Figma转换为React:搭配人工智能——是否值得?

这里提供的示例清楚地表明,在更复杂的项目中,选择一个人工智能插件可以产生显著的差异。然而,无论选择何种方式,每次生成的代码与我们想要实现的目标都有所不同。在手动编程时,添加人工智能工具可能会提高生产力,但仍然需要对生成的代码进行检查和调整。 没有人工智能工具的情况下,前端程序员使用的语言支持协议(LSP)建议更快、更准确地使用组件。纠正糟糕的代码比手写新代码花费更多时间。

审查由人工智能生成的代码需要大量努力来确认代码是否正确。由于代码通常看起来可以正常运行,很容易产生误导,难以在代码中找到并解决。不幸的是,目前人工智能工具存在一个重要问题,即生成的代码并不总是与我们打算使用的库的API兼容,这经常会让我们产生一个简单的问题:这个特定工具确实对我们有用吗,还是只是一种营销骗局?

玩弄人工智能工具非常有趣,所以我建议您亲自测试设计转代码工具的功能(如果您已经订阅使用付费模式,如Figma中的开发者模式)。至于在生产中使用,您可能需要更深入的评估。 对于计划开展的项目,我建议你要非常谨慎。

如果你想了解如何使用React构建应用程序,请查看[前端应用程序困境](https://scalac.io/blog/frontend-app-building-dilemma-custom-solution-or-using-a-library/)文章,以及[React.js的后端](https://scalac.io/blog/backend-for-react-native/)文章。

作者:Michał Szulczewski