前端程序员:如何用javascript开发一款在线IDE?
liuian 2025-09-18 03:45 43 浏览
前言
3年前在AWS re:Invent 大会上AWS 宣布推出 Cloud9, 用于在云端编写、运行和调试代码,它可以直接运行在浏览器中,也就是传说中的 Web IDE。3年后的今天随着国内云计算的发展, 各大云计算服务厂商都在部署自己的WEB IDE, 而且已经有非常成熟的落地方案, 对于这一块的技术原理和实现, 也非常值得我们去学习和剖析.
目前比较成熟的WEB IDE方案有CodeSandbox, Cloud Studio等, 接下来笔者将实现一个简单的WEB IDE, 来带大家了解其原理和实现过程.
正文
笔者接下来会介绍WEB IDE的实现原理和应用场景, 并介绍如何在H5-Dooring中使用它.
1. web编辑器实现原理
我们先来看看一个成熟WEB IDE的结构:
抽象出来可以分为3个核心部分: 文件导航区 代码编辑区 * 预览容器
如下图所示:
在把模块抽象出来之后我们来思考具体的功能实现. 对于文件导航区我们可以很容易地使用react/vue的ui库来实现, 对于文件保存, 目录树生成等我们可以使用nodejs + DB(如mysql,Redis)来实现. 代码编辑区我们可以用第三方成熟的库比如react-codemirror2 或者react-monaco-editor来做. 由于预览容器我们不清楚预览类型(如小程序, web页面还是app), 所以这里我们暂时考虑web页面容器, 也就是我们比较熟悉的iframe. 那么我们可以画出如下技术实现图:
实际上WEB IED实现过程远比上面的复杂, 我们这里只做简单的抽象. 我们接下来梳理一下在线代码编辑器的需求: 支持在线编写前端代码(html,javascript,css) 支持实时预览 * 支持代码在线下载
1.1 技术选型
在了解了以上实现方式之后, 我们开始来搭建环境并进行代码开发. 以下列举我们的技术选型: koa 基于nodejs的服务端框架 koa-static 基于koa的静态资源中间件 koa-body 解析请求体的中间件 koa2-cors 处理跨域的中间件 koa-logger 处理请求日志的中间件 react 前端框架 react-codemirror2 代码编辑器 antd 基于react的前端组件库
以下是笔者实现的效果图:
1.2 实现细节
对于以上笔者列出的koa和react的技术方案不熟悉的大家可以先了解一下,笔者这里直接实现具体逻辑。
我们先用umi来创建工程,然后在根目录新建server.js文件。该文件主要用来处理nodejs相关逻辑,在稍后我会详细介绍。
界面的实现笔者不一一介绍了,前端模块笔者来介绍一下如何配置代码编辑器。react-codemirror2基本使用方式如下:
import {UnControlled as CodeMirror} from 'react-codemirror2';
require('codemirror/mode/xml/xml');
require('codemirror/mode/javascript/javascript');
export default function() {
// ...
return (
<CodeMirror
className={styles.codeWrap}
value={html}
options={{
mode: 'xml',
theme: 'material',
lineNumbers: true
}}
onChange={handleChange}
cursor={cursor}
onCursor={onCursorChange}
/>
);
}
通过以上方式我们就能生成一个基本的代码编辑器,需要注意的是我们需要再单独引入对应的主题样式文件:
@import '~codemirror/lib/codemirror.css';
@import '~codemirror/theme/material.css';
为了实现预览功能,笔者之前想了两种方案,一种是直接通过页面组件的方式来实现预览,但是缺点是只有dom和样式更新能生效,如果编写js代码,由于react的内部机制是无法直接执行script的。
另一种方案是iframe,这种方案可以很好地隔离react和预览代码,实现机制如下:
也就是说我们在代码编辑器里编辑完代码之后统一通过请求的方式保存在node端,然后通过iframe请求nodejs渲染的静态页面来实现预览功能。有点类似服务端渲染的感觉。
那么如何保证实时预览呢?这块完全可以设计成用户手动点击预览,也是笔者最初的设想,但是为了增强用户体验,笔者决定采用实时预览, 也就是用户代码的变化可以实时反应在“预览窗口”。方案就是在onChange中更新state来实现rerender,这一点用react hooks很好实现。但是实时更新对性能很不友好, 所以为了提高预览性能和页面体验,笔者在这里使用防抖来控制请求频次和时机。代码如下:
const handleChange = (editor, data, value) => {
fetchPage(value)
}
const fetchPage = (v) => {
if(timer) clearTimeout(timer);
timer = setTimeout(() => {
fetch('http://localhost:80/dooring/render', {method: 'POST', body: v}).then(res => {
html = v
setUpdate(prev => !prev)
});
}, 1000);
}
在开发中还遇到同一个问题就是iframe每刷新一次,代码编辑器的光标都会被重置,这一点对用户在线coding的体验非常不好,所以笔者又看了一遍官方文档,找到了cursor这个有意思的api,中文的意思就是说可以手动设置光标停止的位置,那么我们在每次光标变化的时候都强制设置为当前光标所在的位置,那么就不会应为iframe刷新的影响而被迫触发失焦动作了。 代码实现如下:
const onCursorChange = (editor, data) => {
const { line, ch } = data
setCursor({ line, ch })
}
// ...
<CodeMirror
className={styles.codeWrap}
value={html}
options={{
mode: 'xml',
theme: 'material',
lineNumbers: true
}}
onChange={handleChange}
cursor={cursor}
onCursor={onCursorChange}
/>
至此我们的核心功能就实现了,如下图:
对于界面中的下载html功能以及一件部署的功能都比较简单,笔者已将代码提交到github,感兴趣的可以学习了解一下。
1.3 服务端实现
服务端实现主要是写请求接口来存储html页面以及直出html页面,对于跨域请求我们还需要处理跨域问题, 由于代码逻辑比较简单, 这里笔者的实现代码如下:
// server.js
const Koa = require('koa');
const { resolve } = require('path');
// const staticServer = require('koa-static');
const koaBody = require('koa-body');
const cors = require('koa2-cors');
const logger = require('koa-logger');
const fs = require('fs');
const app = new Koa();
app.use(koaBody());
app.use(logger());
// 设置跨域
app.use(cors({
origin: function (ctx) {
if (ctx.url.indexOf('/dooring') > -1) {
return '*'; // 允许来自所有域名请求
}
return '';
},
exposeHeaders: ['WWW-Authenticate', 'Server-Authorization', 'x-test-code'],
maxAge: 5, // 该字段可选,用来指定本次预检请求的有效期,单位为秒
credentials: true,
allowMethods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
allowHeaders: ['Content-Type', 'Authorization', 'Accept', 'x-requested-with', 'Content-Encoding'],
}));
let htmlStr = ''
app.use(async (ctx, next) => {
console.log(ctx.url);
if(ctx.url === '/dooring/render') {
htmlStr = ctx.request.body;
ctx.body = 'success';
}else if(ctx.url.indexOf('/html') === 0) {
ctx.type = "html";
ctx.body = htmlStr;
}
});
app.listen(80);
2. 应用场景
对于在线coding的应用场景,笔者大致举几个应用场景。比如说我们在H5-Dooring编辑器中,要实现用户自定义组件库或者自定义h5页面,并实时下载预览,我们可以直接使用它,如下:
其次,对于需要部署和实时修改的网站,如果上线之后需要快速修改部署,我们可以直接在线coding之后一键部署。对于个人技术博客来说也是可以实现在线编辑和在线一键部署,这样我们就无需依赖特定环境和特定电脑了。对于更多强大的应用,对于企业级来说,也可以实现在线coding的方式写服务端代码,在线写sql。比如云开发,云计算领域催生的在线saas协作等。
3. 总结
以上教程笔者的一个简单版本,基本上可以实现在线写前端代码,对于一些更复杂的功能,通过合理的设计也是可以实现的,大家可以自行探索,笔者已将在线编写H5页面的代码和逻辑集成到H5-Dooring项目里,大家可以自行下载学习。
github地址:H5-Dooring
最后
如果想学习更多H5游戏, webpack,node,gulp,css3,javascript,nodeJS,canvas数据可视化等前端知识和实战,欢迎在《趣谈前端》一起学习讨论,共同探索前端的边界。
相关推荐
-
- 手机系统更新软件(手机系统更新软件不更新会怎样)
-
第一步:打开苹果手机的设置,点击“通用”选项进入。第二步:选择“软件更新”选项进入第三步:在软件更新界面,如果有新的更新,点击“下载并安装”即可!应用商店里一键更新。在手机软件里,为了更新最新版本可以进软件商店里面找一下,更新点一下,然后它...
-
2026-01-14 09:37 liuian
- 联想小新如何重装系统(联想小新重装系统怎么操作)
-
联想小新重装系统,方法∶在关机状态下,按下电脑的一键恢复按钮(需确保笔记本屏盖在打开状态)。首先找到按键孔,用针对准插孔,捅一下,电脑启动,进入启动选择界面,选择systemrecovery,按回车...
- 麦克风没声音(win11麦克风没声音)
-
一.先确保你的麦克风能正常使用。请确保麦克风本身是好的,连接线没有问题,请确保你的测试软件已正确设置,如YY之类的软件。二.确认你的麦克风是否插入正确的插孔一般麦克风是插入红色插孔中。三.确认你...
- vs2015官网下载(vs2015 下载)
-
VisualStudio2015下载完成之后,会有一个名为“vs2015.pro_chs.iso”的光盘镜像文件。光盘镜像文件将光盘镜像文件在虚拟光驱中加载之后,可以打开查看光盘内容。安装文件双...
- u盘自我保护怎么解除(怎么样取消u盘的自我保护)
-
要解除U盘保护,首先插入U盘后打开“我的电脑”,右击选择U盘图标,点击“属性”。在弹出的对话框中,选择“安全”标签,然后点击“编辑”按钮,根据自己的需要选择或取消“对于系统用户完全控制”权限,点击“确...
- 如何设置自动关机win10(windows 10如何设置自动关机)
-
Win10设置自动关机,需要以下步骤:1.按“Win+R”组合键,呼出“运行”;2.将定时关机命令设置为“shutdown-s-t7200”;3.在“运行”内输入命令,点击“确定”即可;4.如果设置错...
- 公版驱动(公版驱动和专用驱动的区别)
-
公版这个名词特指显卡本身,与驱动无关。一般采用芯片制造商自己设计的显卡,称为公版。显卡驱动只有WHQL版本、Beta版本、兼容版、定制版之分。兼容版一般称为万能驱动,不会给显卡带来多少优化,只是让你能...
- nod32是什么软件(nod32是哪个国家的)
-
起源于捷克斯洛伐克总部现在美国下面是nod32的由来:nod是根据一部电视剧(城市边缘的医院)起的,原意是“磁盘边的医院”32是源于当16-bitNOD-ICE很成熟的时候32位处理器出来了升级适应3...
- 欧拉linux系统官网(欧拉系统命令)
-
在华为欧拉服务器上配置Linux网络,首先需要编辑网络配置文件,位于/etc/sysconfig/network-scripts目录下,根据网络需求配置对应的网络接口,IP地址、子网掩码、网关等信息,...
- deepin安装显卡驱动(deepin安装显卡驱动后无法进入图形界面)
-
1、首先必须使用rufus制作U盘启动,必须选择DD格式2、从其他linux镜像比如Ubantu或其他拷贝出EFI的引导文件,具体是镜像中的EFI—boot—grubx86.efi这个文件,把这个文件...
- 小米路由器管理员初始密码(miwifi小米路由器管理员初始密码)
-
小米路由器管理员密码初始是123456。1、小米路由器的管理员初始密码是admin。2、如果输入admin路由器没有反应,说明密码错误,可以查看路由器背面的管理员登录密码,登录后可以自行修改设置账户名...
- 路由器组网具体连接方法(路由器组网教程)
-
举例:你正在用一个TP-LINK的无线路由器上网,信号不是太好,你想在下边再连一个无线路由器的话,就得在第二个路由器上设置了,先不管第一个路由器,设置步骤:把第二个无线路由器连接到电脑上(只是路...
- 雨林木风win7纯净版gho(雨林木风win7官网)
-
雨林木风WIN7光盘重装系统的步骤是将光盘放入光驱内,设置光驱为第一启动盘,打开电脑后进入光盘引导,最后将系统文件镜像到系统盘上1.你下载的雨林木风GHOSTXPSP3纯净版Y8.0是一个克隆光...
- 一周热门
-
-
飞牛OS入门安装遇到问题,如何解决?
-
如何在 iPhone 和 Android 上恢复已删除的抖音消息
-
Boost高性能并发无锁队列指南:boost::lockfree::queue
-
大模型手册: 保姆级用CherryStudio知识库
-
用什么工具在Win中查看8G大的log文件?
-
如何在 Windows 10 或 11 上通过命令行安装 Node.js 和 NPM
-
威联通NAS安装阿里云盘WebDAV服务并添加到Infuse
-
Trae IDE 如何与 GitHub 无缝对接?
-
idea插件之maven search(工欲善其事,必先利其器)
-
如何修改图片拍摄日期?快速修改图片拍摄日期的6种方法
-
- 最近发表
- 标签列表
-
- python判断字典是否为空 (50)
- crontab每周一执行 (48)
- aes和des区别 (43)
- bash脚本和shell脚本的区别 (35)
- canvas库 (33)
- dataframe筛选满足条件的行 (35)
- gitlab日志 (33)
- lua xpcall (36)
- blob转json (33)
- python判断是否在列表中 (34)
- python html转pdf (36)
- 安装指定版本npm (37)
- idea搜索jar包内容 (33)
- css鼠标悬停出现隐藏的文字 (34)
- linux nacos启动命令 (33)
- gitlab 日志 (36)
- adb pull (37)
- python判断元素在不在列表里 (34)
- python 字典删除元素 (34)
- vscode切换git分支 (35)
- python bytes转16进制 (35)
- grep前后几行 (34)
- hashmap转list (35)
- c++ 字符串查找 (35)
- mysql刷新权限 (34)
