快速搭建一个代码在线编辑预览工具(下)
支持预处理器
除了基本的html
、js
和css
,作为一个强大的工具,我们有必要支持一下常用的预处理器,比如html
的pug
,js
的TypeScript
及css
的less
等,实现思路相当简单,加载对应预处理器的转换器,然后转换一下即可。
动态切换编辑器语言
Monaco Editor
想要动态修改语言的话我们需要换一种方式来设置文档,上文我们是创建编辑器的同时直接把语言通过language
选项传递进去的,然后使用setValue
来设置文档内容,这样后期无法再动态修改语言,我们修改为切换文档模型的方式:
// 创建编辑器
editor = monaco.editor.create(editorEl.value, {
minimap: {
enabled: false, // 关闭小地图
},
wordWrap: 'on', // 代码超出换行
theme: 'vs-dark', // 主题
fontSize: 18,
fontFamily: 'MonoLisa, monospace',
})
// 更新编辑器文档模型
const updateDoc = (code, language) => {
if (!editor) {
return
}
// 获取当前的文档模型
let oldModel = editor.getModel()
// 创建一个新的文档模型
let newModel = monaco.editor.createModel(code, language)
// 设置成新的
editor.setModel(newModel)
// 销毁旧的模型
if (oldModel) {
oldModel.dispose()
}
}
加载转换器
转换器的文件我们都放在/public/parses/
文件夹下,然后进行动态加载,即选择了某个预处理器后再去加载对应的转换器资源,这样可以节省不必要的请求。
异步加载js
我们使用loadjs这个小巧的库,新增一个load.js
:
// 记录加载状态
const preprocessorLoaded = {
html: true,
javascript: true,
css: true,
less: false,
scss: false,
sass: false,
stylus: false,
postcss: false,
pug: false,
babel: false,
typescript: false
}
// 某个转换器需要加载多个文件
const resources = {
postcss: ['postcss-cssnext', 'postcss']
}
// 异步加载转换器的js资源
export const load = (preprocessorList) => {
// 过滤出没有加载过的资源
let notLoaded = preprocessorList.filter((item) => {
return !preprocessorLoaded[item]
})
if (notLoaded.length <= 0) {
return
}
return new Promise((resolve, reject) => {
// 生成加载资源的路径
let jsList = []
notLoaded.forEach((item) => {
let _resources = (resources[item] || [item]).map((r) => {
return `/parses/${r}.js`
})
jsList.push(..._resources)
})
loadjs(jsList, {
returnPromise: true
}).then(() => {
notLoaded.forEach((item) => {
preprocessorLoaded[item] = true
})
resolve()
}).catch((err) => {
reject(err)
})
})
}
然后修改一下上文预览部分的run
方法:
const run = async () => {
let h = editData.value.code.HTML.language
let j = editData.value.code.JS.language
let c = editData.value.code.CSS.language
await load([h, j, c])
// ...
}
转换
所有代码都使用转换器转换一下,因为有的转换器是同步方式的,有的是异步方式的,所以我们统一使用异步来处理,修改一下run
方法:
const run = async () => {
// ...
await load([h, j, c])
let htmlTransform = transform.html(h, editData.value.code.HTML.content)
let jsTransform = transform.js(j, editData.value.code.JS.content)
let cssTransform = transform.css(c, editData.value.code.CSS.content)
Promise.all([htmlTransform, jsTransform, cssTransform])
.then(([htmlStr, jsStr, cssStr]) => {
// ...
})
.catch((error) => {
// ...
})
}
接下来就是最后的转换操作,下面只展示部分代码,完整代码有兴趣的可查看源码:
// transform.js
const html = (preprocessor, code) => {
return new Promise((resolve, reject) => {
switch (preprocessor) {
case 'html':
// html的话原封不动的返回
resolve(code)
break;
case 'pug':
// 调用pug的api来进行转换
resolve(window.pug.render(code))
default:
resolve('')
break;
}
})
}
const js = (preprocessor, code) => {
return new Promise((resolve, reject) => {
let _code = ''
switch (preprocessor) {
case 'javascript':
resolve(code)
break;
case 'babel':
// 调用babel的api来编译,你可以根据需要设置presets
_code = window.Babel.transform(code, {
presets: [
'es2015',
'es2016',
'es2017',
'react'
]
}).code
resolve(_code)
default:
resolve('')
break;
}
})
}
const css = (preprocessor, code) => {
return new Promise((resolve, reject) => {
switch (preprocessor) {
case 'css':
resolve(code)
break;
case 'less':
window.less.render(code)
.then(
(output) => {
resolve(output.css)
},
(error) => {
reject(error)
}
);
break;
default:
resolve('')
break;
}
})
}
可以看到很简单,就是调一下相关转换器的api
来转换一下,不过想要找到这些转换器的浏览器使用版本和api
可太难了,笔者基本都没找到,所以这里的大部分代码都是参考codepan的。
其他功能
另外还有一些实现起来简单,但是能很大提升用户体验的功能,比如添加额外的css
或js
资源,免去手写link
或script
标签的麻烦:
预设一些常用模板,比如vue3
、react
等,方便快速开始,免去写基本结构的麻烦:
有没有更快的方法
如果你看到这里,你一定会说这是哪门子快速搭建,那有没有更快的方法呢,当然有了,就是直接克隆本项目的仓库或者codepan,改改就可以使用啦~
结尾
本文从零开始介绍了如何搭建一个代码在线编辑预览的工具,粗糙实现总有不足之处,欢迎指出。
项目仓库code-run,欢迎star
。
- 点赞
- 收藏
- 关注作者
评论(0)