當前位置: 妍妍網 > 碼農

教你做個線上程式碼編輯器,體驗堪比 VSCode!

2024-04-28碼農

有的需求需要在網頁上寫程式碼。

比如線上執行程式碼的 playground:

或者線上面試:

如果讓你實作網頁版 TypeScript 編輯器,你會如何做呢?

有的同學說,直接用微軟的 monaco editor 呀:

確實,直接用它就可以,但是有挺多地方需要處理的。

我們來試試看。

npx create-vite

建立個 vite + react 的計畫。

安裝依賴:

npm install
npm install @monaco-editor/react

這裏用 @monaco-editor/react 這個包,它把 monaco editor 封裝成了 react 元件。

去掉 main.tsx 裏的 index.css

然後在 App.tsx 用一下:

import MonacoEditor from'@monaco-editor/react'
exportdefaultfunctionApp() {
const code = `import lodash from 'lodash';
function App() {
return <div>guang</div>
}
`
;
return<MonacoEditor
height={'100vh'}
path={"guang.tsx"}
language={"typescript"}
value={code}
/>

}

跑下開發服務:

npm run dev

試下看:

現在就可以在網頁寫 ts 程式碼了。

但是有報錯:

jsx 語法不知道怎麽處理。

這裏明顯要改 typescript 的 tsconfig.json。

怎麽改呢?

這樣:

import MonacoEditor, { OnMount } from'@monaco-editor/react'
exportdefaultfunctionApp() {
const code = `import lodash from 'lodash';
function App() {
return <div>guang</div>
}
`
;
const handleEditorMount: OnMount = (editor, monaco) => {
monaco.languages.typescript.typescriptDefaults.setCompilerOptions({
jsx: monaco.languages.typescript.JsxEmit.Preserve,
esModuleInteroptrue,
})
}
return<MonacoEditor
height={'100vh'}
path={"guang.tsx"}
language={"typescript"}
onMount={handleEditorMount}
value={code}
/>

}



onMount 的時候,設定 ts 的 compilerOptions。

這裏設定 jsx 為 preserve,也就是輸入 <div> 輸出 <div>,保留原樣。

如果設定為 react 會輸出 React.createElement("div")。

再就是 esModuleInterop,這個也是 ts 常用配置。

預設 fs 要這麽引入,因為它是 commonjs 的包,沒有 default 內容:

import * as fs from'fs';

設定 esModuleInterop 會在編譯的時候自動加上 default 內容。

就可以這樣引入了:

import fs from'fs';

可以看到,現在 jsx 就不報錯了:

還有一個錯誤:

沒有 lodash 的型別定義。

寫 ts 程式碼沒提示怎麽行呢?

我們也要支持下。

這裏用到 @typescript/ata 這個包:

ata 是 automatic type acquisition 自動型別獲取。

它可以傳入源碼,自動分析出需要的 ts 型別包,然後自動下載。

我們新建個 ./ata.ts,復制文件裏的範例程式碼:

import { setupTypeAcquisition } from'@typescript/ata'
import typescriprt from'typescript';
exportfunctioncreateATA(onDownloadFile: (code: string, path: string) => void{
const ata = setupTypeAcquisition({
projectName'my-ata',
typescript: typescriprt,
loggerconsole,
delegate: {
receivedFile(code, path) => {
console.log('自動下載的包', path);
onDownloadFile(code, path);
}
},
})
return ata;
}

安裝用到的包:

npm install --save @typescript/ata -f 

這裏就是用 ts 包去分析程式碼,然後自動下載用到的型別包,有個 receivedFile 的回呼函式裏可以拿到下載的程式碼和路徑。

然後在 mount 的時候呼叫下:

const ata = createATA((code, path) => {
monaco.languages.typescript.typescriptDefaults.addExtraLib(code, `file://${path}`)
})
editor.onDidChangeModelContent(() => {
ata(editor.getValue());
});
ata(editor.getValue());

就是最開始獲取一次型別,然後內容改變之後獲取一次型別,獲取型別之後用 addExtraLib 添加到 ts 裏。

看下效果:

有型別了!

寫程式碼的時候用到的包也會動態去下載它的型別:


比如我們用到了 ahooks,就會即時下載它的型別包然後套用。

這樣,ts 的開發體驗就有了。

再就是現在字型有點小,明明內容不多右邊卻有一個捲軸:

這些改下 options 的配置就好了:

scrollBeyondLastLine 是到了最後一行之後依然可以捲動一屏,關閉後就不會了。

minimap 就是縮圖,關掉就沒了。

scrollbar 是設定橫向縱向捲軸寬度的。

theme 是修改主題。

return<MonacoEditor
height={'100vh'}
path={"guang.tsx"}
language={"typescript"}
onMount={handleEditorMount}
value={code}
options={
{
fontSize:16,

scrollBeyondLastLine:false,
minimap: {
enabled:false,
},
scrollbar: {
verticalScrollbarSize:6,
horizontalScrollbarSize:6,
}
}
}
/>

好多了。

我們還可以添加快捷鍵的互動:

預設 cmd(windows 下是 ctrl) + j 沒有處理。

我們可以 cmd + j 的時候格式化程式碼。

editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyJ, () => {
editor.getAction('editor.action.formatDocument')?.run()
});

試下效果:

有同學可能問,monaco editor 還有哪些 action 呢?

打印下就知道了:

editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyJ, () => {
// editor.getAction('editor.action.formatDocument')?.run()
let actions = editor.getSupportedActions().map((a) => a.id);
console.log(actions);
});

有 131 個:

用到再搜就行。

這樣,我們的網頁版 TypeScript 編輯器就完成了。

總結

有的需求需要實作網頁版編輯器,我們一般都用 monaco editor 來做。

今天我們基於 @monaco-editor/react 實作了 TypeScript 編輯器。

可以在 options 裏配置捲軸、字型大小、主題等。

然後 onMount 裏可以設定 compilerOptions,用 addCommand 添加快捷鍵等。

並且我們基於 @typescript/ata 實作了自動下載用到的 ts 型別的功能,它會掃描程式碼裏的 import,然後自動下載型別,之後 addExtraLib 添加到 ts 裏。

這樣在網頁裏就有和 vscode 一樣的 ts 編寫體驗了。


👇🏻 點選下方閱讀原文,獲取魚皮往期編程幹貨。

往期推薦