CodeasilyX增强版 - 可回放的webIDE

514 阅读4分钟

时隔多年,我叒更新codeasily了🤣,不了解codeasily的朋友可以回顾我之前的文章 CodeasilyX - 可记录交互行为的代码编辑器,这次带来了编辑器功能的增强,在上一版中只能做简单的静态html运行,这次加入了类似webpack的打包运行的能力,目前只做了react+typescript技术栈的运行环境,未来可以支持更多运行环境

此项目暂时下线,请看文章内演示

简介

这只是个人探索项目,有很多体验不佳的地方请多多包涵🙏

体验链接:x.codeasily.net

我预置了演示片段可以直接播放,简单来说这是一个在线代码编辑器,可以对代码文件编辑运行,然后可以对代码编辑的过程和动作进行捕捉并会录音,再回放捕捉过的动作

目前只能在自己的浏览器捕捉和回放,无法分享给其他人回放,能看到别人的回放的形式只有我预置的演示片段能回放,实现回放所需要的导出音频和动作脚本的功能是做了的,只是没开放。

Dpendencies

界面没有多大变化,只是左边中间多了一块Dependencies的模块,用过CodeSandbox的朋友一定了解这个,它可以添加任意npm包到当前项目中来,并且文件目录中的package.json里的Dpendencies也会随之更新,添加包后在代码里就可以引入包,就像本地开发环境一样

image-20210819160846585

添加包时可以模糊搜索,这里我用的是官网npmjs.org的接口,正常请求有跨域限制,通过服务器转发就可以了

image-20210819172148893

Sandpack

这次改版最大的变化就是下半部分这块预览组件

image-20210819161946665

为了能够实现类似webpack的打包能力,我找到了两个能实现的项目CodeSandbox以及 Stackblitz ,他们都有提供开源的sdk方便在自己项目中接入,

  • Stackblitz集成度较高,包含文件管理和编辑器以及预览框,就是embed的形式接入,配置项少灵活度低
  • CodeSandboxsandpack插件则只负责对代码解析和渲染,我可以自由开发文件管理和代码编辑器,而且还可以自己部署预览框里iframe的地址,这样能对跨域的iframe自定义通信做更多功能

所以我采用sandpack插件,它提供了解析代码并运行在对应容器中的能力,只要传入文件结构和代码内容即可生成结果,只负责运行,而我可以负责文件管理、代码编辑器以及包管理

iframe事件传递

由于预览组件的运行机制是运行在iframe元素中的,当鼠标经过iframe区域或者点击了iframe内的元素是需要被记录到的,所以还需要记录Iframe里的事件并作区分合并,播放时识别iframe的行为在iframe里执行

以前的预览框运行环境是当前项目开出来的iframe元素,没有跨域限制可以直接注入拦截事件的代码,现在换成了sandpack插件后,iframe地址会变成sandpack的bundler服务

好在我可以自建sandpack-bundler服务作为iframe的地址,我找到ensorrow/sandpack-bundler (github.com)这个项目已经帮我构建出了bundler服务的文件,直接静态服务启动即可,这样一来虽然是另起服务域名不一样,但是我依然可以在文件中加入拦截代码并通过postmessage传递给宿主记录动作

image-20210819171743444

代码高亮

sandpack支持的前端语法环境很多,但Monaco-editor代码编辑器有挺多语法不支持,jsx和tsx默认不支持,社区有提供jsx语法高亮的monarch配置:monaco-monarch-jsx/typescript-jsx.js at master · antimatter15/monaco-monarch-jsx (github.com)

拿到jsx配置后如下代码引入:

import { registerLanguage } from 'monaco-editor/esm/vs/basic-languages/_.contribution.js';
// ts支持jsx语法
registerLanguage({
  id: 'typescript',
  extensions: ['.ts', '.tsx'],
  aliases: ['TypeScript', 'ts', 'typescript'],
  mimetypes: ['text/typescript'],
  loader: function () { return import('./jsx-language'); }
});
// js文件支持jsx语法
registerLanguage({
  id: 'javascript',
  extensions: ['.js', '.jsx'],
  aliases: ['javascript', 'js'],
  mimetypes: ['text/javascript'],
  loader: function () { return import('@/utils/jsx-language'); }
});
// 解决jsx语法错误提示
monaco.languages.typescript.typescriptDefaults.setCompilerOptions({
  jsx: monaco.languages.typescript.JsxEmit.React,
  jsxFactory: 'React.createElement',
  reactNamespace: 'React',
  allowNonTsExtensions: true,
  allowJs: true,
  target: monaco.languages.typescript.ScriptTarget.Latest,
});

小结

Codeasily一直是我用来技术探索的项目,它一直陪伴着我学习成长,虽然没什么人关注,但也乐在其中,享受过程,感受进步,自娱自乐😉

未来也希望能探索更多好玩的技术,我会一直维护codeasily.net这个域名(๑•̀ㅂ•́)و✧