2024年最全厉害了,手把手教你搭建一个代码在线编辑预览工具(1),2024年最新面试web前端自我介绍

最后更多分享:前端字节跳动真题解析

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

ps.在本文基础上笔者开发了一个完整的线上工具,带云端保存,地址:lxqnsys.com/code-run/,欢迎使用。

页面结构

====

image-20210427170009062.png

我挑了一个比较典型也比较好看的结构来仿照,默认布局上下分成四部分,工具栏、编辑器、预览区域及控制台,编辑器又分为三部分,分别是HTMLCSSJavaScript,其实就是三个编辑器,用来编辑代码。

各部分都可以拖动进行调节大小,比如按住js编辑器左边的灰色竖条向右拖动,那么js编辑器的宽度会减少,同时css编辑器的宽度会增加,如果向左拖动,那么css编辑器宽度会减少,js编辑器的宽度会增加,当css编辑器宽度已经不能再减少的时候css编辑器也会同时向左移,然后减少html的宽度。

在实现上,水平调节宽度和垂直调节高度原理是一样的,以调节宽度为例,三个编辑器的宽度使用一个数组来维护,用百分比来表示,那么初始就是100/3%,然后每个编辑器都有一个拖动条,位于内部的左侧,那么当按住拖动某个拖动条拖动时的逻辑如下:

1.把本次拖动瞬间的偏移量由像素转换为百分比;

2.如果是向左拖动的话,检测本次拖动编辑器的左侧是否存在还有空间可以压缩的编辑器,没有的话代表不能进行拖动;如果有的话,那么拖动时增加本次拖动编辑器的宽度,同时减少找到的第一个有空间的编辑器的宽度,直到无法再继续拖动;

3.如果是向右拖动的话,检测本次拖动编辑器及其右侧是否存在还有空间可以压缩的编辑器,没有的话也代表不能再拖动,如果有的话,找到第一个并减少该编辑器的宽度,同时增加本次拖动编辑器左侧第一个编辑器的宽度;

核心代码如下:

const onDrag = (index, e) => {

let client = this._dir === ‘v’ ? e.clientY : e.clientX

// 本次移动的距离

let dx = client - this._last

// 换算成百分比

let rx = (dx / this._containerSize) * 100

// 更新上一次的鼠标位置

this._last = client

if (dx < 0) {

// 向左/上拖动

if (!this.isCanDrag(‘leftUp’, index)) {

return

}

// 拖动中的编辑器增加宽度

if (this._dragItemList.value[index][this._prop] - rx < this.getMaxSize(index)) {

this._dragItemList.value[index][this._prop] -= rx

} else {

this._dragItemList.value[index][this._prop] = this.getMaxSize(index)

}

// 找到左边第一个还有空间的编辑器索引

let narrowItemIndex = this.getFirstNarrowItemIndex(‘leftUp’, index)

let _minSize = this.getMinSize(narrowItemIndex)

// 左边的编辑器要同比减少宽度

if (narrowItemIndex >= 0) {

// 加上本次偏移还大于最小宽度

if (this._dragItemList.value[narrowItemIndex][this._prop] + rx > _minSize) {

this._dragItemList.value[narrowItemIndex][this._prop] += rx

} else {

// 否则固定为最小宽度

this._dragItemList.value[narrowItemIndex][this._prop] = _minSize

}

}

} else if (dx > 0) {

// 向右/下拖动

if (!this.isCanDrag(‘rightDown’, index)) {

return

}

// 找到拖动中的编辑器及其右边的编辑器中的第一个还有空间的编辑器索引

let narrowItemIndex = this.getFirstNarrowItemIndex(‘rightDown’, index)

let _minSize = this.getMinSize(narrowItemIndex)

if (narrowItemIndex <= this._dragItemList.value.length - 1) {

let ax = 0

// 减去本次偏移还大于最小宽度

if (this._dragItemList.value[narrowItemIndex][this._prop] - rx > _minSize) {

ax = rx

} else {

// 否则本次能移动的距离为到达最小宽度的距离

ax = this._dragItemList.value[narrowItemIndex][this._prop] - _minSize

}

// 更新拖动中的编辑器的宽度

this._dragItemList.value[narrowItemIndex][this._prop] -= ax

// 左边第一个编辑器要同比增加宽度

if (index > 0) {

if (this._dragItemList.value[index - 1][this._prop] + ax < this.getMaxSize(index - 1)) {

this._dragItemList.value[index - 1][this._prop] += ax

} else {

this._dragItemList.value[index - 1][this._prop] = this.getMaxSize(index - 1)

}

}

}

}

}

复制代码

实现效果如下:

2021-04-29-19-15-42.gif

为了能提供多种布局的随意切换,我们有必要把上述逻辑封装一下,封装成两个组件,一个容器组件Drag.vue,一个容器的子组件DragItem.vueDragItem通过slot来显示其他内容,DragItem主要提供拖动条及绑定相关的鼠标事件,Drag组件里包含了上述提到的核心逻辑,维护对应的尺寸数组,提供相关处理方法给DragItem绑定的鼠标事件,然后只要根据所需的结构进行组合即可,下面的结构就是上述默认的布局:

复制代码

这部分代码较多,有兴趣的可以查看源码。

编辑器

===

目前涉及到代码编辑的场景基本使用的都是codemirror,因为它功能强大,使用简单,支持语法高亮、支持多种语言和主题等,但是为了能更方便的支持语法提示,本文选择的是微软的monaco-editor,功能和VSCode一样强大,VSCode有多强就不用我多说了,缺点是整体比较复杂,代码量大,内置主题较少。

monaco-editor支持多种加载方式,esm模块加载的方式需要使用webpack,但是vite底层打包工具用的是Rollup,所以本文使用直接引入js的方式。

在官网上下载压缩包后解压到项目的public文件夹下,然后参考示例的方式在index.html文件里添加:

复制代码

monaco-editor内置了10种语言,我们选择中文的,其他不用的可以直接删掉:

image-20210430163748892.png

接下来创建编辑器就可以了:

const editor = monaco.editor.create(

editorEl.value,// dom容器

{

value: props.content,// 要显示的代码

language: props.language,// 代码语言,css、javascript等

minimap: {

enabled: false,// 关闭小地图

},

wordWrap: ‘on’, // 代码超出换行

theme: ‘vs-dark’// 主题

}

)

复制代码

就这么简单,一个带高亮、语法提示、错误提示的编辑器就可以使用了,效果如下:

image-20210430154406199.png

其他几个常用的api如下:

// 设置文档内容

editor.setValue(props.content)

// 监听编辑事件

editor.onDidChangeModelContent((e) => {

console.log(editor.getValue())// 获取文档内容

})

// 监听失焦事件

editor.onDidBlurEditorText((e) => {

console.log(editor.getValue())

})

复制代码

预览

==

代码有了,接下来就可以渲染页面进行预览了,对于预览,显然是使用iframeiframe除了src属性外,HTML5还新增了一个属性srcdoc,用来渲染一段HTML代码到iframe里,这个属性IE目前不支持,不过vue3都要不支持IE了,咱也不管了,如果硬要支持也简单,使用write方法就行了:

iframeRef.value.contentWindow.document.write(htmlStr)

复制代码

接下来的思路就很清晰了,把htmlcssjs代码组装起来扔给srcdoc不就完了吗:

复制代码

const assembleHtml = (head, body) => {

return `

${head}

${body}

`

}

const run = () => {

let head = `

预览<\/title>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值