示範什麼是代碼編輯器

示範
當我們看到這個編輯器的時候,你有沒有好奇這是這麼做出來的?如果是讓你來做,你會怎麼做?
閑扯
學無止境,我們現在的技術都是基于前一代人之上做出來的,要想成為一個進階/資深的前端,重點不是你能創造多少東西,而是你知道多少東西。有沒有解決問題的能力。遇到問題能不能找到最優解,為公司減少成本的同時提升效率。系統性的解決問題,提高代碼的維護性、穩定性、可擴充行等等。是以現代社會是一個認知的社會,隻有不斷的突破自己的認知,才能夠成為更優秀的人。
最近在搞公衆号,雖然公衆号已經寫了快兩年了,但是一直沒有推廣過。最近打算推廣,加了好多大佬的微信,我感覺我之前做的事情就是小巫見大巫,根本不值一提,前方還有好長的路要走,我隐隐感覺有點興奮,因為隻有迎難而上、才能迎刃而解。關注我公衆号,前路漫漫,一起修行!
正題
當我們想做一個事情的時候,往往最難的不是做,而是不知道從哪做起,怎麼做?我的每篇文章都會講我是如何去一點點解決問題的,希望能夠盡我的綿薄之力幫助有心之人。
- 看到網站之後不要急着去百度,因為百度有太多無用的資訊幹擾你,而且這些無用的資訊很可能會把你的注意力轉移,最後忘了你為什麼要百度!
- 以 codepen 官網為例,我是如何去查他用了什麼技術?
- 思考,這種編輯器的功能一定是有開源庫的,因為好多網站都使用過,那麼順着思路走,找到這個開源庫的名字,我們就完成一半了。
- 怎麼找,首先右擊打開檢查,檢視 Network 有沒有有用的資訊,比如加載了哪個js,在js源碼中找到一些線索(一般都會被打包過了,找到的幾率不大)。然後打開元素檢查,查找 class 名稱有沒有一些蹊跷
- 為什麼找 class ,因為 class 最能直覺的找到表達者的意圖
class
我們找到了一個很可疑的單詞 CodeMirror
接下來去 github 上搜尋一下 CodeMirror
class
果然被我們找到了,點進去檢視他的用法。接下來你應該知道怎麼做了~
動工
上面講解的是我如何找工具的方法,我現在使用的不是 CodeMirror,但是我也是通過這種方法找到的。我接下來用 monacoEditor 來講解我的做法。
加載 monaco 腳本
這是一段加載 monaco 的js。主要邏輯就是 load 一段 js,将 monaco 注冊到 window 上
export default {
// https://as.alipayobjects.com/g/cicada/monaco-editor-mirror/0.6.1/min/vs/loader.js
load(srcPath = 'https://as.alipayobjects.com/g/cicada/monaco-editor-mirror/0.6.1/min', callback) {
if (window.monaco) {
callback();
return;
}
const config = {
paths: {
vs: srcPath + '/vs'
}
};
const loaderUrl = `${config.paths.vs}/loader.js`;
const onGotAmdLoader = () => {
if (window.LOADER_PENDING) {
window.require.config(config);
}
window.require(['vs/editor/editor.main'], () => {
callback();
});
// 當AMD加載器已被加載時調用延遲的回調
if (window.LOADER_PENDING) {
window.LOADER_PENDING = false;
const loaderCallbacks = window.LOADER_CALLBACKS;
if (loaderCallbacks && loaderCallbacks.length) {
let currentCallback = loaderCallbacks.shift();
while (currentCallback) {
currentCallback.fn.call(currentCallback.window);
currentCallback = loaderCallbacks.shift();
}
}
}
};
if (window.LOADER_PENDING) {
// 我們需要避免加載多個loader.js時
// 有多個編輯器同時加載延遲調用回調除了第一個
window.LOADER_CALLBACKS = window.LOADER_CALLBACKS || [];
window.LOADER_CALLBACKS.push({
window: this,
fn: onGotAmdLoader
});
} else {
if (typeof window.require === 'undefined') {
const loaderScript = window.document.createElement('script');
loaderScript.type = 'text/javascript';
loaderScript.src = loaderUrl;
loaderScript.addEventListener('load', onGotAmdLoader);
window.document.body.appendChild(loaderScript);
window.LOADER_PENDING = true;
} else {
onGotAmdLoader();
}
}
}
}
複制
封裝元件
寫一個元件将加載執行的邏輯封裝在這個元件裡,暴露出一些 api,提供給調用者使用
<template>
<div :style="style"></div>
</template>
<script>
import monacoLoader from './MonacoLoader'
const debounce = require('lodash.debounce');
export default {
props: {
// 編輯器的寬,預設 100%
width: { type: [String, Number], default: '100%' },
// 編輯器的高,預設 100%
height: { type: [String, Number], default: '100%' },
// 傳進來的代碼,一段字元串
code: { type: String, default: '// code \n' },
// 資源路徑
srcPath: { type: String },
// 預設使用 js
language: { type: String, default: 'javascript' },
// 主題 預設 vs-dark
theme: { type: String, default: 'vs-dark' }, // vs, hc-black
// 一些 monaco 配置參數
options: { type: Object, default: () => {} },
// 截流
changeThrottle: { type: Number, default: 0 }
},
// 加載資源
created() {
this.fetchEditor();
},
// 銷毀
destroyed() {
this.destroyMonaco();
},
computed: {
style() {
const { width, height } = this;
const fixedWidth = width.toString().indexOf('%') !== -1 ? width : `${width}px`;
const fixedHeight = height.toString().indexOf('%') !== -1 ? height : `${height}px`;
return {
width: fixedWidth,
height: fixedHeight,
};
},
editorOptions() {
return Object.assign({}, this.defaults, this.options, {
value: this.code,
language: this.language,
theme: this.theme
});
}
},
data() {
return {
defaults: {
selectOnLineNumbers: true,
roundedSelection: false,
readOnly: false,
cursorStyle: 'line',
automaticLayout: false,
glyphMargin: true
}
}
},
methods: {
editorHasLoaded(editor, monaco) {
this.editor = editor;
this.monaco = monaco;
this.editor.onDidChangeModelContent(event =>
this.codeChangeHandler(editor, event)
);
this.$emit('mounted', editor);
},
codeChangeHandler: function(editor) {
if (this.codeChangeEmitter) {
this.codeChangeEmitter(editor);
} else {
this.codeChangeEmitter = debounce(
function(editor) {
this.$emit('codeChange', editor);
},
this.changeThrottle
);
this.codeChangeEmitter(editor);
}
},
fetchEditor() {
monacoLoader.load(this.srcPath, this.createMonaco);
},
createMonaco() {
this.editor = window.monaco.editor.create(this.$el, this.editorOptions);
this.editorHasLoaded(this.editor, window.monaco);
},
destroyMonaco() {
if (typeof this.editor !== 'undefined') {
this.editor.dispose();
}
}
}
};
</script>
複制
完成示範
使用元件,将元件顯示在頁面上。并将 console.log 收集起來,執行完代碼之後将其列印在螢幕上。
最後會有示範
<template>
<div>
<MonacoEditor
height="300"
language="typescript"
:code="code"
:editorOptions="options"
@codeChange="onCodeChange"
></MonacoEditor>
<div class="console">{{ res }}</div>
</div>
</template>
<script>
import MonacoEditor from '../../components/vue-monaco-editor';
let logStore = [];
export default {
components: {
MonacoEditor
},
data() {
return {
result: 'noop',
// 預設 code
code:
`const noop = () => {
console.log('noop')
}
noop()
`,
options: {}
};
},
methods: {
clear() {
this.result = '';
logStore = [];
},
// 重寫consolelog,并儲存在logStore内
overwriteConsoleLog() {
console.log = function(...arg) {
logStore.push(arg);
};
},
// 抽象出一層修飾層
modify(e) {
if (typeof e === 'object') e = JSON.stringify(e);
return e;
},
// 輸出
printf(oriConsole) {
const _this = this
logStore.forEach(item => {
function str() {
return item.map(_this.modify);;
}
oriConsole(str(item));
this.result +=
str(item)
.join(' ') + '\n';
});
console.log = oriConsole;
},
onCodeChange(code) {
// 儲存 console.log 對象
const oriConsole = console.log;
// 清空副作用
this.clear();
// 重寫 console.log,為了将控制台列印值輸出在頁面上
this.overwriteConsoleLog();
// 擷取代碼的片段字元串
const v = code.getValue();
try {
// 執行代碼
eval(v);
// 将控制台列印值輸出在頁面上
this.printf(oriConsole)
} catch (error) {
console.error(error)
}
}
}
};
</script>
<style lang="scss">
.editor {
width: 100%;
height: 100vh;
margin-top: 50px;
}
.console {
height: 500px;
background: orange;
font-size: 40px;
white-space: pre-wrap;
}
</style>
複制
效果示範
小結
又到了小結時刻,當我們看見一個很厲害的東西的時候,不要害怕,其實你也可以,大部分的功能其實已經被别人封裝好了,我們都是站在巨人的肩膀上。
我會經常分享一些自己工作經驗、技術體會、做的小玩意等等。更大家一起學習。
看别人十遍,不如自己動手寫一遍,我的這些源碼和文章都在這裡,可以自己下載下傳下來運作一下試試。
https://github.com/luoxue-victor/source-code/tree/master/src/views/monacoEditor