实现效果:
在网上找的好多都是没有在右侧显示代码的展示效果,并且没有完整地复制运行刷新下载的功能,所以小编带大家看看如何完整实现一个代码编辑器。
小编使用的是vue-codemirror,codemirror官网,话不多说直接上代码。
父组件
<template>
<div class="experience-main">
<experience-editor @codeChange="codeChange"></experience-editor>
<iframe :src="codePath" unselectable="on" frameborder="0" scrolling="no" style="width: calc(100% - 500px);height:100%;" ref="myIframe" id="codeIframe" allowfullscreen></iframe>
</div>
</template>
<script>
import { mapState } from 'vuex';
import ExperienceEditor from '@/views/experience/experience-editor.vue';
export default {
name:'experience-main',
watch: {
// 因为需求是多个html代码展示效果,所以嵌套的iframe地址是动态的
codePath(newValue,oldValue) {
if(newValue !== 'about:blank' && newValue !== 'about:blank#'){
let code = this.readFile(newValue);
this.writeCode(code);
}
}
},
components: {
ExperienceEditor
},
computed: {
...mapState(['codePath'])
},
methods: {
// 利用创建iframe空页面然后写入代码
codeChange(val){
if(this.codePath !== 'about:blank'){
this.$store.commit("updateCodePath", 'about:blank');
} else{
this.$store.commit("updateCodePath", 'about:blank#');
}
this.writeCode(val);
},
// 向iframe空页面写代码
writeCode(code){
const iframe = document.querySelector('#codeIframe');
iframe.onload = function(){
iframe.contentWindow.document.open();
iframe.contentWindow.document.write(code);
iframe.contentWindow.document.close();
};
},
// 读取文件内代码内容转化成字符串
readFile(filePath) {
let xhr = null
if (window.XMLHttpRequest) {
xhr = new XMLHttpRequest()
} else {
xhr = new ActiveXObject('Microsoft.XMLHTTP')
}
const okStatus = document.location.protocol === 'file' ? 0 : 200
xhr.open('GET', filePath, false)
xhr.overrideMimeType('text/html;charset=utf-8')
xhr.send(null)
return xhr.status === okStatus ? xhr.responseText : null
},
},
}
</script>
子组件
<template>
<div class="experience-editor">
<div class="experience-editor-header">
<div class="experience-editor-header-title">源代码编辑器</div>
<div class="experience-editor-header-right">
<div class="experience-editor-header-right-box" v-clipboard:copy="code" v-clipboard:success="onCopy" v-clipboard:error="onError">
<i class="el-icon-document-copy"></i>
复制
</div>
<div class="experience-editor-header-right-box" @click="play">
<i class="el-icon-video-play"></i>
运行
</div>
<div class="experience-editor-header-right-box" @click="refresh">
<i class="el-icon-refresh"></i>
刷新
</div>
<a :href="baseCodePath" :download="codePathName">
<div class="experience-editor-header-right-box">
<i class="el-icon-download"></i>
下载
</div>
</a>
</div>
</div>
<codemirror ref="myCm" v-model="code" :options="cmOptions" @changes="onChanges" class="experience-editor-code"></codemirror>
</div>
</template>
<script>
import { mapState } from 'vuex';
import { codemirror } from 'vue-codemirror';
import 'codemirror/lib/codemirror.css';
import "codemirror/theme/gruvbox-dark.css"; // 引入主题样式,根据设置的theme的主题引入
import 'codemirror/mode/htmlmixed/htmlmixed.js' // html代码高亮
export default {
name:'experience-editor',
data() {
return {
baseCode: '',
code: '',
cmOptions: {
tabSize: 4,
mode: 'htmlmixed', // htmlcssjs可以用这个,其他语言可以看官网的参数
theme: 'gruvbox-dark', // 主题和引入对应
lineWrapping: true, // 自动换行
}
}
},
watch: {
codePath(newValue,oldValue) {
if(newValue !== 'about:blank' && newValue !== 'about:blank#'){
this.code = this.readFile(newValue);
this.baseCode = this.code;
}
}
},
created() {
this.code = this.readFile(this.codePath);
this.baseCode = this.code;
},
components: {
codemirror
},
computed: {
// 把名称过滤出来以便下载
codePathName() {
let path = this.baseCodePath;
let site = path.lastIndexOf("\/");
return path.substring(site + 1, path.length);
},
...mapState(['codePath','baseCodePath']) // baseCodePath是为了防止切换html或者改变代码内容地址变化
},
methods: {
play() {
// 调用父组件的方法
this.$emit('codeChange',this.code);
},
refresh() {
this.code = this.baseCode;
this.$emit('codeChange',this.code);
},
// 复制成功时的回调函数
onCopy (e) {
this.$message.success("内容已复制到剪切板!"); // 使用的element-ui插件
},
// 复制失败时的回调函数
onError (e) {
this.$message.error("抱歉,复制失败!");
},
onChanges(val) {
this.code = this.$refs.myCm.content;
},
readFile(filePath) {
let xhr = null
if (window.XMLHttpRequest) {
xhr = new XMLHttpRequest()
} else {
xhr = new ActiveXObject('Microsoft.XMLHTTP')
}
const okStatus = document.location.protocol === 'file' ? 0 : 200
xhr.open('GET', filePath, false)
xhr.overrideMimeType('text/html;charset=utf-8')
xhr.send(null)
return xhr.status === okStatus ? xhr.responseText : null
}
},
}
</script>
主要是利用了iframe嵌套页面来展示代码效果,通过XMLHttpRequest获取文件代码内容传入编辑器,当用户改变代码内容,write空页面代码。
有一个问题,现在利用的是iframe创建空页面about:blank来写入代码内容,需要about:blank和about:blank#地址切换展示,不然会导致写入失败报错,如果有更好的办法评论区留言给我。
原创不易,请多多点赞评论支持,感谢!!!