搭建一个简易的Playground


搭建一个简易的Playground

前言

看一个动效的效果时,看到源码用到了ace实现了代码实时效果。(案例是直接使用eval执行代码,但是看了一下codepen的html结构,所以自己利用ace+iframe来搞一个简单的Playground玩玩。

“玩后感”:感觉ace这个库使用起来还挺不方便的(比如设置主题什么的,参数是ace/theme/twilight这种形式,还得另外import),实际使用可能还应该调研一下其他的editor工具。

1
2
3
4
import 'ace-builds/src-noconflict/theme-twilight';

//...
editor.setTheme('ace/theme/twilight');

核心

准备一个iframe用的html,然后对这个html的内容进行处理,比如<script></script>替换成<script>${content}</script>。处理结果会是字符串,所以还需要使用URL.createObjectURL转换成URL。

1
2
3
URL.createObjectURL(
new Blob([result], { type: "text/html" })
)

实现

用到ace的部分比较简单:

  1. ace.edit():参数为dom元素,把dom元素变成一个编辑器,返回editor对象
  2. editor.setTheme():设置主题
  3. editor.session.setValue(): 设置编辑器初始值
  4. editor.getValue():获取编辑器当前值

代码:
App.tsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
import { useCallback, useEffect, useRef, useState } from "react";
import ace, { Ace } from "ace-builds";
import confettiRaw from "./assets/confetti.browser.js?raw"; // 直接import文件会报错无法找到模块。加上?raw表示不当成模块处理
import iframeRaw from "./assets/iframe.html?raw";
import 'ace-builds/src-noconflict/theme-twilight';

import "./App.css";

function App() {
const editorRef = useRef<HTMLDivElement>(null);
const [editor, setEditor] = useState<Ace.Editor>();

const handleInit = useCallback(() => {
const defaultCode = `
confetti({
particleCount: 100,
spread: 70,
origin: { y: 1 },
});
`;

if (editorRef.current) {
const newEditor = ace.edit(editorRef.current);
newEditor.setTheme('ace/theme/twilight');
newEditor.session.setValue(defaultCode);

setEditor(newEditor);
}
}, []);

useEffect(() => {
handleInit();
}, [handleInit]);

const importScriptSrc = URL.createObjectURL(
new Blob([confettiRaw], { type: "text/javascript" })
);
const getIframeUrl = () => {
const res = iframeRaw
.replace(
'<script id="import-script"></script>',
`<script id="import-script" src="${importScriptSrc}"></script>`
)
.replace("<script></script>", `<script>${editor?.getValue()}</script>`);

return URL.createObjectURL(
new Blob([res], { type: "text/html" })
);
}

const [iframeUrl, setIframeUrl] = useState<string>(getIframeUrl());
const handleClick = () => {
setIframeUrl(getIframeUrl());
};

return (
<>
<button onClick={handleClick}>fire</button>
<div className="box">
<div ref={editorRef} id="editor"></div>
<iframe src={iframeUrl}></iframe>
</div>
</>
);
}

export default App;

iframe.html

1
2
3
4
5
6
7
8
9
10
11
12
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Iframe</title>
</head>
<body>
<script id="import-script"></script>
<script></script>
</body>
</html>

第三方javascript: confetti.browser.js可以替换成自己需要的第三方资源

效果

添加js-beautify美化代码

初始化编辑器的代码时,传入的字符串格式可能会有问题。

比如上面的例子,初始化的效果就是这样。所以可以setValue之前用js-beautify做一下美化。

1
2
3
4
5
6
7
8
import beautify from 'js-beautify';

const pretty = (code: string) => {
return beautify.js(code);
}

// ...
newEditor.session.setValue(pretty(defaultCode));

使用monaco-editor

monaco-editor是VSCode的代码编辑器。monaco-editor也是功能很强大,还有代码提示功能,写法也是组件写法。

1
2
3
4
5
6
7
8
9
10
11
import Editor from "@monaco-editor/react";
import "./App.css";

function App() {

return (
<Editor height="60vh" defaultLanguage="javascript" defaultValue="console.log('Hello clz!!!')" theme="vs-dark" />
);
}

export default App;



文章作者: 赤蓝紫
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 赤蓝紫 !
评论
  目录