你不知道的 WebIDE:5000字,剖析實現(xiàn)細節(jié)
前言
3年前在AWS re:Invent 大會上AWS?宣布推出?Cloud9, 用于在云端編寫、運行和調(diào)試代碼,它可以直接運行在瀏覽器中,也就是傳說中的?Web IDE。3年后的今天隨著國內(nèi)云計算的發(fā)展, 各大云計算服務廠商都在部署自己的WEB IDE, 而且已經(jīng)有非常成熟的落地方案, 對于這一塊的技術原理和實現(xiàn), 也非常值得我們?nèi)W習和剖析.
目前比較成熟的WEB IDE方案有CodeSandbox,?Cloud Studio等, 接下來筆者將實現(xiàn)一個簡單的WEB IDE, 來帶大家了解其原理和實現(xiàn)過程.
正文
筆者接下來會介紹WEB IDE的實現(xiàn)原理和應用場景, 并介紹如何在H5-Dooring中使用它.
1. web編輯器實現(xiàn)原理

抽象出來可以分為3個核心部分:
文件導航區(qū)
代碼編輯區(qū)
預覽容器

在把模塊抽象出來之后我們來思考具體的功能實現(xiàn). 對于文件導航區(qū)我們可以很容易的使用react/vue的ui庫來實現(xiàn), 對于文件保存, 目錄樹生成等我們可以使用nodejs + DB(如mysql,Redis)來實現(xiàn). 代碼編輯區(qū)我們可以用第三方成熟的庫比如react-codemirror2?或者react-monaco-editor來做. 由于預覽容器我們不清楚預覽類型(如小程序,?web頁面還是app), 所以這里我們暫時考慮web頁面容器, 也就是我們比較熟悉的iframe. 那么我們可以畫出如下技術實現(xiàn)圖:

實際上WEB IED實現(xiàn)過程遠比上面的復雜, 我們這里只做簡單的抽象. 我們接下來梳理一下在線代碼編輯器的需求:
支持在線編寫前端代碼(html,javascript,css) 支持實時預覽 支持代碼在線下載
1.1 技術選型
在了解了以上實現(xiàn)方式之后, 我們開始來搭建環(huán)境并進行代碼開發(fā). 以下列舉我們的技術選型:
koa?基于nodejs的服務端框架 koa-static?基于koa的靜態(tài)資源中間件 koa-body?解析請求體的中間件 koa2-cors?處理跨域的中間件 koa-logger?處理請求日志的中間件 react?前端框架 react-codemirror2?代碼編輯器 antd?基于react的前端組件庫

1.2 實現(xiàn)細節(jié)
對于以上筆者列出的koa和react的技術方案不熟悉的大家可以先了解一下,筆者這里直接實現(xiàn)具體邏輯。
我們先用umi來創(chuàng)建工程,然后在根目錄新建server.js文件。該文件主要用來處理nodejs相關邏輯,在稍后我會詳細介紹。
界面的實現(xiàn)筆者不一一介紹了,前端模塊筆者來介紹一下如何配置代碼編輯器。react-codemirror2基本使用方式如下:
import {UnControlled as CodeMirror} from 'react-codemirror2';
require('codemirror/mode/xml/xml');
require('codemirror/mode/javascript/javascript');
export default function() {
// ...
return (
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';復制代碼
為了實現(xiàn)預覽功能,筆者之前想了兩種方案,一種是直接通過頁面組件的方式來實現(xiàn)預覽,但是缺點是只有dom和樣式更新能生效,如果編寫js代碼,由于react的內(nèi)部機制是無法直接執(zhí)行script的。
另一種方案是iframe,這種方案可以很好的隔離react和預覽代碼,實現(xiàn)機制如下:

也就是說我們在代碼編輯器里編輯完代碼之后統(tǒng)一通過請求的方式保存在node端,然后通過iframe請求nodejs渲染的靜態(tài)頁面來實現(xiàn)預覽功能。有點類似服務端渲染的感覺。
那么如何保證實時預覽呢?這塊完全可以設計成用戶手動點擊預覽,也是筆者最初的設想,但是為了增強用戶體驗,筆者決定采用實時預覽, 也就是用戶代碼的變化可以實時反應在“預覽窗口”。方案就是在onChange中更新state來實現(xiàn)rerender,這一點用react hooks很好實現(xiàn)。但是實時更新對性能很不友好, 所以為了提高預覽性能和頁面體驗,筆者在這里使用防抖來控制請求頻次和時機。代碼如下:
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);
}復制代碼
在開發(fā)中還遇到同一個問題就是iframe每刷新一次,代碼編輯器的光標都會被重置,這一點對用戶在線coding的體驗非常不好,所以筆者又看了一遍官方文檔,找到了cursor這個有意思的api,中文的意思就是說可以手動設置光標停止的位置,那么我們在每次光標變化的時候都強制設置為當前光標所在的位置,那么就不會應為iframe刷新的影響而被迫觸發(fā)失焦動作了。代碼實現(xiàn)如下:
const onCursorChange = (editor, data) => {
const { line, ch } = data
setCursor({ line, ch })
}
// ...className={styles.codeWrap}
value={html}
options={{
mode: 'xml',
theme: 'material',
lineNumbers: true
}}
onChange={handleChange}
cursor={cursor}
onCursor={onCursorChange}
/>復制代碼

對于界面中的下載html功能以及一件部署的功能都比較簡單,筆者已將代碼提交到github,感興趣的可以學習了解一下。
1.3 服務端實現(xiàn)
服務端實現(xiàn)主要是寫請求接口來存儲html頁面以及直出html頁面,對于跨域請求我們還需要處理跨域問題, 由于代碼邏輯比較簡單, 這里筆者的實現(xiàn)代碼如下:
// 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編輯器中,要實現(xiàn)用戶自定義組件庫或者自定義h5頁面,并實時下載預覽,我們可以直接使用它,如下:

其次,對于需要部署和實時修改的網(wǎng)站,如果上線之后需要快速修改部署,我們可以直接在線coding之后一件部署。對于個人技術博客來說也是可以實現(xiàn)在線編輯和在線一鍵部署,這樣我們就無需依賴特定環(huán)境和特定電腦了。對于更多強大的應用,對于企業(yè)級來說,也可以實現(xiàn)在線coding的方式寫服務端代碼,在線寫sql。比如云開發(fā),云計算領域催生的在線saas協(xié)作等。
3. 總結
以上教程筆者的一個簡單版本,基本上可以實現(xiàn)在線寫前端代碼,對于一些更復雜的功能,通過合理的設計也是可以實現(xiàn)的,大家可以自行探索,筆者已將在線編寫H5頁面的代碼和邏輯集成到H5-Dooring項目里,大家可以自行下載學習。
github地址:H5在線編輯器H5-Dooring
·END·
匯聚精彩的免費實戰(zhàn)教程
喜歡本文,點個“在看”告訴我


