前言
不熟悉的朋友可能不知道,我叫老骥
,前端切图仔,单位内卷,疯狂加班
在上一篇的文章中,我们详细介绍了 在线IDE的优劣势, 市面上的在线IDE种类,IDE的大致的实现方式,以及简单的实现原理
算是水了一篇吧, 属于是,主要介绍了, 理论性的东西 ,可谓,听君一席话,如听一席话
,
听着好有道理,实则并没有什么卵用,
第二篇了,得直入正题了,接下来跟大家一块实现一个残废版——码上掘金
钻研原型
所谓知己知彼,才能百战百胜
, 既然要抄码上掘金, 那么我们就要了解东家,也就是官方版本-- 这个项目的结构
整个码上掘金, 从大块上包含两个部分
IDE跟用户交互部分
跟用户交互部分,自不必过多介绍, 大致,就是那老几样,crud而已,比如:包含
发布
,当前用户发布列表 ,收藏,点赞 , fork 代码,布局,等等功能
听到这,相信各位jym
脑袋蹭蹭蹭全是画面,干业务不天天都是这玩意吗
?
确实,坦率的讲,对于一个技术项目,这个东西在技术难度层面,就是侮辱人的智商的,很多人都对他嗤之以鼻, 很是不屑
然而,我想说的是,在我们的日常工作中,很多人都是都是靠着这么多crud 去养活
没有这些东西,一个产品就不能是一个好产品,
这也东西也是有着很重要的业务方面的思考的
,他的业务复杂度,也很高深。 他的重要程度其实并不比技术差
举个例,比如我这个页面的ui 风格
,怎样影响用户的留存,再比如,什么样的布局,能提升用户体验,
还比如,增加怎样的交互,能增加用户的易用性
,
其实这些都需要思考的,他的难度也不比技术差, 给这些琢磨明白了,你也能值钱,并不一定是技术好
所以,我就想在这里发出一个疑问?在我们这个行当,技术好一定是绝对优势吗
?
不一定
, 他还跟你您的性格,能耐,运气,选择有很大的关系,所以,别人下次,叫你大佬,您一定要有空杯心态
您要知道,三人行,必有我师
,您要知道,别人夸你,可能就是商业互吹,一说一乐
我就发现,很多人,有点技术, 就不知道自己是谁了,行为张狂, 表情夸张,天天好为人师, 指手画脚
其实你就是个井底之蛙,垃圾键盘侠
。。。。
到这,你可能觉得我在批评那些 除了技术别的一概听不进去的伪大佬,
呃,其实,这里只是为了告诫自己,警醒自己,三省吾身
,请大家不要对号入座。。。
到这,你以为我在虚怀若谷?
呃,其实,我也可能在为了挣钱不要脸的凑字数
总结起来就是一句话, 互联网江湖,真真假假,虚虚实实,大家一定要,提高警惕,擦亮双眼,
走正确的路,学正确的技术,干正确的事。
至于是什么是正确?
俺以为,大多数人走的路,那就是正确,不要想着标新立异,这也会那也会,有一项安身立命的资本即可。
对于俺,干好vue
这一家,走到哪里都不怕!
所以,俺这个残废版——码上掘金,还用vue
写
git地址如下: 残废版--码上掘金
(最近单位比较忙,后期慢慢给代码补上)
额,有点跑题了,但刚才着实痛快了一把,说了点心里话,
我们言归正传,继续往下走
说完了,交互部分,在此在此强调一下,这一部分也很重要,这一部分也是您安身立命的资本之一
,
只是,不是我们的主要研究内容,所以暂时按下不表!
下面高潮开始,上主菜
IDE主体部分
码上掘金,从结构上来说只有三个部分,分别是编辑器部分,渲染编译器部分,以及 错误提示控制台部分
由于他的初心是是为了轻便,简洁,所见即所得, 所以省略了文件系统
那,既然这样的话,我们也不需要了吧, 毕竟残废版
其实,我在之前的文章中写了个文件系统
git 地址如下,有兴趣的jym 可自取
tree list
接下来,我们一个个梳理他的这几个模块
编辑器部分
东家的编辑器部分,可谓非常传统 ,他用微软干了很多年头的在线编辑器-也就是vscode 的前身
monaco-editor 这玩意什么都好,毕竟是vscode 的祖宗,就是文档,是真费解啊
当然你也可以另辟蹊径,找了另一个极端codemirror5
这也是跟monaco-editor 可以分庭抗礼的编辑器,支持语言众多而且接入方便,文档,齐全, 虽然也是英文,
可我们有翻译软件啊
而在,在社区繁荣的今天,更是有大佬在他的基础上做出了专门用于vue 的定制版本vue-codemirror
使得我们的接入更加方便, 如此一来,挣钱也就更容易了!
有很多jym
对这个一块可能还相当陌生,那么我们就来分别对这两个编辑器的使用方式来一个简单的介绍
monaco-editor
monaco-editor 虽然也有vue的版本接入 vue-monaco-editor
但是目前在社区的认可度还不够高,所以暂时不要不要使用
我们还是使用原始的接入方法
代码语言:javascript复制// 引入 monaco-editor
<template>
<div id="codeEditBox"></div>
</template>
<script lang="ts" setup>
import * as monaco from 'monaco-editor'
import { onMounted, ref } from 'vue'
const editor = ref(null)
const language = ref("html")
const initEditor = () => {
// 初始化编辑器,确保dom已经渲染
editor.value = monaco.editor.create(document.getElementById('codeEditBox'), {
value: '', //编辑器初始显示文字
language: language.value, //语言支持自行查阅demo
theme: 'vs-dark', //编辑器主题
selectOnLineNumbers: true,//显示行号
roundedSelection: false,
readOnly: false, // 只读
cursorStyle: 'line', //光标样式
automaticLayout: true, //自动布局
glyphMargin: true, //字形边缘
useTabStops: false,
fontSize: 15, //字体大小
autoIndent: 'full', //自动布局
quickSuggestionsDelay: 100, //代码提示延时
});
}
onMounted(() => {
initEditor()
})
</script>
<style>
#codeEditBox {
font-family: Avenir, Helvetica, Arial, sans-serif;
height: 400px;
padding: 0;
overflow: hidden;
margin-bottom: 15px;
}
</style>
将以上代码导入vue 即可初始化编辑器,然而比较坑的是,他还需要导入一些包和做一些配置,来运行编辑器, 不然会出现以下错误
万幸的是,社区的力量是伟大的,他们有针对webpack的插件,自动导入。来省去了自己配置的繁琐
代码语言:javascript复制// vue.config.js
const path = require('path')
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin')
module.exports = {
lintOnSave: false,
productionSourceMap: false,
configureWebpack: {
plugins: [
new MonacoWebpackPlugin({
languages: ['css', 'html', 'javascript', 'less', 'pug', 'scss', 'typescript', 'coffee']
})
]
}
当然还有vite 版本的
代码语言:javascript复制//vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import * as path from "path";
import monacoEditorPlugin from 'vite-plugin-monaco-editor';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), monacoEditorPlugin.default({})]
})
这里需要注意的是,在高版本的vite中 有个esm的bug
,所以需要手动添加default
能跑通编辑器之后,我们就需要来接入主题美化了在vscode中俺以为最美的主题莫过于OneDarkPro
于是,俺在网上找到了他的移植版本
其实就是一堆的配置文件,我们只需要引入即可
代码语言:javascript复制import { json } from 'OneDarkPro.js'
import * as monaco from 'monaco-editor'
// 安装主题
monaco.editor.defineTheme('OneDarkPro', json)
然后引入之后你就会发现变成了这样
还不如官方主题,其实我们要做的还有一步, 关联语法,由于我们是要使用vscode 语法,但是vscode
和monaco-editor
本质上又不是一个东西
vscode 使用的是 vscode-textmate
来解解析,做的关联,但是monaco-editor
这玩意没有啊?
那么问题来了? 我们要怎么关联怎么解析呢?
好在,社区的力量是强大的,我翻了codesandbox的源码
在他的源码中找到了蛛丝马迹
monaco-textmate
这个库,专门用来解析monaco-editor
他的功能类似于vscode-textmate
但是,他们俩虽然配对成功了,但是却还有层窗户纸没有捅破,他们还没有建立连接
于是同样还是这个大佬(在此我放上他的github:Neek Sandhu)
又做了个插件 monaco-editor-textmate
将他们关联起来了!
有了大佬的贡献,我们说干就干
在开始之前,我们还需要一样东西onigasm
这个东西简单的来说,就是一个web版本的正则表达式的库 ,他脱胎于c语言编写Oniguruma
简单的来说,就是将 Oniguruma
编译为 WebAssembly
WebAssembly
可能很多人都比较陌生
简而言之,他就是能在浏览器直接跑的非js代码,这个玩意非常神奇, 他让在浏览器跑node 成为了可能。
有了这些,我们就可以开始干了
我们就以vue文件的主题为例,少废话,先看东西:
效果基本可vscode一模一样了
首先,我们需要引入用到的字体包
代码语言:javascript复制// 引入主题包
import { json } from '../assets/themes/OneDarkPro'
import html from '../assets/grammars/html.tmLanguage.json'
import css from '../assets/grammars/css.tmLanguage.json'
import js from '../assets/grammars/JavaScript.tmLanguage.json'
// 引入解析器包
import { loadWASM } from 'onigasm'
import { onMounted, ref } from 'vue'
import { Registry } from 'monaco-textmate'
import { wireTmGrammars } from 'monaco-editor-textmate'
然后导入这个web 版本的正则,配置运行环境
代码语言:javascript复制 await loadWASM(`/onigasm/onigasm.wasm`)
// 这里按需加载不同的运行环境
window.MonacoEnvironment = {
getWorkerUrl: function (moduleId, label) {
hasGetWorkUrl = true
if (label === 'json') {
return 'monaco/json.worker.bundle.js'
}
if (label === 'css' || label === 'scss' || label === 'less') {
return 'monaco/css.worker.bundle.js'
}
if (label === 'html' || label === 'handlebars' || label === 'razor') {
return 'monaco/html.worker.bundle.js'
}
if (label === 'typescript' || label === 'javascript') {
return 'monaco/ts.worker.bundle.js'
}
return 'monaco/editor.worker.bundle.js'
}
}
然后确定更改默认的主题解释器即可
代码语言:javascript复制 const grammars = new Map()
grammars.set('html', 'text.html.basic')
const registry = new Registry({
getGrammarDefinition: (scopeName: string) => {
return new Promise((resolve) => {
resolve({
format: 'json',
content: map[scopeName]
})
})
}
})
monaco.languages.register({ id: 'html' })
let loop = () => {
if (hasGetWorkUrl) {
Promise.resolve().then(async () => {
await wireTmGrammars(monaco, registry, grammars, editor.value)
})
} else {
setTimeout(() => {
loop()
}, 100)
}
}
loop()
完整代码如下
代码语言:javascript复制<template>
<div id="codeEditBox"></div>
</template>
<script lang="ts" setup>
import { json } from '../assets/themes/OneDarkPro'
import html from '../assets/grammars/html.tmLanguage.json'
import css from '../assets/grammars/css.tmLanguage.json'
import js from '../assets/grammars/JavaScript.tmLanguage.json'
import * as monaco from 'monaco-editor'
import { loadWASM } from 'onigasm'
import { onMounted, ref } from 'vue'
import { Registry } from 'monaco-textmate'
import { wireTmGrammars } from 'monaco-editor-textmate'
const map = {
'text.html.basic': html,
'source.css': css,
'source.js': js,
}
const editor = ref(null)
let hasGetWorkUrl = false
const language = ref("html")
const initEditor = () => {
// 初始化编辑器,确保dom已经渲染
editor.value = monaco.editor.create(document.getElementById('codeEditBox'), {
value: ``, //编辑器初始显示文字
minimap: {
enabled: false // 关闭小地图
},
language: language.value, //语言支持自行查阅demo
theme: 'OneDarkPro', //编辑器主题
selectOnLineNumbers: true,//显示行号
roundedSelection: false,
cursorStyle: 'line', //光标样式
automaticLayout: true, //自动布局
glyphMargin: true, //字形边缘
useTabStops: false,
fontSize: 18, //字体大小
autoIndent: 'full', //自动布局
// quickSuggestionsDelay: 100, //代码提示延时
fontFamily: 'MonoLisa, monospace',
contextmenu: false, // 不显示右键菜单
fixedOverflowWidgets: true, // 让语法提示层能溢出容器
});
}
onMounted(async () => {
await loadWASM(`/onigasm/onigasm.wasm`)
window.MonacoEnvironment = {
getWorkerUrl: function (moduleId, label) {
hasGetWorkUrl = true
if (label === 'json') {
return 'monaco/json.worker.bundle.js'
}
if (label === 'css' || label === 'scss' || label === 'less') {
return 'monaco/css.worker.bundle.js'
}
if (label === 'html' || label === 'handlebars' || label === 'razor') {
return 'monaco/html.worker.bundle.js'
}
if (label === 'typescript' || label === 'javascript') {
return 'monaco/ts.worker.bundle.js'
}
return 'monaco/editor.worker.bundle.js'
}
}
monaco.editor.defineTheme('OneDarkPro', json)
monaco.editor.setTheme('OneDarkPro')
initEditor()
const grammars = new Map()
grammars.set('html', 'text.html.basic')
const registry = new Registry({
getGrammarDefinition: (scopeName: string) => {
return new Promise((resolve) => {
resolve({
format: 'json',
content: map[scopeName]
})
})
}
})
monaco.languages.register({ id: 'html' })
// 这里为了防止worker还没加载完就执行,所以加了个延时
let loop = () => {
if (hasGetWorkUrl) {
Promise.resolve().then(async () => {
await wireTmGrammars(monaco, registry, grammars, editor.value)
})
} else {
setTimeout(() => {
loop()
}, 100)
}
}
loop()
// Promise.resolve().then(async () => {
// await wireTmGrammars(monaco, registry, grammars, editor.value)
// })
})
</script>
<style>
#codeEditBox {
font-family: Avenir, Helvetica, Arial, sans-serif;
height: 100vh;
padding: 0;
overflow: hidden;
margin-bottom: 15px;
}
</style>
vue-codemirror
在介绍vue-codemirror
之前,我们先来介绍
codemirror
这个老牌编辑器
CodeMirror 是通过 JavaScript 实现的文本编辑器。专门用于编辑代码,带有大量的语言模式和实现更高级的插件功能。
拥有丰富的编程 API 和 CSS 主题化系统可用于定制 CodeMirror ,使它更适合你的应用和扩展新功能。
现在他已经跟新到了codemirror5
vue-codemirror其实就是在他的基础上做了个vue 的封装
接下来我们就直接使用vue这个版本来封装一个属于我们的编辑器
用到的包相对于monaco-editor
就简单很多了
主要包含:
- 编辑器包
vue-codemirror
- 主题包
@codemirror/theme-one-dark
自带暗黑主题 - js 语言包
@codemirror/lang-javascript
- css 语言包
@codemirror/lang-css
- html 语言包
@codemirror/lang-html
- json 语言包
@codemirror/lang-json
- markdown 语言包
@codemirror/lang-markdown
组件代码如下
代码语言:javascript复制<template>
<div class="box">
<Codemirror style="font-size: 16px;" ref="CodemirrorRef" :modelValue="code" :style="{ height: '100%' }"
:autofocus="true" :indent-with-tab="true" :tabSize="2" :extensions="extensions" @change="change">
</Codemirror>
</div>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
import { Codemirror } from 'vue-codemirror'
import { oneDark } from '@codemirror/theme-one-dark'
import { javascript } from '@codemirror/lang-javascript'
import { css } from "@codemirror/lang-css";
import { html } from "@codemirror/lang-html";
import { json } from "@codemirror/lang-json";
import { markdown } from "@codemirror/lang-markdown";
const extensions = [html(), oneDark]
const code = ref('')
const CodemirrorRef = ref(null)
const change = (e) => {
console.log(e)
}
</script>
<style>
.box {
width: 100%;
height: 100vh;
}
</style>
效果如下,看着也还可以,虽然比不上monaco-editor
,但是好在简单啊,大大降低了使用门槛
然而,我是那种喜欢安逸的人吗?所以咱们的残废版码上掘金,我要毅然决然的选择 monaco-editor
毕竟有难度,才够装x
,面试官才能能佩服,佩服才能高薪,高薪才能走向人生巅峰!
总结
我们本期解决了编辑器选型问题,接下来,就要开始做编译器,的处理了 ,
欲知后事如何,且听下回分解
,其实我也想这回分解的,但是东家不让啊!