有的需求需要在網頁上寫程式碼。
比如線上執行程式碼的 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,
esModuleInterop: true,
})
}
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,
logger: console,
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 編寫體驗了。
👇🏻 點選下方閱讀原文,獲取魚皮往期編程幹貨。
往期推薦