费了很大功夫终于完成了react的playGround,我很开心,这将又是一次编码功底的提升。谢了很多年的业务,很少关注这些小工具,其实这些小工具才是真正带领我们提升能力的臂膀。希望大家有时间也去敲一下,重新理解前端。现在就对这个工具做一个简单的总结。
1.需求
playground 就是一个代码演示器,他最大的优点就是实现代码在线编辑,然后实时获取渲染结果。还能通过分享链接的方式和同伴一起联调代码。同时你也可以下载代码,拿到文件包。最大限度的提高咱们组件库开发的效率。
在整个playground的页面上主要功能是:
1.编辑器编写代码,右侧预览区预览效果
2.编辑区支持react和ts,可以进行代码提示和ts类型提示。
3.预览区错误以抽屉的形式,显示在预览区的下侧,方便开发人员及时关注。
4.在fileList上可以切换文件,删除文件,还能修改文件名,也能新增文件。
5.页面右上角可以切换主题
6.页面右上角可以分享地址,实现多人开发。
7.页面右上角可以下载文件,实现代码移植。
2.实现
2.1布局
因为咱们的playground的编辑区和预览区的中界线是可以左右移动的,你可以自己写css实现,也可以使用组件库,我们选用:allotment;
2.2编辑器
编辑区的编辑器种类有很多,我们选择@monaco-editor/react,因为它可以编译jsx,ts,通过配置就能实现错误展示,还有api查找
这样使用起来更加接近咱们的编辑器vscode
2.3 ts类型检测
在正常的项目里面,我们使用tscofig.json来配置ts的检测,但是在这里,我们无法使用它,那就用@typescript/ata,它会帮我们在浏览器里面进行类型检测
2.4 数据中心context
编辑器里面出来的文件都是以字符串的形式存放在files对象里面的。
你会发现整个playground的数据中心就是整个context。不管是文件list,还是编辑器,还是预览器,还是地址分享,内容下载,还是主题色的切换,都是以context为中心来活动的。
2.5 fileList文件列表
fileList里面对文件的增删改查完全是操作context的结果。
2.6 预览区--编译代码
预览区就是一个iframe,我们创建一个iframe.html文件,然后将react入口文件main.tsx插入root下面去,然后拿到文件url就可以渲染了。
首先浏览器不认识jsx,也不认ts,在files里面的main.tsx是字符串的形式存储的,所以我们要用@babel/standalone去编译文件,把jsx转成React.createElement() 这样就能拿到一个浏览器可以认识的js文件了。
2.7 预览区--处理本地文件
光编译代码是不行的,因为我们的组件一般上面都会用 import 导入其他文件,比如 React 文件,比如本地组件App.tsx,现在我们就处理下import App from './App';
他是通过babel插件实现的,就是去监控babel的import, 如果我们的文件是本地文件,他一定存储在files里面,然后从files里面拿到具体的文件代码,然后处理。
本地文件有三种:css,json,js
1.css文件:在header下面创建一个style标签,然后将css代码插入标签里面就好了。
2.json文件:先把他转成js,就是代码前面加上export default,就变成导出数组了,然后获取内存地址URL.createObjectURL(new Blob([js], { type: 'application/javascript' }));
3.根据文件名,先去files里面拿到文件,然后编译,再获取内存地址:URL.createObjectURL(new Blob([js], { type: 'application/javascript' }));,此时的编译,就是一个递归过程,他会将下属文件里面所有的import地址转成内存地址。
2.8 处理包文件
处理import React from 'react'很重要,就是在html文件里面插入importmap类型的script,然后把包地址配置进去就好了。
script标签里面放入导入第三方的包地址,一定要注意它的type是importmap
这样导入是找不到具体的包的,所以必须用importmap才行。
<script src="https://unpkg.com/react@17/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
这样也行。
但是我们采用的是ems.sh地址。
2.9 预览器渲染页面
预览区页面加载,拿到编译好的main.tsx文件以后,我们将他插入到html的script的标签里面。一个完整的html文件就出来,然后用 URL.createObjectURL(new Blob([res], { type: 'text/html' }))拿到html的内存地址,然后交给iframe标签,要浏览器进行渲染。
2.10 主题切换
利用 css3的var()函数
变量要以两个连字符--(横杆)(减号)为开头
就是在父组件里面定义变量,然后在子组件里面使用var()函数加载
2.11 链接分享
我们把files数据存储到window.location.hash里面,在保存之前要对files进行压缩,我们选择fflate做压缩器,它的优点就是小巧好用。
压缩好的files数据全部存储到location的hash上,这样即便你改了编辑器里面的代码,也会同步到url里面。
复制数据使用copy-to-clipboard做剪切板。
拿到url以后,第一件事就是解析url里面的files,首先对他进行解压,然后再把他用JSON.parse()编译成对象。存入context.provide的files初始数据里面去。
2.12 下载文件
下载文件我们选用的工具是:jszip 和'file-saver,如果不借用后端,直接下载页面文件,我们可以使用file-saver实现,用这个工具我们还可以实现页面表格导出excel。下载的时候我们需要对files里存储的所有的文件进行打包,所以选择jszip
在我们实现功能的时候导出都用到了Blob对象,我们对这个对象重新进行学习