# 相关网址

  1. Vue 官方插件库推荐的集成实现
    • 这个实现做的比较全面,但不支持动态语法高亮的切换
  2. codemirror 支持的语言类型
  3. codemirror 官网

# 具体实现

  1. 首先需要运行 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 始终渲染整个文档。注意:这样设置在处理大文档时会影响性能。