最近新开了一个vue3的项目,一个充满坑的旅程,下面就简单记载一下,vue3中codemirror的简单实用,以及所遇到的一些问题。
目前项目的依赖库版本如下:
"axios": "^0.21.1",
"element-plus": "1.0.2-beta.46",
"js-cookie": "^2.2.1",
"mitt": "^3.0.0",
"vue": "3.0.11",
"vue-router": "4.0.4",
"vuex": "4.0.0",
"@vitejs/plugin-vue": "^1.2.2",
"@vitejs/plugin-vue-jsx": "^1.1.4",
"@vue/compiler-sfc": "3.0.11",
"dayjs": "^1.10.4",
"eslint": "^7.26.0",
"eslint-plugin-prettier": "^3.4.0",
"sass": "^1.32.13",
"vite": "^2.3.3"
vite, vue, element-plus是频繁发版,直接升级会遇到很多问题,所以没有用最新的版本,等基本稳定后再更新吧。。。如果codemirror有支持vue3的版本了,也希望大佬们可以不吝赐教。
引入codemirror依赖
npm官方库有一个vue-codemirror,之前vue2.X版本都是用的这个库做集成(npm i vue-codemirror),但vue3引入该依赖使用后会报错。所以项目中,我是用的codemirror github.com/codemirror/…的代码,放进本地项目目录。目录如下图:
由于项目中仅需要支持sql,所以我只引入了sql,如果有其它语言的需求,也是直接copy放进mode目录里。
codemirror简单使用
废话少说,直接上代码。
封装code组件
<template>
<div>
<el-input
:id="`code-editor-${currentTimeStamp}`"
type="textarea"
:rows="row"
placeholder="请输入SQL"
v-model="currentCode"
>
</el-input>
</div>
</template>
<script setup>
import { defineProps, onMounted, ref, useContext, watch, defineEmit } from 'vue'
// 引入相关依赖
import '@/cm5/lib/codemirror.js'
import '@/cm5/lib/codemirror.css'
import '@/cm5/mode/sql/sql.js'
let codeEditor = null
const props = defineProps({
code: {
type: String,
default: ''
},
row: {
type: Number,
default: 6
},
readOnly: {
type: Boolean,
default: false
},
needWatch: {
type: Boolean,
default: true
}, // 此处是为了解决多个codemirror来回显示,值错乱问题;设置成false,不会自动更新值,需要父组件手动赋值
lineNumbers: {
type: Boolean,
default: true
}
})
const emit = defineEmit(['onBlur'])
const currentCode = ref('')
// 确保codemirror唯一
const currentTimeStamp = new Date().getTime()
watch(props, (newProps) => {
if (codeEditor && props.needWatch) {
if (newProps.code !== currentCode.value) {
currentCode.value = newProps.code
codeEditor.setValue(newProps.code)
}
}
})
onMounted(() => {
codeEditor = CodeMirror.fromTextArea(
document.getElementById(`code-editor-${currentTimeStamp}`),
{
lineNumbers: props.lineNumbers,
mode: 'text/x-sql',
lineWrapping: true,
readOnly: props.readOnly,
autoRefresh: true
}
)
// 失去焦点监听
codeEditor.on('blur', (instance, event) => {
emit('onBlur')
})
if (props.needWatch) {
currentCode.value = props.code
codeEditor.setValue(props.code || '')
}
})
// 获取codemirror值
const getValue = () => {
if (codeEditor) {
return codeEditor.getValue()
}
return ''
}
// 给codemirror赋值
const setValue = (newValue) => {
if (codeEditor) {
currentCode.value = newValue
codeEditor.setValue(newValue)
}
}
// 获取codemirror选中
const getSelection = () => {
if (codeEditor) {
return codeEditor.getSelection()
}
return null
}
// 手动刷新codemirror
const refresh = () => {
if (codeEditor) {
codeEditor.refresh()
}
}
const { expose } = useContext()
expose({
setValue,
getValue,
getSelection,
refresh
})
</script>
父组件使用
<code-editor
:code="sql"
ref="codeEditorRef"
>
</code-editor>
// 手动赋值
<code-editor
ref="codeEditorRef"
:need-watch="false"
>
</code-editor>
codemirror样式问题,整体左移
解决办法:官方说只需要添加autoRefresh: true属性,就可以,但我这边设置了没用。 后来经过不断查找发现,重新设置值之后,手动调用codeEditor.refresh(),就完美解决了。
多个tab切换,父组件手动赋值,没有正确显示内容
如果是在watch里监听到值的变化,再去赋值,很有可能codemirror还没有渲染出来,但是已经watch到值的变化,需要等dom渲染完毕之后再去赋值。
watch(props, async (newProps) => {
if (newProps.sql !== props.sql) {
await nextTick() // 为了解决切换会话,prettyCodeRef还未渲染,但是值已经watch到,所以等渲染完毕再手动赋值
if (prettyCodeRef.value) {
prettyCodeRef.value.setValue(newProps.sql || '')
prettyCodeRef.value.refresh() // 为了解决codemirror重新渲染之后,样式错乱(整体左移)
}
}
})