
提升你的开发效率:使用openai Canvas构建高效的react应用程序的5个技巧
- Rifx.Online
- Programming , Software Development , Generative AI
- 23 Feb, 2025
开放AI几周前发布了他们的画布工具的重要更新。如果你不知道画布是什么,或者只模糊地听说过但从未使用过,画布是聊天生成预训练变换器中的一个界面,旨在增强写作和编码项目的协作。与您熟悉的传统基于聊天的交互不同,画布提供了一个并排的工作区,用户可以在接收聊天生成预训练变换器的实时帮助的同时直接编辑文本或代码。这种设置允许用户与模型响应之间进行动态交互,使其成为需要迭代编码、文本编辑和精炼的任务的有用工具。
画布的关键特性
用户可以直接修改画布中的文本或代码,从而获得更大的灵活性和对内容的控制。
您可以通过简单地突出显示文本或代码的关键部分并提出进一步的问题,让聊天生成预训练变换器提供针对性的反馈和建议。
专用的后退按钮允许轻松恢复先前版本,确保每次更改都是可逆的。
画布还提供了多种便捷的写作和编码快捷方式。
写作快捷方式
- 从聊天生成预训练变换器那里获得清晰度和连贯性的改进。
- 调整文本长度以满足不同需求。
- 调整复杂性以匹配目标受众。
- 精炼语法以获得精致的最终结果。
- 使用表情符号来增加强调和视觉效果。
编码快捷方式
- 获取内联建议以提升代码质量。
- 插入打印语句以调试和跟踪代码流程。
- 添加注释以提高可读性和可维护性。
- 快速发现和修复错误。
- 将代码翻译成流行的语言,如JavaScript、Python和Java。
最新更新
我们将集中讨论本文剩余部分的编码方面,从这个角度来看,有两个新的重要更新。
- 能够在画布上使用开放AI的o1模型。从模型选择下拉框中选择o1模型,然后在聊天输入框中使用
/canvas
命令。对于更困难的任务,您可能只需要使用o1模型(例如,参见我下面的地图显示应用)。对于简单的任务,使用较弱的模型应该足够。 - 反应和超文本标记语言代码的创建与开发。如果您的代码包含这些内容,画布现在可以渲染它,以便您可以在画布工具中查看输出。
让我们看看新变化的实际效果
我假设您已经可以访问桌面或基于浏览器的聊天生成预训练变换器应用程序。
图片来自开放AI网站
您首先需要做的是从聊天生成预训练变换器屏幕左上角的模型下拉列表中选择您想要的模型,如上所示。
之后,有三种方法可以访问画布。
-
您可以通过在消息框中输入指令 打开画布 来指示聊天生成预训练变换器显示画布。
-
您可以使用消息框正下方的小三点 “肉丸” 图标。点击此图标,画布选项将是可供您选择的选项之一。
-
您可以点击聊天输入框内并输入
/canvas
然后按 RETURN 键。您会看到/canvas
文本将被替换为蓝色的 画布 一词,等待您进一步输入文本,如下所示。
请注意,目前并非所有模型都支持使用画布;例如,最近发布的 03-mini 模型都不支持。
图片来自开放AI网站
从这里开始,只需输入您希望画布为您编码的内容。
当画布准备好输出代码时,您的屏幕将分为两个部分。左侧将是您的聊天记录。右侧将是代码。请注意,代码部分的右上角有一个 预览 按钮。一旦模型完成编写代码,您可以使用此按钮查看渲染的应用程序。
示例
为了展示新功能,我将展示两个使用画布编写的反应/超文本标记语言应用的示例案例。第一个是一个相对简单的井字棋游戏应用。第二个是一个使用谷歌地图API的更复杂的互动应用。
项目 1 — 一个简单的井字游戏
对于这个任务,由于相对简单,我选择了 GTP-4o 作为我的模型,并在 画布 模式中输入了这个简单的提示。
使用 反应 和 超文本标记语言 创建一个简单的井字游戏
画布 在短时间内显示了这个内容。
尽管游戏按预期工作,但我不喜欢它的外观,想将方块的背景颜色改为白色,将圈和叉改为黑色,所以我要求 画布 这样做。以下是最终结果。
供参考,这里是生成的最终代码。
import { useState } from "react";
import { Button } from "@/components/ui/button";
export default function TicTacToe() {
const [board, setBoard] = useState(Array(9).fill(null));
const [isXNext, setIsXNext] = useState(true);
const handleClick = (index) => {
if (board[index] || calculateWinner(board)) return;
const newBoard = board.slice();
newBoard[index] = isXNext ? "X" : "O";
setBoard(newBoard);
setIsXNext(!isXNext);
};
const winner = calculateWinner(board);
const status = winner ? `Winner: ${winner}` : `Next Player: ${isXNext ? "X" : "O"}`;
return (
<div className="flex flex-col items-center space-y-4">
<h1 className="text-xl font-bold">Tic-Tac-Toe</h1>
<div className="grid grid-cols-3 gap-2">
{board.map((cell, index) => (
<Button key={index} className="w-16 h-16 text-lg bg-white text-black" onClick={() => handleClick(index)}>
{cell}
</Button>
))}
</div>
<p className="text-lg font-semibold">{status}</p>
<Button onClick={() => setBoard(Array(9).fill(null))} className="mt-2">Reset</Button>
</div>
);
}
function calculateWinner(board) {
const lines = [
[0, 1, 2], [3, 4, 5], [6, 7, 8],
[0, 3, 6], [1, 4, 7], [2, 5, 8],
[0, 4, 8], [2, 4, 6]
];
for (let [a, b, c] of lines) {
if (board[a] && board[a] === board[b] && board[a] === board[c]) {
return board[a];
}
}
return null;
}
所以,这个效果非常好,令人印象深刻。是时候进行一个更具挑战性的任务了。
编码项目 2 — 互动全球地图
在这个例子中,我想创建一个应用程序,使用谷歌地图API显示地球的全球地图。地图应该显示按人口排名的前20个城市,并添加功能,使得当点击城市位置时,会显示一个小的弹出信息框,提供该城市的信息。
这是一个更复杂的任务,因此我选择o1模型作为调用画布之前的模型。
这是我使用的初始提示。
为我创建一个使用反应/超文本标记语言和谷歌地图JavaScript API的互动全球地图。在地图上,显示按人口排名的前20个城市的位置。当我点击一个城市时,在屏幕中间显示一个弹出框,显示:- 城市名称、其人口排名,例如1/20、其人口以及所在国家。
一分钟后,画布开始生成所需的代码,当它完成时,显示了一个可工作的应用程序。第一次也有点工作,除了信息弹出框是模态的,并且使显示的地图失去焦点,因此我输入了这个额外的提示。
弹出框似乎是模态的。你能把它改为非模态的,这样在显示时地图就不会变得模糊吗?
这解决了问题,正如你从下面的截图中看到的那样。
作者提供的图片
作为参考,这里是画布创建的完整代码。我没有提供任何城市信息或其他内容。这些都是由聊天生成预训练变换器创建的。
import React, { useEffect, useRef, useState } from "react";
const cities = [
{ name: "Tokyo", country: "Japan", rank: 1, population: "37,339,804", lat: 35.6895, lng: 139.6917 },
{ name: "Delhi", country: "India", rank: 2, population: "31,181,376", lat: 28.7041, lng: 77.1025 },
{ name: "Shanghai", country: "China", rank: 3, population: "27,795,702", lat: 31.2304, lng: 121.4737 },
{ name: "Sao Paulo", country: "Brazil", rank: 4, population: "22,237,472", lat: -23.5505, lng: -46.6333 },
{ name: "Mexico City", country: "Mexico", rank: 5, population: "22,085,140", lat: 19.4326, lng: -99.1332 },
{ name: "Cairo", country: "Egypt", rank: 6, population: "20,900,604", lat: 30.0444, lng: 31.2357 },
{ name: "Mumbai", country: "India", rank: 7, population: "20,667,656", lat: 19.076, lng: 72.8777 },
{ name: "Beijing", country: "China", rank: 8, population: "20,463,985", lat: 39.9042, lng: 116.4074 },
{ name: "Dhaka", country: "Bangladesh", rank: 9, population: "20,283,552", lat: 23.8103, lng: 90.4125 },
{ name: "Osaka", country: "Japan", rank: 10, population: "19,059,856", lat: 34.6937, lng: 135.5023 },
{ name: "New York", country: "USA", rank: 11, population: "18,823,000", lat: 40.7128, lng: -74.006 },
{ name: "Karachi", country: "Pakistan", rank: 12, population: "16,459,472", lat: 24.8607, lng: 67.0011 },
{ name: "Buenos Aires", country: "Argentina", rank: 13, population: "15,154,000", lat: -34.6037, lng: -58.3816 },
{ name: "Chongqing", country: "China", rank: 14, population: "15,872,000", lat: 29.563, lng: 106.5516 },
{ name: "Istanbul", country: "Turkey", rank: 15, population: "15,190,000", lat: 41.0082, lng: 28.9784 },
{ name: "Kolkata", country: "India", rank: 16, population: "14,681,589", lat: 22.5726, lng: 88.3639 },
{ name: "Manila", country: "Philippines", rank: 17, population: "14,158,573", lat: 14.5995, lng: 120.9842 },
{ name: "Lagos", country: "Nigeria", rank: 18, population: "14,862,111", lat: 6.5244, lng: 3.3792 },
{ name: "Rio de Janeiro", country: "Brazil", rank: 19, population: "13,544,000", lat: -22.9068, lng: -43.1729 },
{ name: "Tianjin", country: "China", rank: 20, population: "13,396,402", lat: 39.3434, lng: 117.3616 },
];
export default function GlobalMapWithMarkers() {
const mapRef = useRef(null);
const [selectedCity, setSelectedCity] = useState(null);
const [mapLoaded, setMapLoaded] = useState(false);
useEffect(() => {
const existingScript = document.getElementById("google-maps-script");
if (!existingScript) {
const script = document.createElement("script");
script.id = "google-maps-script";
script.src = `https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY`;
script.async = true;
script.defer = true;
script.onload = () => {
setMapLoaded(true);
};
document.body.appendChild(script);
} else {
setMapLoaded(true);
}
}, []);
useEffect(() => {
if (mapLoaded && window.google && mapRef.current) {
const map = new window.google.maps.Map(mapRef.current, {
center: { lat: 20, lng: 0 },
zoom: 2,
});
cities.forEach((city) => {
const marker = new window.google.maps.Marker({
position: { lat: city.lat, lng: city.lng },
map,
title: city.name,
});
marker.addListener("click", () => {
setSelectedCity(city);
});
});
}
}, [mapLoaded]);
return (
<div className="w-full h-screen relative">
{/* 地图容器 */}
<div ref={mapRef} className="w-full h-full" />
{/* 非模态的城市信息弹出框 */}
{selectedCity && (
<div
className="absolute top-1/2 left-1/2 z-10 max-w-md w-full -translate-x-1/2 -translate-y-1/2 bg-white p-4 shadow-lg rounded-2xl"
>
<div className="flex justify-between items-center mb-2">
<h2 className="text-xl font-bold">城市信息</h2>
<button
className="px-2 py-1 text-sm font-semibold bg-gray-200 rounded hover:bg-gray-300"
onClick={() => setSelectedCity(null)}
>
关闭
</button>
</div>
<div className="space-y-2">
<p className="text-lg font-semibold">{selectedCity.name}</p>
<p>排名: {selectedCity.rank}/20</p>
<p>人口: {selectedCity.population}</p>
<p>国家: {selectedCity.country}</p>
</div>
</div>
)}
</div>
);
}
注意:在上述代码正常工作之前,您需要拥有一个谷歌云账户,设置API密钥,并启用地图JavaScript API。
摘要
随着这些令人印象深刻的更新,开放AI显著提高了其画布产品的实用性。好吧,它不会说服任何当前的Cursor或Windsurf用户放弃他们的工具集……但谁知道开放AI将在接下来的几周和几个月内将这个产品推向何处。由于DeepSeek等公司的基本LLM业务可能受到挤压,他们可能会开始将注意力转向有利可图的编码助手市场。
也许未来的某一天,画布会得到极大的增强,独立出来,并单独对LLM使用收费。
当然,Cursor和其他人也可能在关注这个升级时感到恐慌,以防开放AI继续增加画布的功能而不收取额外费用。如果我们在不久的将来看到他们的月费减少,我们就会知道他们认为哪个未来更有可能。
但目前,画布仍然是它本来的样子。一个有用的,我会说相当令人印象深刻的工具,您可以根据需要随时使用。绝对值得一看。