Onlook:一个 React 可视化编辑器

2025-06-07

Onlook:一个 React 可视化编辑器

作者:Jude Miracle✏️

设计与开发之间的差距常常让许多团队感到沮丧。设计师使用 Figma 等工具创建精美的界面,但开发人员却发现很难使用传统的 IDE 将这些设计转化为可运行的代码。这种脱节有时会减慢开发速度,并可能导致误解和昂贵的修复。

Onlook 是一款新工具,它通过将设计功能直接引入您的开发环境来帮助弥补这一差距。借助 Onlook,您可以改进工作流程,增强设计师和开发人员之间的团队合作,并更快、更准确地交付产品。

在本文中,我们将探索 Onlook,这是一款将设计功能直接引入开发环境的工具。我们将比较它与 Figma 和 Webflow 等现有工具的优缺点,并提供一个实用的设置指南。

Onlook 是什么?

Onlook是一款专为 React 应用程序设计的开源可视化编辑器。它使开发人员能够在使用实时 React 组件的同时创建和修改用户界面。Onlook 提供了类似于 Figma 的直接操作界面,提供拖放编辑、实时预览和无缝代码生成等功能。

借助 Onlook,开发人员可以在应用程序的实际代码库中直观地操作组件、调整样式并实时查看更改。它的直接集成简化了设计与开发之间的交接流程,减少了不一致之处并简化了整体工作流程。

与传统的设计工具或无代码构建器不同,Onlook 允许开发者直接集成现有的 React 项目,并在获得可视化编辑优势的同时,保持完全的开发控制权。Onlook 相对较新,Github 上有超过 4.2k 个 star。它拥有超过 40 位贡献者,这意味着他们会定期发布更新和新功能。

Onlook 功能

  • React 应用内的实时编辑: Onlook 允许你在组件功能齐全且可交互的情况下进行编辑。它允许你查看更改如何影响实际的应用程序状态和行为,使用流经组件的真实数据修改布局,并在进行设计更改时立即测试交互。
  • 实时设计编辑: Onlook 的实时编辑功能超越了基本的样式设置。它包括用于组件定位和布局调整的拖放界面、可即时反馈的 CSS 更改实时预览、通过响应式断点可视化功能交互式调整元素大小,以及实时更新组件属性和状态。
  • AI 聊天: Onlook 集成 AI 聊天功能,进一步提升其功能。借助 AI,它可以根据当前工作提出设计建议,推荐配色方案、布局和组件选择,并帮助用户快速构思。
  • 代码优先方法:其代码优先方法可确保您的可视化编辑转换为干净、可维护的 React 代码,可直接操作现有组件代码,不会生成不必要的包装元素或样式膨胀,并在编辑期间完全访问组件属性、状态和方法
  • 与现有 React 项目兼容: Onlook 可以与现有 React 项目无缝集成,适用于任何 React 项目结构,支持现代 React 功能和流行的样式解决方案,如 Tailwind CSS、CSS 模块、上下文、钩子,并保留现有的构建和部署工作流程

使用 Onlook 的好处

  • Onlook 有助于消除在设计工具和代码编辑器之间切换的需要,并促进更高效、更集成的开发流程
  • 它允许快速尝试不同的布局、样式和交互
  • Onlook 通过提供共享的视觉语言弥合了设计师和开发人员之间的差距,这使得交流设计理念和确保一致性变得更加容易
  • 将其设计转化为 React 代码,让 Onlook 更容易被 React 开发人员使用

先决条件

Onlook 是一个独立的应用程序,但要遵循本教程,您还需要:

  • 像 VS Code 或 Cursor 这样的代码编辑器
  • 熟悉在终端上运行命令的基本知识
  • 熟悉设计和 React 概念

项目设置

要开始使用 Onlook,请按照以下步骤操作:

  • 下载并安装 Onlook 桌面应用程序,选择适合您系统的版本。
  • 启动 Onlook 应用程序。系统将提示你使用 GitHub 或 Google 帐户注册或登录。
  • 创建一个新的 React 项目:
    • 给你的项目命名。
    • 为您的应用程序选择一个文件夹。
    • 设置完成后,您将被引导至可以设计应用程序的界面。
  • 启动开发服务器:单击底部工具栏中的播放按钮。
  • 验证 URL:确保http://localhost:3000/指向了正确的位置。如果它在你的浏览器中打开,它也应该在 Onlook 应用程序中打开。
  • 检查终端:您还可以打开终端来确认您的应用程序正在运行。

运行后,您将看到应用程序的主页。Onlook 界面示例现在我们的应用程序已经运行起来了,在设计我们的第一个应用程序之前,让我们先深入了解一下 Onlook 的界面。

了解 Onlook 界面

首次打开 Onlook,您会看到一个融合了可视化编辑和代码功能的界面。该界面包含几个关键区域,每个区域在您的开发过程中都有特定的用途。

主工作区有三个主要部分:

  • 组件画布位于中心,您可以在其中实时查看组件的工作情况
  • 属性和 AI 聊天面板位于右侧,显示当前正在编辑的元素的控件,包括可以与 AI 聊天的部分
  • 组件树导航器位于左侧,显示应用程序的结构

此外,还有其他重要部分:

  • 工具栏:位于界面底部,包括选择元素、平移视图、插入对象和文本、启动应用程序以及访问控制台/终端的按钮。

Onlook 界面连接设计与开发。任何设计变更都会立即更新底层 React 代码。任何代码变更也会反映在设计中。这种双向关系正是 Onlook 与传统设计工具的区别所在。

从设计到代码

与将设计导出为代码的传统工作流程不同,Onlook 会在您的视觉编辑和 React 代码之间保持实时连接。这允许:

  • 视觉变化可立即更新 React 应用程序中的代码
  • 代码可以通过您喜欢的编辑器(如 VS Code)保持访问
  • 可以通过视觉或代码进行更改,并保持同步

让我们把它分解成各个组件。当你处理组件时,你可以:

  • 在 Onlook 的界面中进行可视化设计
  • 点击“在 VS Code 中打开”即可查看和编辑生成的代码
  • 在任一环境中进行更改
  • 立即在两个地方看到您的更改Onlook 界面 2

创建和管理组件

让我们使用 Onlook 创建一个待办事项列表应用,以展示这款可视化编辑器如何与 React 协同工作。本示例将重点介绍 Onlook 的主要功能,并构建一个使用状态管理、用户交互和组件组合功能的真实应用。

在 Onlook 中开始设计时,您有两种选择:直接使用可视化编辑器,或者使用 Onlook 的 AI 助手。对于初学者或需要指导的用户,AI 助手可能会很有帮助。您可以描述您的需求,AI 会帮助您创建一个可以改进的基本布局:在 Onlook 中创建和管理组件可视化编辑器允许您拖放元素、更改样式并快速查看结果。当您进行更改时,Onlook 会自动为您编写 React 代码。首先,所有代码都会写入page.tsx文件中,这只是一个起点,但对于生产应用程序来说并非最佳设置。

构建应用程序结构虽然 Onlook 的自动代码生成功能很有用,但实际的应用程序需要更清晰的结构。为了解决这个问题,我们可以使用基于组件的架构。这种方法可以分离关注点,使代码更易于维护。以下是我们将如何组织项目:

app/
├── page.tsx
└── components/
    ├── TodoContainer.tsx
    ├── TodoForm.tsx
    ├── TodoList.tsx
    ├── TodoItem.tsx
    └── types.ts
Enter fullscreen mode Exit fullscreen mode

此结构遵循 React 最佳实践,将用户界面拆分为清晰、可复用的组件。每个组件(我们将在下面演示)都有特定的功能,有助于保持代码的条理性和易理解性。Onlook Types 组件


// types.ts
export interface Todo {
    id: number;
    text: string;
    completed: boolean;
}
Enter fullscreen mode Exit fullscreen mode

Onlook 的Page组件


// page.tsx
import TodoContainer from './components/TodoContainer';

export default function Page() {
    return (
        <div className="w-full min-h-screen bg-gradient-to-br from-purple-50 to-white dark:from-gray-900 dark:to-gray-800 p-4">
            <div className="max-w-2xl mx-auto">
                <div className="text-center mb-8">
                    <h1 className="text-4xl font-bold text-purple-600 dark:text-purple-400 mb-2">
                        Todo List
                    </h1>
                    <p className="text-gray-600 dark:text-gray-300">
                        Stay organized and productive
                    </p>
                </div>
                <TodoContainer />
            </div>
        </div>
    );
}
Enter fullscreen mode Exit fullscreen mode

Onlook 的 TodoContainer 组件:


// components/TodoContainer.tsx
'use client';

import { useState } from 'react';
import TodoForm from './TodoForm';
import TodoList from './TodoList';
import { Todo } from './types';

export default function TodoContainer() {
    const [todos, setTodos] = useState<Todo[]>([]);
    const [newTodo, setNewTodo] = useState('');

    const addTodo = (e: React.FormEvent) => {
        e.preventDefault();
        if (newTodo.trim()) {
            setTodos([
                ...todos,
                {
                    id: Date.now(),
                    text: newTodo.trim(),
                    completed: false,
                },
            ]);
            setNewTodo('');
        }
    };

    const toggleTodo = (id: number) => {
        setTodos(
            todos.map((todo) => (todo.id === id ? { ...todo, completed: !todo.completed } : todo)),
        );
    };

    const deleteTodo = (id: number) => {
        setTodos(todos.filter((todo) => todo.id !== id));
    };

    return (
        <>
            <TodoForm
                newTodo={newTodo}
                setNewTodo={setNewTodo}
                addTodo={addTodo}
            />
            <TodoList
                todos={todos}
                toggleTodo={toggleTodo}
                deleteTodo={deleteTodo}
            />
        </>
    );
}
Enter fullscreen mode Exit fullscreen mode

Onlook 的 TodoForm 组件:


// components/TodoForm.tsx
'use client';

interface TodoFormProps {
    newTodo: string;
    setNewTodo: (value: string) => void;
    addTodo: (e: React.FormEvent) => void;
}

export default function TodoForm({ newTodo, setNewTodo, addTodo }: TodoFormProps) {
    return (
        <form onSubmit={addTodo} className="mb-8">
            <div className="flex gap-2">
                <input
                    type="text"
                    value={newTodo}
                    onChange={(e) => setNewTodo(e.target.value)}
                    placeholder="What needs to be done?"
                    className="flex-1 p-3 rounded-lg border border-gray-300 dark:border-gray-600 
                             bg-white dark:bg-gray-700 text-gray-900 dark:text-white
                             focus:ring-2 focus:ring-purple-500 focus:border-transparent"
                />
                <button
                    type="submit"
                    className="px-6 py-3 bg-purple-600 text-white rounded-lg
                             hover:bg-purple-700 focus:outline-none focus:ring-2
                             focus:ring-purple-500 focus:ring-offset-2
                             transition-colors duration-200"
                >
                    Add
                </button>
            </div>
        </form>
    );
}
Enter fullscreen mode Exit fullscreen mode

Onlook 的 TodoList 组件


// components/TodoList.tsx
'use client';

import { Todo } from './types';
import TodoItem from './TodoItem';

interface TodoListProps {
    todos: Todo[];
    toggleTodo: (id: number) => void;
    deleteTodo: (id: number) => void;
}

export default function TodoList({ todos, toggleTodo, deleteTodo }: TodoListProps) {
    return (
        <div className="bg-white dark:bg-gray-800 rounded-xl shadow-lg p-6">
            {todos.length === 0 ? (
                <p className="text-center text-gray-500 dark:text-gray-400 py-8">
                    No todos yet. Add one above!
                </p>
            ) : (
                <ul className="space-y-3">
                    {todos.map((todo) => (
                        <TodoItem
                            key={todo.id}
                            todo={todo}
                            toggleTodo={toggleTodo}
                            deleteTodo={deleteTodo}
                        />
                    ))}
                </ul>
            )}

            {todos.length > 0 && (
                <div className="mt-6 pt-6 border-t border-gray-200 dark:border-gray-700">
                    <div className="flex justify-between text-sm text-gray-600 dark:text-gray-400">
                        <span>{todos.filter((t) => !t.completed).length} items left</span>
                        <span>{todos.filter((t) => t.completed).length} completed</span>
                    </div>
                </div>
            )}
        </div>
    );
}
Enter fullscreen mode Exit fullscreen mode

Onlook 的 TodoItem 组件


// components/TodoItem.tsx
'use client';

import { Todo } from './types';

interface TodoItemProps {
    todo: Todo;
    toggleTodo: (id: number) => void;
    deleteTodo: (id: number) => void;
}

export default function TodoItem({ todo, toggleTodo, deleteTodo }: TodoItemProps) {
    return (
        <li className="flex items-center gap-3 bg-gray-50 dark:bg-gray-700/50 
                      p-4 rounded-lg group">
            <input
                type="checkbox"
                checked={todo.completed}
                onChange={() => toggleTodo(todo.id)}
                className="w-5 h-5 rounded border-gray-300 
                         text-purple-600 focus:ring-purple-500"
            />
            <span
                className={`flex-1 ${
                    todo.completed
                        ? 'line-through text-gray-400 dark:text-gray-500'
                        : 'text-gray-700 dark:text-gray-200'
                }`}
            >
                {todo.text}
            </span>
            <button
                onClick={() => deleteTodo(todo.id)}
                className="opacity-0 group-hover:opacity-100 transition-opacity
                         text-red-500 hover:text-red-600 p-1"
            >
                <svg
                    xmlns="http://www.w3.org/2000/svg"
                    className="h-5 w-5"
                    viewBox="0 0 20 20"
                    fill="currentColor"
                >
                    <path
                        fillRule="evenodd"
                        d="M9 2a1 1 0 00-.894.553L7.382 4H4a1 1 0 000 2v10a2 2 0 002 2h8a2 2 0 002-2V6a1 1 0 100-2h-3.382l-.724-1.447A1 1 0 0011 2H9zM7 8a1 1 0 012 0v6a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v6a1 1 0 102 0V8a1 1 0 00-1-1z"
                        clipRule="evenodd"
                    />
                </svg>
            </button>
        </li>
    );
}
Enter fullscreen mode Exit fullscreen mode

Onlook 的一个实用功能是能够双向链接可视化编辑器和代码。这是通过data-oidOnlook 在开发过程中添加到组件的一个特殊属性来实现的:

<TodoContainer data-oid="zshvwmp"/>
Enter fullscreen mode Exit fullscreen mode

此属性可帮助 Onlook 跟踪组件的变化,将视觉元素与代码连接起来,允许双向实时更新,并在可视化编辑的同时维护组件功能。

在构建应用程序时,您可以使用 Onlook 的可视化编辑器来改进设计,而不会损害代码质量。例如,您可以更改组件布局、调整间距、优化颜色和字体,以及测试应用程序对不同屏幕尺寸的响应情况:

Onlook 在设计生态系统中的位置

Web 开发工具满足不同的需求和工作流程。它们为设计和开发提供解决方案。为了了解 Onlook 的适用性,我们将它与其他工具进行比较,重点介绍其优缺点。

Onlook 与 Figma:传统设计工具对比传统设计工具擅长插画、协作、原型设计和设计资产管理。Onlook 则有所不同,它允许直接操作 React 组件,实时与组件状态和属性交互,提供可用于生产环境的代码,与开发工作流程无缝集成,并在设计过程中主动测试组件行为。

Onlook 与 Webflow:无代码构建器对比
无代码平台提供可视化开发和代码导出功能,擅长快速原型设计、可视化 CMS 集成和内置托管。Onlook 的独特之处在于它可以直接集成到现有的 React 代码库中,维护组件逻辑,并以开发者为先,同时允许可视化编辑。它与开发工具集成,支持具有完整 React 功能的自定义组件,并提供简洁的 React 代码。

直接在 React 中设计的优势

  • 开发人员可以在设计过程中使用真实数据和状态变化来观察组件的行为。这有助于他们发现交互问题和状态管理问题。
  • 使用实时 React 组件可以让开发人员快速评估设计选择如何影响性能
  • 设计师和开发人员可以对组件在不同状态下的外观做出明智的决定,确保他们在设计过程中考虑所有可能的状态
  • 使用 React 可以让设计人员快速访问应用程序的 prop 系统和上下文,这样他们就可以处理真实的数据流
  • 开发人员可以尝试不同的方法来组合和组织组件,同时仍然可以完全访问组件的逻辑和属性

结论

Onlook 改善了整个 React 生态系统的工作流程。它增强了设计和开发团队之间的凝聚力,并允许用户在保持代码完整性的同时以可视化的方式编辑 React 组件。通过实时同步可视化更改和代码,Onlook 解决了各种常见的工作流程问题。此外,它的 AI 功能以及与现有 React 项目的顺畅集成使其成为现代开发流程的有力补充。

尽管 Onlook 仍在改进中,但它已展现出改变 React 应用程序构建方式的巨大潜力。随着新功能的不断添加,它有望成为 React 开发者提升工作流程、更轻松地打造高质量用户体验的重要工具。欢迎了解 Onlook 并在您的项目中试用其功能。


几分钟内即可设置 LogRocket 的现代 React 错误跟踪:

  1. 访问https://logrocket.com/signup/获取应用程序 ID。
  2. 通过 NPM 或脚本标签安装 LogRocket。LogRocket.init()必须称为客户端,而不是服务器端。

新公共管理(NPM):

$ npm i --save logrocket 

// Code:

import LogRocket from 'logrocket'; 
LogRocket.init('app/id');
Enter fullscreen mode Exit fullscreen mode

脚本标签:

Add to your HTML:

<script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script>
<script>window.LogRocket && window.LogRocket.init('app/id');</script>
Enter fullscreen mode Exit fullscreen mode

3.(可选)安装插件以实现与您的堆栈的更深入集成:

  • Redux 中间件
  • ngrx中间件
  • Vuex 插件

立即开始

文章来源:https://dev.to/logrocket/onlook-a-react-visual-editor-1n23
PREV
6 个最佳 CSS 背景生成器
NEXT
npm 与 npx:有什么区别?