当前位置: 首页 > news >正文

哪个网站ppt模板免费下载泰安网站建设有哪些

哪个网站ppt模板免费下载,泰安网站建设有哪些,宣讲家网站两学一做心得体会,网站建设客户分析一、前言 最近AI在线生成前端代码特别火,很多公司的PD都学会使用这类平台开始生成原型图了。例如blot.new、1D等一系列P2C、D2C的场景。 但这些应用都有一个特性,在页面上脱离不开几个核心组件: 文件列表代码编辑器;预览的页面…

一、前言

最近AI在线生成前端代码特别火,很多公司的PD都学会使用这类平台开始生成原型图了。例如blot.new、1D等一系列P2C、D2C的场景。

但这些应用都有一个特性,在页面上脱离不开几个核心组件:

  1. 文件列表+代码编辑器;
  2. 预览的页面;
  3. 提示词对话框;

那这里核心的问题是,在浏览器怎么跑node从而生成本地前端服务呢?这里核心是依赖@webcontainer/api

二、@webcontainer/api

简单介绍一下:

  • 由 StackBlitz 开发
  • 运行在浏览器中的 WebContainers 技术
  • 它让浏览器拥有一个原生 Node.js 运行时容器
  • 可以直接在浏览器内执行 npm install、跑 Express、Vite、Next.js 等服务
  • 不需要后端服务器,全部计算和运行都在本地浏览器完成
  • 非常适合 在线 IDE(CodeSandbox、StackBlitz)、AI 生成代码直接运行文档里的可运行示例

简而言之就是你在电脑终端能做的事情,基本都能在@webcontainer/api中实现,它在页面中提供了一个类似终端的容器给你。

三、0~1实现一个雏形应用

3.1、新建前端应用

新建一个前端项目,用于演示交互。

# 新建目录
mkdir ai-webcontainer-demo
cd ai-webcontainer-demo
# 初始化 package.json
npm init -y
# 安装依赖
npm install react react-dom @webcontainer/api cross-fetch @monaco-editor/react
npm install webpack webpack-cli webpack-dev-server ts-loader typescript @types/react @types/react-dom --save-dev

3.2、新建webpack.config.js

const path = require("path");module.exports = {mode: "development",entry: "./src/index.tsx",output: {path: path.resolve(__dirname, "dist"),filename: "bundle.js",publicPath: "/",},resolve: {extensions: [".ts", ".tsx", ".js"],},module: {rules: [{ test: /\.tsx?$/, loader: "ts-loader", exclude: /node_modules/ }],},devServer: {static: {directory: path.join(__dirname, "public"),},headers: {"Cross-Origin-Opener-Policy": "same-origin","Cross-Origin-Embedder-Policy": "require-corp",},hot: true,port: 3000,},
};

3.3、新建tsconfig.json

{"compilerOptions": {"target": "ES2020","module": "ESNext","jsx": "react-jsx","moduleResolution": "node","strict": true,"esModuleInterop": true,"skipLibCheck": true,"forceConsistentCasingInFileNames": true},"include": ["src"]
}

3.4、新建src/index.tsx

import { createRoot } from "react-dom/client";
import App from "./App";const container = document.getElementById("root")!;
const root = createRoot(container);
root.render(<App />);

3.6、新建files.ts

export const files = {"package.json": {file: {contents: `
{"name": "vite-react-hello","version": "1.0.0","private": true,"scripts": {"dev": "vite --host 0.0.0.0","build": "vite build","preview": "vite preview --host 0.0.0.0"},"dependencies": {"react": "^18.2.0","react-dom": "^18.2.0"},"devDependencies": {"@vitejs/plugin-react": "latest","vite": "latest"}
}`.trim(),},},"vite.config.js": {file: {contents: `
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';export default defineConfig({plugins: [react()],server: {host: '0.0.0.0',port: 5173}
});`.trim(),},},"index.html": {file: {contents: `
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Vite + React + WebContainer</title></head><body><div id="root"></div><script type="module" src="/src/main.jsx"></script></body>
</html>`.trim(),},},src: {directory: {"main.jsx": {file: {contents: `
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App.jsx';
import './index.css';ReactDOM.createRoot(document.getElementById('root')).render(<React.StrictMode><App /></React.StrictMode>
);`.trim(),},},"App.jsx": {file: {contents: `
import React, { useState } from 'react';
import Hello from './components/Hello.jsx';
import Counter from './components/Counter.jsx';export default function App() {const [showCounter, setShowCounter] = useState(true);return (<div className="app-container"><h1>🚀 Hello from Vite + React in WebContainer!</h1><Hello name="WebContainer User" /><button onClick={() => setShowCounter(!showCounter)}>{showCounter ? 'Hide' : 'Show'} Counter</button>{showCounter && <Counter />}</div>);
}`.trim(),},},components: {directory: {"Hello.jsx": {file: {contents: `
import React from 'react';export default function Hello({ name }) {return <p className="hello">Hello, {name}! 👋</p>;
}`.trim(),},},"Counter.jsx": {file: {contents: `
import React, { useState } from 'react';export default function Counter() {const [count, setCount] = useState(0);return (<div className="counter"><p>Count: {count}</p><button onClick={() => setCount(count + 1)}>Increment</button><button onClick={() => setCount(count - 1)}>Decrement</button></div>);
}`.trim(),},},},},"index.css": {file: {contents: `
body {font-family: sans-serif;margin: 0;padding: 0;background: #f6f6f6;
}.app-container {padding: 2rem;
}button {margin: 0.25rem;padding: 0.5rem 1rem;
}.hello {color: #0070f3;
}.counter {margin-top: 1rem;
}`.trim(),},},},},
};

3.5、新建App.tsx

这是页面组件,包含了最早说的几个部分。

代码编辑器基于@monaco-editor/react实现,文件列表基于webcontainer初始化文件后读取实现。

预览的页面基于webcontainer跑完vite dev server后返回容器内存虚拟链接,然后在iframe中渲染实现。

提示词对话框很简单,就暂时用了原生的textarea

代码如下:

import { Editor } from "@monaco-editor/react";
import { WebContainer } from "@webcontainer/api";
import React, { useRef, useState } from "react";
import { files } from "./files"; // 这里可以换成 AI 动态生成const App: React.FC = () => {const iframeRef = useRef<HTMLIFrameElement>(null);const webcontainerRef = useRef<any>(null);const [loading, setLoading] = useState(false);const [prompt, setPrompt] = useState("创建一个最简单的vite+vanilla js项目");const [iframeSrc, setIframeSrc] = useState("");// 文件列表和编辑const [fileList, setFileList] = useState<string[]>([]);const [selectedFile, setSelectedFile] = useState<string>("");const [fileContent, setFileContent] = useState<string>("");/** 安装依赖 */async function installDependencies() {const installProcess = await webcontainerRef.current.spawn("npm", ["install",]);installProcess.output.pipeTo(new WritableStream({write(data) {console.log(data);},}));return installProcess.exit;}/** 启动 Dev Server */async function startDevServer() {await webcontainerRef.current.spawn("npm", ["run", "dev"]);webcontainerRef.current.on("server-ready", (_port: any, url: any) => {console.log("Server ready:", url);setIframeSrc(url);});}/** 从容器读取文件列表 */async function loadFileList(dir: string = "/") {const entries = await webcontainerRef.current.fs.readdir(dir, {withFileTypes: true,});const files: string[] = [];for (const entry of entries) {if (entry.isFile()) {files.push(entry.name);}if (entry.isDirectory()) {const subEntries = await webcontainerRef.current.fs.readdir(`${dir}${entry.name}`,{ withFileTypes: true });subEntries.forEach((se: any) => {if (se.isFile()) {files.push(`${entry.name}/${se.name}`);}});}}setFileList(files);}/** 选择文件并读取内容 */async function handleSelectFile(fileName: string) {setSelectedFile(fileName);const content = await webcontainerRef.current.fs.readFile("/" + fileName,"utf-8");setFileContent(content as string);}/** 保存文件并触发热更新(HMR) */async function handleSaveFile() {if (!selectedFile) return;await webcontainerRef.current.fs.writeFile("/" + selectedFile, fileContent);console.log(`${selectedFile} 已保存`);}/** 启动容器并运行项目 */const handleGenerate = async () => {setLoading(true);if (!webcontainerRef.current) {webcontainerRef.current = await WebContainer.boot();}// AI模式:把files换成AI返回的对象await webcontainerRef.current.mount(files);const exitCode = await installDependencies();if (exitCode !== 0) {throw new Error("Installation failed");}await startDevServer();await loadFileList("/");setLoading(false);};return (<div style={{ display: "flex", height: "100vh" }}>{/* 左侧:文件列表和编辑区 */}<divstyle={{flex: "0 0 700px",borderRight: "1px solid #ccc",display: "flex",flexDirection: "column",}}><div style={{ padding: 8 }}><h3>文件列表</h3>{fileList.map((name) => (<divkey={name}style={{cursor: "pointer",background: name === selectedFile ? "#eee" : "transparent",padding: "4px 8px",}}onClick={() => handleSelectFile(name)}>{name}</div>))}</div>{selectedFile && (<div style={{ flex: 1, display: "flex", flexDirection: "column" }}><divstyle={{fontWeight: "bold",borderTop: "1px solid #ccc",padding: 4,}}>正在编辑: {selectedFile}</div><Editorheight="calc(100% - 40px)" // 编辑器高度defaultLanguage={selectedFile.endsWith(".js") ? "javascript" : "plaintext"}value={fileContent}theme="vs-dark" // 主题,可换成 "light"onChange={(value) => setFileContent(value ?? "")}options={{fontSize: 14,minimap: { enabled: false },scrollBeyondLastLine: false,}}/><button onClick={handleSaveFile} style={{ padding: 8 }}>保存</button></div>)}</div>{/* 右侧:启动按钮 + iframe 预览 */}<div style={{ flex: 1, display: "flex", flexDirection: "column" }}><div style={{ padding: 8, borderBottom: "1px solid #ccc" }}><textareavalue={prompt}onChange={(e) => setPrompt(e.target.value)}style={{ width: "70%", height: "40px" }}/><button onClick={handleGenerate} disabled={loading}>{loading ? "生成中..." : "启动项目"}</button></div><div style={{ flex: 1 }}><iframesrc={iframeSrc}ref={iframeRef}style={{ width: "100%", height: "100%", border: "none" }}/></div></div></div>);
};export default App;

这里有几个关键点。

  1. 当用户点击生成项目时,会先初始化webcontainer容器,对应WebContainer.boot()
  2. 将项目所有文件注入到webcontainer中(真实应用这里应该先从AI的响应取,再序列化组装),这里作为demo直接mock了一份webcontainer格式的files,然后调用await webcontainerRef.current.mount(files)
  3. 执行installDependencies容器安装依赖。
  4. 执行startDevServer运行vite dev server。
  5. 接收虚拟内存链接,在demo iframe中渲染。

效果:

在这里插入图片描述

四、未来的思考

本demo其实没有涉及到AI交互的部分,实现了核心的文件交互+项目预览+容器渲染,如果加了AI,这个Demo还需要做哪些事情?

  • AI部分,基于prompt约束大模型返回的files遵循webcontainer的要求结构,在启动项目之前,注入AI响应的交互和技术链路;
  • 序列化组装files,后续和本demo的交互保持一致。

也就是说,只需要接入AI,这就是个mini blot.new,是否感觉并没有很复杂呢?如果本文给你带来灵感,欢迎在评论区讨论。

http://www.dtcms.com/a/509820.html

相关文章:

  • 网站设计的发展趋势微信怎么开自己的公众号
  • 网站建设iis配置河北招投标信息服务平台
  • 智慧团建网站几点关闭h5网站开发公司
  • 中小学网站建设规范大型的网站建设公司
  • 如何做自己的个人网站网站建设的职责
  • c 怎么做网站开发做网站需要交印花税
  • 免费网站域名注册个人襄州区城乡建设局网站
  • 网站建设服务合同 印花税网站空间不够用
  • 全flash网站下载跨境电商平台有哪些app
  • 环保设备东莞网站建设wordpress开发手册下载
  • 建设网站 无法显示图片搜索引擎优化的定义是什么
  • 快递网站域名更换宁波海曙网站开发公司电话
  • 自然堂官方网站建设网站怎么建设好看
  • 哈尔滨网站搭建页面设计制作网站
  • 无法访问网站怎么看别人网站是怎么做的
  • 短视频剪辑自学seo外链推广工具
  • 如何做游戏推广网站cms是网站吗
  • 企业网站的规划与建设wordpress本地批量传文章
  • 网站资料如何做脚注佳匠网站建设
  • 彩票自己开盘做网站wordpress 情侣博客
  • h5商城网站建站南宁百度seo放心选择
  • 青岛网站建设q479185700棒常见的网络营销模式
  • 优质的网站设计模板主题为气流
  • 温州正规制作网站公司大连网站开发公司shepiguo
  • 公司网站代做做网站是要云空间吗
  • 个人做搜索引擎网站违法吗司法局门户网站建设该报告
  • 网站建设骗子广州开发区人才工作集团有限公司
  • 设计策划网站深圳专业seo
  • 手机网站的域名送菜上门网站app如何做
  • 深圳网站建设公司的英文名是广告策划书封面