├── .gitignore ├── Dockerfile ├── README.md ├── cdn.js ├── package-lock.json ├── package.json ├── public ├── favicon.ico ├── index.html ├── logo192.png ├── logo512.png ├── manifest.json └── robots.txt ├── server.js ├── src ├── App.css ├── App.js ├── App.test.js ├── baseCode.js ├── icon │ ├── browser.png │ ├── css.png │ ├── dot.png │ ├── github.png │ ├── js.png │ └── loading.png ├── index.css ├── index.js └── serviceWorker.js ├── views ├── error.ejs └── index.ejs └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # 设置基础镜像,如果本地没有该镜像,会从Docker.io服务器pull镜像 2 | FROM node:8.16.2-alpine 3 | ARG OSS_KEY_SECRET 4 | # 设置时区 5 | RUN apk --update add tzdata \ 6 | && cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ 7 | && echo "Asia/Shanghai" > /etc/timezone \ 8 | && apk del tzdata 9 | # 创建app目录 10 | RUN mkdir -p /usr/src/node-app 11 | # 设置工作目录 12 | WORKDIR /usr/src/node-app 13 | # 拷贝package.json文件到工作目录 14 | # !!重要:package.json需要单独添加。 15 | # Docker在构建镜像的时候,是一层一层构建的,仅当这一层有变化时,重新构建对应的层。 16 | # 如果package.json和源代码一起添加到镜像,则每次修改源码都需要重新安装npm模块,这样木有必要。 17 | # 所以,正确的顺序是: 添加package.json;安装npm模块;添加源代码。 18 | COPY package.json /usr/src/node-app/package.json 19 | # 安装npm依赖(使用淘宝的镜像源) 20 | # 如果使用的境外服务器,无需使用淘宝的镜像源,即改为`RUN npm i`。 21 | RUN npm i --registry=https://registry.npm.taobao.org 22 | 23 | COPY . /usr/src/node-app 24 | 25 | RUN npm run build 26 | 27 | RUN node cdn.js $OSS_KEY_SECRET 28 | 29 | EXPOSE 3001 30 | 31 | ENTRYPOINT ["node", "server.js"] 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 前言 2 | 3 | 近几年前端发展迅速,很多概念性的想法渐渐地变成了现实,比如前端微服务、Serverless 在前端的应用、在线IDE编码等,越来越多“工程”级别的项目选择使用 Web 浏览器作为宿主平台,前端发展可谓如日中天,本篇文章选择在线IDE作为切入点,介绍其实现原理,并且动手写一个简易版本的 React 在线IDE。 4 | 5 | ## CodeSandBox 6 | 7 | [CodeSandBox](https://codesandbox.io/) 是目前为止最为强大的在线 IDE 工具之一,他实现了 90% 的本地前端 IDE 工具的功能,以下是比较令人惊叹的几个特性: 8 | 9 | 1. 完全浏览器在线打包,实现了可在浏览器运行的 webpack 工具。 10 | 2. 可使用 npm 包 11 | 3. 支持文件系统 12 | 4. 可离线使用 13 | 5. 可在线发布应用 14 | 15 | 是的,你没有看错,这一切都是发生在浏览器上,更为惊叹的是,CodeSanBox 是个人项目,并不是由专业的公司团队开发完成。其强大功能证明了,浏览器能够做的事情是无穷无尽的,下面我将简单讲解其结构以及实现方案。 16 | 17 |  18 | 19 | 其主要结构分为以下几个部分: 20 | 21 | - Editor: 编辑器,这里集成了 VsCode,包含了大部分桌面版 VsCode 的主要功能(自动填充、光标提示、快捷键等),当文件变动时,会通知 SandBox 进行编译。 22 | - SandBox:这部分是 CodeSandBox 最核心的部分,它负责代码的转译,也就是最核心的 Webpack 在浏览器上的打包实现方案,用户编写的代码与用户所使用到的 npm 包源码,注入到转译的 Complier 中,转译完成的代码会注入 Iframe 中预览。 23 | - Packager:包管理器,相当于浏览器版本的 npm、yarn 包管理器。 24 | - Iframe:最后面是用来预览项目的内嵌 Iframe。 25 | 26 | 看似简单的实现方案,但是由于 CodeSandBox 的强大功能,因此其实现过程是非常复杂且具有挑战性的,因为浏览器并没有 Node 环境,并且 webpack 、npm、babel 对浏览器端使用并不是完全覆盖,甚至只是提供最基本的功能,还有需要克服的文件系统,这一切都需要大量的精力与时间,非常佩服与赞叹其作者,假如你想知道其具体的实现方案,以下资料推荐给你: 27 | 28 | - 国内首篇讲解:[CodeSandbox 浏览器端的webpack是如何工作的? 上篇](https://juejin.im/post/5d1e0dea51882514bf5bedfa#heading-1) 29 | - 作者的博客: [Ives van Hoorne](https://hackernoon.com/@compuives) 30 | - 项目开源地址:[codesandbox](https://github.com/codesandbox) 31 | 32 | ## 简易IDE需求 33 | 34 | 简单分析完 CodeSandBox 的实现方案,我们也能试着动手做一个简单的在线 IDE,我们需要实现以下基础功能: 35 | 36 | 1. 可编写 ES6、JSX 的 React 代码并预览 37 | 2. 可编写 CSS 38 | 3. 可保存源码数据 39 | 4. 可发布应用 40 | 41 | 这里,我放出我完成的项目,读者有兴趣可以进入下面地址试玩一下:[Online Editor](http://online.vince.xin/) 42 | 43 | 项目源码: [react-online-editor](https://github.com/Vincedream/react-online-editor) 44 | 45 | ## 实现方案 46 | 47 | 完成需求分析后,我列出了在我完成该 IDE 过程中给你遇到几个棘手的问题: 48 | 49 | 1. 在浏览器端如何转译 ES6、JSX 代码? 50 | 2. 如何实现预览功能 51 | 3. 如何完成编辑器的样式 52 | 4. 需要保证用户编写的代码数据持久化 53 | 5. 即时发布应用 54 | 55 | ### 总览分析 56 | 57 | 在解决上述问题,我们先对 Online Editor 的结构做一个讲解: 58 | 59 |  60 | 61 | 这是项目主要的结构,首先,用户编写代码后,Cmd/Ctrl + S 保存代码,触发 @babel/standalone 转译,这里将 ES6、JSX 的代码转译成 ES5 的可执行代码以及可可执行的 CSS 代码,注入到浏览器内嵌的 Iframe 中,使得 Iframe 刷新重新运行新的代码,同时,这一步我们会将用户编写的 Js、Css 代码以字符串的方式以一个唯一的 uuid 作为标识存入到 OSS 中,只要用户持有当前的 url,遍能访问到之前写的代码,这里我们便解决了上述的第1、2、4个问题。 62 | 63 | 用户编写完后,需要将其当前页面发布到线上环境,供其他人访问,这里我们看下面的分析结构图: 64 | 65 |  66 | 67 | 当我们点击发布按钮时,将转译后的 js 与 Css 代码存入到 OSS 中,以 ` 151 | 152 | 153 | 154 |
155 |