# 相关网址
- Vue 官方插件库推荐的集成实现
- 这个实现做的比较全面,但不支持动态语法高亮的切换
- codemirror 支持的语言类型
- codemirror 官网
# 具体实现
- 首先需要运行
npm i codemirror --save
在项目中安装对应组件
<template> | |
<div class="in-coder-panel"> | |
<textarea ref="textarea"></textarea> | |
<el-select class="code-mode-select" v-model="mode" | |
@change="changeMode"> | |
<el-option v-for="mode in modes" | |
:key="mode.value" :label="mode.label" :value="mode.value"> | |
</el-option> | |
</el-select> | |
</div> | |
</template> | |
<script type="text/ecmascript-6"> | |
// 引入全局实例 | |
import _CodeMirror from 'codemirror' | |
// 核心样式 | |
import 'codemirror/lib/codemirror.css' | |
// 引入主题后还需要在 options 中指定主题才会生效 | |
import 'codemirror/theme/cobalt.css' | |
// 需要引入具体的语法高亮库才会有对应的语法高亮效果 | |
//codemirror 官方其实支持通过 /addon/mode/loadmode.js 和 /mode/meta.js 来实现动态加载对应语法高亮库 | |
// 但 vue 貌似没有无法在实例初始化后再动态加载对应 JS ,所以此处才把对应的 JS 提前引入 | |
import 'codemirror/mode/javascript/javascript.js' | |
import 'codemirror/mode/css/css.js' | |
import 'codemirror/mode/xml/xml.js' | |
import 'codemirror/mode/clike/clike.js' | |
import 'codemirror/mode/markdown/markdown.js' | |
import 'codemirror/mode/python/python.js' | |
import 'codemirror/mode/r/r.js' | |
import 'codemirror/mode/shell/shell.js' | |
import 'codemirror/mode/sql/sql.js' | |
import 'codemirror/mode/swift/swift.js' | |
import 'codemirror/mode/vue/vue.js' | |
// 尝试获取全局实例 | |
const CodeMirror = window.CodeMirror || _CodeMirror | |
export default { | |
name: 'in-coder', | |
props: { | |
// 外部传入的内容,用于实现双向绑定 | |
value: String, | |
// 外部传入的语法类型 | |
language: { | |
type: String, | |
default: null | |
} | |
}, | |
data () { | |
return { | |
// 内部真实的内容 | |
code: '', | |
// 默认的语法类型 | |
mode: 'javascript', | |
// 编辑器实例 | |
coder: null, | |
// 默认配置 | |
options: { | |
// 缩进格式 | |
tabSize: 2, | |
// 主题,对应主题库 JS 需要提前引入 | |
theme: 'cobalt', | |
// 显示行号 | |
lineNumbers: true, | |
line: true | |
}, | |
// 支持切换的语法高亮类型,对应 JS 已经提前引入 | |
// 使用的是 MIME-TYPE ,不过作为前缀的 text/ 在后面指定时写死了 | |
modes: [{ | |
value: 'css', | |
label: 'CSS' | |
}, { | |
value: 'javascript', | |
label: 'Javascript' | |
}, { | |
value: 'html', | |
label: 'XML/HTML' | |
}, { | |
value: 'x-java', | |
label: 'Java' | |
}, { | |
value: 'x-objectivec', | |
label: 'Objective-C' | |
}, { | |
value: 'x-python', | |
label: 'Python' | |
}, { | |
value: 'x-rsrc', | |
label: 'R' | |
}, { | |
value: 'x-sh', | |
label: 'Shell' | |
}, { | |
value: 'x-sql', | |
label: 'SQL' | |
}, { | |
value: 'x-swift', | |
label: 'Swift' | |
}, { | |
value: 'x-vue', | |
label: 'Vue' | |
}, { | |
value: 'markdown', | |
label: 'Markdown' | |
}] | |
} | |
}, | |
mounted () { | |
// 初始化 | |
this._initialize() | |
}, | |
methods: { | |
// 初始化 | |
_initialize () { | |
// 初始化编辑器实例,传入需要被实例化的文本域对象和默认配置 | |
this.coder = CodeMirror.fromTextArea(this.$refs.textarea, this.options) | |
// 编辑器赋值 | |
this.coder.setValue(this.value || this.code) | |
// 支持双向绑定 | |
this.coder.on('change', (coder) => { | |
this.code = coder.getValue() | |
if (this.$emit) { | |
this.$emit('input', this.code) | |
} | |
}) | |
// 尝试从父容器获取语法类型 | |
if (this.language) { | |
// 获取具体的语法类型对象 | |
let modeObj = this._getLanguage(this.language) | |
// 判断父容器传入的语法是否被支持 | |
if (modeObj) { | |
this.mode = modeObj.label | |
} | |
} | |
}, | |
// 获取当前语法类型 | |
_getLanguage (language) { | |
// 在支持的语法类型列表中寻找传入的语法类型 | |
return this.modes.find((mode) => { | |
// 所有的值都忽略大小写,方便比较 | |
let currentLanguage = language.toLowerCase() | |
let currentLabel = mode.label.toLowerCase() | |
let currentValue = mode.value.toLowerCase() | |
// 由于真实值可能不规范,例如 java 的真实值是 x-java ,所以讲 value 和 label 同时和传入语法进行比较 | |
return currentLabel === currentLanguage || currentValue === currentLanguage | |
}) | |
}, | |
// 更改模式 | |
changeMode (val) { | |
// 修改编辑器的语法配置 | |
this.coder.setOption('mode', `text/${val}`) | |
// 获取修改后的语法 | |
let label = this._getLanguage(val).label.toLowerCase() | |
// 允许父容器通过以下函数监听当前的语法值 | |
this.$emit('language-change', label) | |
} | |
} | |
} | |
</script> | |
<style lang="stylus" rel="stylesheet/stylus"> | |
.in-coder-panel | |
flex-grow 1 | |
display flex | |
position relative | |
.CodeMirror | |
flex-grow 1 | |
z-index 1 | |
.CodeMirror-code | |
line-height 19px | |
.code-mode-select | |
position absolute | |
z-index 2 | |
right 10px | |
top 10px | |
max-width 130px | |
</style> |
# options 可以使用的参数
CodeMirror 函数和它的 fromTextArea 方法都可以使用一个配置对象作为第二个参数。
# value: string | CodeMirror.Doc
编辑器的初始值(文本),可以是字符串或者 CodeMirror 文档对象 (不同于 HTML 文档对象)。
# mode: string | object
通用的或者在 CodeMirror 中使用的与 mode 相关联的 mime,当不设置这个值的时候,会默认使用第一个载入的 mode 定义文件。一般地,会使用关联的 mime 类型来设置这个值;除此之外,也可以使用一个带有 name 属性的对象来作为值(如:{name: “JavaScript”, json: true})。可以通过访问 CodeMirror.modes 和 CodeMirror.mimeModes 获取定义的 mode 和 MIME。
# lineSeparator: string|null
明确指定编辑器使用的行分割符(换行符)。默认(值为 null)情况下,文档会被 CRLF (以及单独的 CR, LF) 分割,单独的 LF 会在所有的输出中用作换行符(如:getValue)。当指定了换行字符串,行就只会被指定的串分割。
# theme: string
配置编辑器的主题样式。要使用主题,必须保证名称为 .cm-s-[name] (name 是设置的 theme 的值) 的样式是加载上了的。当然,你也可以一次加载多个主题样式,使用方法和 html 和使用类一样,如: theme: foo bar,那么此时需要 cm-s-foo cm-s-bar 这两个样式都已经被加载上了。
# indentUnit: integer
缩进单位,值为空格数,默认为 2 。
# smartIndent: boolean
自动缩进,设置是否根据上下文自动缩进(和上一行相同的缩进量)。默认为 true。
# abSize: integer
tab 字符的宽度,默认为 4 。
# indentWithTabs: boolean
在缩进时,是否需要把 n*tab 宽度个空格替换成 n 个 tab 字符,默认为 false 。
# electricChars: boolean
在输入可能改变当前的缩进时,是否重新缩进,默认为 true (仅在 mode 支持缩进时有效)。
# specialChars: RegExp
需要被占位符 (placeholder) 替换的特殊字符的正则表达式。最常用的是非打印字符。默认为:/[\u0000-\u0019\u00ad\u200b-\u200f\u2028\u2029\ufeff]/。
# specialCharPlaceholder: function(char) → Element
这是一个接收由 specialChars 选项指定的字符作为参数的函数,此函数会产生一个用来显示指定字符的 DOM 节点。默认情况下,显示一个红点(・),这个红点有一个带有前面特殊字符编码的提示框。
# keyMap: string
配置快捷键。默认值为 default,即 codemorrir.js 内部定义。其它在 key map 目录下。
# extraKeys: object
给编辑器绑定与前面 keyMap 配置不同的快捷键。
# lineWrapping: boolean
在长行时文字是换行 (wrap) 还是滚动 (scroll),默认为滚动 (scroll)。
# lineNumbers: boolean
是否在编辑器左侧显示行号。
# firstLineNumber: integer
行号从哪个数开始计数,默认为 1 。
# lineNumberFormatter: function(line: integer) → string
使用一个函数设置行号。
# gutters: array
用来添加额外的 gutter(在行号 gutter 前或代替行号 gutter)。值应该是 CSS 名称数组,每一项定义了用于绘制 gutter 背景的宽度(还有可选的背景)。为了能明确设置行号 gutter 的位置(默认在所有其它 gutter 的右边),也可以包含 CodeMirror-linenumbers 类。类名是用于传给 setGutterMarker 的键名 (keys)。
# fixedGutter: boolean
设置 gutter 跟随编辑器内容水平滚动(false)还是固定在左侧(true 或默认)。
# scrollbarStyle: string
设置滚动条。默认为”native”,显示原生的滚动条。核心库还提供了”null” 样式,此样式会完全隐藏滚动条。Addons 可以设置更多的滚动条模式。
# coverGutterNextToScrollbar: boolean
当 fixedGutter 启用,并且存在水平滚动条时,在滚动条最左侧默认会显示 gutter,当此项设置为 true 时,gutter 会被带有 CodeMirror-gutter-filler 类的元素遮挡。
# inputStyle: string
选择 CodeMirror 处理输入和焦点的方式。核心库定义了 textarea 和 contenteditable 输入模式。在移动浏览器上,默认是 contenteditable,在桌面浏览器上,默认是 textarea。在 contenteditable 模式下对 IME 和屏幕阅读器支持更好。
# readOnly: boolean|string
编辑器是否只读。如果设置为预设的值 “nocursor”,那么除了设置只读外,编辑区域还不能获得焦点。
# showCursorWhenSelecting: boolean
在选择时是否显示光标,默认为 false。
# lineWiseCopyCut: boolean
启用时,如果在复制或剪切时没有选择文本,那么就会自动操作光标所在的整行。
# undoDepth: integer
最大撤消次数,默认为 200(包括选中内容改变事件) 。
# historyEventDelay: integer
在输入或删除时引发历史事件前的毫秒数。
# tabindex: integer
编辑器的 tabindex。
# autofocus: boolean
是否在初始化时自动获取焦点。默认情况是关闭的。但是,在使用 textarea 并且没有明确指定值的时候会被自动设置为 true。
# 低级选项
下面的选项仅用于一些特殊情况。
# dragDrop: boolean
是否允许拖放,默认为 true。
# allowDropFileTypes: array
默认为 null。当设置此项时,只接收包含在此数组内的文件类型拖入编辑器。文件类型为 MIME 名称。
# cursorBlinkRate: number
光标闪动的间隔,单位为毫秒。默认为 530。当设置为 0 时,会禁用光标闪动。负数会隐藏光标。
# cursorScrollMargin: number
当光标靠近可视区域边界时,光标距离上方和下方的距离。默认为 0 。
# cursorHeight: number
光标高度。默认为 1,也就是撑满行高。对一些字体,设置 0.85 看起来会更好。
# resetSelectionOnContextMenu: boolean
设置在选择文本外点击打开上下文菜单时,是否将光标移动到点击处。默认为 true。
# workTime, workDelay: number
通过一个假的后台线程高亮 workTime 时长,然后使用 timeout 休息 workDelay 时长。默认为 200 和 300 。
# pollInterval: number
指明 CodeMirror 向对应的 textarea 滚动(写数据)的速度(获得焦点时)。大多数的输入都是通过事件捕获,但是有的输入法(如 IME)在某些浏览器上并不会生成事件,所以使用数据滚动。默认为 100 毫秒。
# flattenSpans: boolean
默认情况下,CodeMirror 会将使用相同 class 的两个 span 合并成一个。通过设置此项为 false 禁用此功能。
# addModeClass: boolean
当启用时(默认禁用),会给每个标记添加额外的表示生成标记的 mode 的以 cm-m 开头的 CSS 样式类。例如,XML mode 产生的标记,会添加 cm-m-xml 类。
# maxHighlightLength: number
当需要高亮很长的行时,为了保持响应性能,当到达某些位置时,编辑器会直接将其他行设置为纯文本 (plain text)。默认为 10000,可以设置为 Infinity 来关闭此功能。
# viewportMargin: integer
指定当前滚动到视图中内容上方和下方要渲染的行数。这会影响到滚动时要更新的行数。通常情况下应该使用默认值 10。可以设置值为 Infinity 始终渲染整个文档。注意:这样设置在处理大文档时会影响性能。