monaco-editor samples 目录详解:从零运行官方示例,掌握 ESM/AMD 编辑器集成方案
【免费下载链接】monaco-editorA browser based code editor项目地址: https://gitcode.com/gh_mirrors/mo/monaco-editor
monaco-editor 仓库的samples/目录汇集了官方维护的独立 HTML 集成示例,覆盖浏览器、Electron、NW.js 等运行环境以及 Webpack、Vite、Parcel、esbuild 等主流构建工具。本文以 samples/README.md 为核心,结合各示例目录的真实代码,讲解如何本地运行全部示例、ESM 与 AMD 两种加载方式的差异,以及 Diff 编辑器、自定义 Monarch 语法、本地化、共享 Model 等进阶技巧,读完后你可以直接在任意工程中选择正确的集成路径并复现官方示例。
一、运行全部示例:三步启动本地服务
samples/README.md 给出的标准流程如下:
git clone https://github.com/microsoft/monaco-editor.git cd monaco-editor cd samples npm install . npm run simpleserver启动后访问http://localhost:8888即可浏览全部示例。这个端口并非魔法值,它由 samples/package.json 中的脚本定义:
"scripts": { "simpleserver": "yaserver --root ./ --port 8888" }即示例服务是一个由yaserver提供的静态文件服务器,以samples/为根目录、监听 8888 端口。这也解释了为什么各示例页面上能直接xhr('original.txt')或引用../../node_modules/monaco-editor/min/vs这类相对路径——示例本质上就是一堆可被静态服务的 HTML。
samples/package.json 的 devDependencies 还揭示了示例体系依赖的工具链:
monaco-editor(^0.53.0-dev-20250905):被示例引用的编辑器发行包;monaco-editor-webpack-plugin:Webpack 集成插件(见下文browser-esm-webpack-monaco-plugin);webpack、webpack-cli、webpack-dev-server、ts-loader、html-webpack-plugin、terser-webpack-plugin:各类 webpack 示例的构建工具;electron:Electron 桌面示例;typescript:TypeScript 示例的编译器。
二、加载方式(Loading variations):AMD 与 ESM 两条路线
README 将加载方式归纳为以下类别:
| 示例 | 说明 |
|---|---|
browser-amd-editor | 浏览器中通过AMD懒加载运行 |
browser-script-editor | 浏览器中通过<script>标签以AMD同步加载 |
browser-esm-webpack | 浏览器中通过 webpack 运行 |
browser-esm-webpack-small | 通过 webpack 运行编辑器的一个子集 |
electron-amd | 在 Electron 中运行 |
nwjs-amd/nwjs-amd-v2 | 在 NW.js 中运行(README 注明:v2 可用,初版 reportedly 不可用) |
对照当前仓库的文件树可以确认两点仓库事实:
- AMD 示例已整体迁移到
samples/legacy/目录,且 samples/legacy/README.md 明确声明:“These AMD samples are not supported anymore and will be removed in future releases. Please use the ESM version of the editor!”——即 AMD 路线仅作历史参考,新集成一律走 ESM; - README 中提到的
browser-script-editor在当前仓库树中已不存在,属于文档滞后于代码的典型情况。
而当前samples/下的 ESM 示例远比 README 列表丰富,实际包含以下构建工具变体:
- samples/browser-esm-esbuild:esbuild 打包;
- samples/browser-esm-parcel:Parcel 打包;
- samples/browser-esm-vite 与 samples/browser-esm-vite-react:Vite 纯 TS 与 Vite + React 两种形态;
- samples/browser-esm-webpack、samples/browser-esm-webpack-small、samples/browser-esm-webpack-monaco-plugin、samples/browser-esm-webpack-typescript、samples/browser-esm-webpack-typescript-react:从手写 worker 入口到使用官方插件、从原生 TS 到 React 的完整 webpack 光谱;
- samples/electron-esm-webpack:Electron 环境下的 ESM 集成。
官方针对 ESM 集成的完整指导文档另见 docs/integrate-esm.md。
2.1 传统 AMD 示例的写法(legacy)
以 samples/legacy/browser-amd-editor/index.html 为代表,AMD 集成只有三件事:引入loader.js、用require.config把vs指向编辑器 min 目录、异步加载vs/editor/editor.main后创建实例:
<script src="../../node_modules/monaco-editor/min/vs/loader.js"></script> <script> require.config({ paths: { vs: '../../node_modules/monaco-editor/min/vs' } }); require(['vs/editor/editor.main'], function () { var editor = monaco.editor.create(document.getElementById('container'), { value: ['function x() {', '\tconsole.log("Hello world!");', '}'].join('\n'), language: 'javascript' }); }); </script>HTML 中只需一个尺寸明确的容器:
<div id="container" style="width: 800px; height: 600px; border: 1px solid grey"></div>2.2 ESM + Webpack 示例:手动管理五个 Worker 入口
samples/browser-esm-webpack/webpack.config.js 展示了不依赖任何官方插件时的标准做法——把 Monaco 的五个 worker 显式写成额外的构建入口:
module.exports = { mode: 'development', entry: { app: './index.js', 'editor.worker': 'monaco-editor/esm/vs/editor/editor.worker.js', 'json.worker': 'monaco-editor/esm/vs/language/json/json.worker', 'css.worker': 'monaco-editor/esm/vs/language/css/css.worker', 'html.worker': 'monaco-editor/esm/vs/language/html/html.worker', 'ts.worker': 'monaco-editor/esm/vs/language/typescript/ts.worker' }, output: { globalObject: 'self', filename: '[name].bundle.js', path: path.resolve(__dirname, 'dist') }, // css / ttf 的 loader 规则略 };注意output.globalObject: 'self'这一项,它使产物同时兼容浏览器与 Electron 渲染进程(Electron 中没有window全局,需用self)。
对应地,应用入口 samples/browser-esm-webpack/index.js 通过self.MonacoEnvironment.getWorkerUrl告诉编辑器去哪个 bundle 里找对应语言的 worker(L3-L19):
import * as monaco from 'monaco-editor'; self.MonacoEnvironment = { getWorkerUrl: function (moduleId, label) { if (label === 'json') { return './json.worker.bundle.js'; } if (label === 'css' || label === 'scss' || label === 'less') { return './css.worker.bundle.js'; } if (label === 'html' || label === 'handlebars' || label === 'razor') { return './html.worker.bundle.js'; } if (label === 'typescript' || label === 'javascript') { return './ts.worker.bundle.js'; } return './editor.worker.bundle.js'; } }; monaco.editor.create(document.getElementById('container'), { value: ['function x() {', '\tconsole.log("Hello world!");', '}'].join('\n'), language: 'javascript' });label参数由 Monaco 按语言分发:json、css/scss/less、html/handlebars/razor、typescript/javascript各有专属语言服务 worker,其余语言(含纯语法高亮)回落到通用的editor.worker。Electron 版示例 samples/electron-esm-webpack/index.js 与浏览器版几乎完全相同,印证了globalObject: 'self'的跨环境设计意图。
三、最小化打包:browser-esm-webpack-small 的按需导入
README 特别标注browser-esm-webpack-small只加载“编辑器的一个子集”。其实现机制值得一看:samples/browser-esm-webpack-small/generate-imports.js 是一个代码生成脚本,它扫描node_modules/monaco-editor/esm下所有语言的.contribution.js与monaco.contribution.js,以及edcore.main.js+editor.all.js中声明的各功能模块,然后向 samples/browser-esm-webpack-small/index.js 注入形如import 'monaco-editor/esm/...'的语句,把体积从“全量”压到“真正用到的部分”。
两个值得注意的细节(均可在脚本源码中确认):
- 语言导入以注释形式生成:
generateLanguages()中所有语言 import 默认被加上//前缀,仅python保持启用(L69-L73 的/python/.test(l)判断)。也就是说该示例默认只启用 Python 语法高亮,其余语言需手动取消注释——这正是“子集”的含义; - 功能导入同理:
generateFeatures()读取edcore.main.js与editor.all.js的import '...'行,排除standaloneStrings、tokenization、editor.all、codiconStyles四个基础项后,仅coreCommands与findController默认启用(L99-L104、L131),其余功能(折叠、括号匹配、多光标等)默认注释。
脚本用// BEGIN_LANGUAGES/// END_LANGUAGES与// BEGIN_FEATURES/// END_FEATURES标记对来定位index.js中待替换的区段(L76-L79、L134-L138)。这套“生成器 + 标记区段”的模式展示了 monaco-editor 官方推荐的按需裁剪入口方式:不 import 整个monaco-editor包,而是逐条 import 语言与功能模块。
四、其他示例与技术(Other examples & techniques)
README 列出的五项进阶示例现均位于 samples/legacy/ 下,虽然基于 AMD 加载,但演示的 API 与 ESM 时代完全一致。
4.1 Diff 编辑器
samples/legacy/browser-amd-diff-editor/index.html 演示createDiffEditor的标准用法:先创建差异编辑器实例,再异步加载两份文本(original.txt/modified.txt,与页面同目录),最后用一对 Model 作为setModel参数:
var diffEditor = monaco.editor.createDiffEditor(document.getElementById('container')); Promise.all([xhr('original.txt'), xhr('modified.txt')]).then(function (r) { var originalTxt = r[0].responseText; var modifiedTxt = r[1].responseText; diffEditor.setModel({ original: monaco.editor.createModel(originalTxt, 'javascript'), modified: monaco.editor.createModel(modifiedTxt, 'javascript') }); });4.2 共享 Model:两个编辑器一份数据
samples/legacy/browser-amd-shared-model/index.html 展示了 Model 与 Editor 解耦的核心能力:先用monaco.editor.createModel创建一份 JavaScript 文本模型,再把同一个model 传给两个create调用:
var model = monaco.editor.createModel( ['function x() {', '\tconsole.log("Hello world!");', '}'].join('\n'), 'javascript' ); var editor1 = monaco.editor.create(document.getElementById('container1'), { model: model }); var editor2 = monaco.editor.create(document.getElementById('container2'), { model: model });任何一侧的编辑都会反映到另一侧,这是实现“主编辑器 + 预览/对比视图”的基础。
4.3 本地化(German locale)
samples/legacy/browser-amd-localized/index.html 通过 AMD 的 nls 配置把界面语言切为德语:
require.config({ 'vs/nls': { availableLanguages: { '*': 'de' } } });在 ESM 集成中,对应的做法是 import 语言包(samples/browser-esm-vite/main.ts 中保留了被注释的参考行// import 'monaco-editor/nls/lang/de';)。
4.4 自定义 Monarch 语法
samples/legacy/browser-amd-monarch/index.html 演示了为未知语言编写 Monarch tokenizer 的完整闭环,示例文本是 Apache 服务器日志:
monaco.languages.register({ id: 'myCustomLanguage' })注册语言;monaco.languages.setMonarchTokensProvider提供 tokenizer,用四条正则把日志拆成custom-error/custom-notice/custom-info/custom-date四种 token(如[/\[error.*/, 'custom-error']);monaco.editor.defineTheme('myCoolTheme', { base: 'vs', inherit: false, rules: [...] })定义一个只为这些 token 着色的主题,例如{ token: 'custom-error', foreground: 'ff0000', fontStyle: 'bold' };create时传入theme: 'myCoolTheme'与language: 'myCustomLanguage'。
4.5 iframe 内运行
samples/legacy/browser-amd-iframe/index.html 将编辑器放入<iframe>并加载内部页面(同目录inner.html),用于验证跨 frame 场景下的样式与事件表现。此外 samples/legacy/browser-amd-shadow-dom、samples/legacy/browser-amd-trusted-types、samples/legacy/browser-amd-requirejs 还分别覆盖了 Shadow DOM、Trusted Types、requirejs 三种集成边缘场景。
五、桌面运行时:Electron 与 NW.js
- Electron:README 指向
electron-amd(现位于 samples/legacy/electron-amd,含主进程main.js与两个 HTML 页面),仓库另提供了更现代的 samples/electron-esm-webpack(ESM + webpack,main.js为 Electron 主进程,渲染端代码与浏览器版 webpack 示例一致)。 - NW.js:samples/nwjs-amd 与 samples/nwjs-amd-v2 各是一个单文件 HTML。README 明确记录了官方观察结论:“it is reported that v2 works and the initial version does not”,即 NW.js v2 兼容而初版存在问题——选型时建议直接以 v2 示例为准。
六、小结与延伸阅读
samples/目录的价值在于它把 monaco-editor 的集成矩阵拆成了可逐个复现的最小工程:
- 集成新工程请走 ESM 路线(AMD 示例已标记为不再维护),首选官方 Webpack 插件(见 samples/browser-esm-webpack-monaco-plugin,其依赖
monaco-editor-webpack-plugin),或按 samples/browser-esm-webpack/webpack.config.js 手动声明五个 worker 入口; - 追求小体积时参考 browser-esm-webpack-small 的语言/功能按需 import 模式;
- 高级特性(Diff、共享 Model、Monarch 语法、本地化、iframe)可先在 samples/legacy/ 中读懂 API 用法,再平移到 ESM 工程。
仓库内与本文相关的进一步资料:
- docs/integrate-esm.md:ESM 集成的官方指南;
- webpack-plugin/:
monaco-editor-webpack-plugin插件的源码(含AddWorkerEntryPointPlugin等实现),可深入理解插件如何自动管理 worker 入口; - monaco-lsp-client/:将 Monaco 语言服务对接 LSP 协议的参考实现;
- src/editor.ts 与 src/index.ts:发行包
monaco-editor的入口源码,示例中import * as monaco from 'monaco-editor'的源头。
全部示例遵循 MIT 许可(见 samples/README.md 末尾及根目录 LICENSE.txt)。
【免费下载链接】monaco-editorA browser based code editor项目地址: https://gitcode.com/gh_mirrors/mo/monaco-editor
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考