实现一个 Code Pen:(四)浏览器编译代码

sxkk20082年前知识分享142

前言

前面的文章中,我们配置好了编辑器,实现了 css、html、js 的编辑,现在我们需要做代码实时运行的功能了,并且可以直接写 less、scss、可以写 JavaScript、typescript、react。这个就涉及到了浏览器编译代码的逻辑,前期我们编译语言少一点、先把整体流程跑通,后面可以对语言和功能再慢慢丰富,这也是做项目的主要思路。

Iframe 实时运行

想要一个页面实时运行,并且 JS 变量不污染全局,Iframe 是一个不错的选择,得益于 iframe 有一个 srcDoc,我们可以直接更改里面的内容,页面就会实时变更和渲染, 业内的编辑器也是这么做的,一起看看下最简单的实现代码吧。

import React, { useState, useEffect } from 'react'
import Editor from './Editor'
import { useLocalStorage } from 'react-use'

function App() {
  const [html, setHtml] = useLocalStorage('html', '')
  const [css, setCss] = useLocalStorage('css', '')
  const [js, setJs] = useLocalStorage('js', '')
  const [srcDoc, setSrcDoc] = useState('')

  useEffect(() => {
    const timeout = setTimeout(() => {
      setSrcDoc(`
        
          
            
          
          ${html}
          
        
      `)
    }, 800)

    return () => clearTimeout(timeout)
  }, [html, css, js])

  return (
    <>
      <div className="pane">
        <Editor language="html" value={html} onChange={setHtml} />
        <Editor language="css" value={css} onChange={setCss} />
        <Editor language="javascript" value={js} onChange={setJs} />
      </div>
      <div className="pane">
        <iframe
          srcDoc={srcDoc}
          title="output"
          sandbox="allow-scripts"
          frameBorder="0"
          width="100%"
          height="100%"
        />
      </div>
    </>
  )
}

export default App

首先我们安装了react-use, 这个 hooks 是目前比较流行的 hook 库,使用useLocalStorage, 将数据存储到 LocalStorage 中,这样可以放在刷新页面的时候数据丢失。当然这是最简单的代码逻辑,为了防止整个 iframe dom 的销毁和重建,我使用 postMessage,具体代码可以直接看 Github

JS 编译

以上代码逻辑, 编辑器实现了原生 js 和 css 的支持,但是不支持 react 和 typescript,若要支持,需要在插入 srcDoc 之前将代码表编译成 es5,其实 babel 有个游览器版本@babel/standalone,并且有 presets 预设,支持 react 和 typescript, 只需要引入 srcipt 就可以,详情可以参考官方文档

<script src="https://unpkg.com/@babel/standalone/babel.min.js">script>
<script type="text/babel" data-presets="typescript">
  const x: number = 0
  console.log(x)
script>

以上代码就可以支持在浏览器执行

接下来我们需要支持 react 代码

import * as Babel from '@babel/standalone'

function compileJs(code) {
  const res = Babel.transform(code, {
    presets: ['react'],
  })
  return res.code
}

其实也很简单只需要设置 presets 设置为 react 就可以将编译 jsx 为 es5 了。

编译 typescript

编译 typescript 也是如此,需要注意的是 typescript 需要传入一个 filename 才可以

function compileTs(code) {
  const res = Babel.transform(code, {
    presets: ['typescript'],
    filename: 'index.ts',
  })
  return res.code
}

Less 编译

大部分同学都知道 less 使用的 2 种方式

  1. 在 Node.js 环境中使用 Less
npm install -g less
lessc styles.less styles.css
  1. 在浏览器环境中使用 Less
<link rel="stylesheet/less" type="text/css" href="styles.less" />
<script src="//cdnjs.cloudflare.com/ajax/libs/less.js/3.11.1/less.min.js">script>
  1. 我们的需求也是在浏览器中执行,但我们可以将编译的逻辑放在 web worker 中
import Less from 'less/lib/less'
const less = Less()
less.PluginLoader = function () {}

async function compileLess(code) {
  return await less.render(code).then((res) => res.css)
}

Scss 编译

scss 编译我选择的是 sass.js

同样首先需要安装

npm install -g sass.js

安装完成后,可以看下 node_modules 中的目录

sass.js 目录

我们发现目录中有个 sass.worker.js, 这个就 编译的 web worker js 代码, sass.js 已经将编译的逻辑独立到了这个 js 中,使用的时候需要设置 worker 的路径。 所以我们需要手动拷贝 node_modules 下的 sass.worker.jspublic/vendor 中,下面是实现代码

import Sass from 'sass.js/dist/sass'
Sass.setWorkerUrl('/vendor/sass.worker.js')

function compileScss(code) {
  const sass = new Sass()
  return new Promise((resolve, reject) => {
    sass.compile(code, (result) => {
      if (result.status === 0) return resolve(result.text)
      reject(new Error(result.formatted))
    })
  })
}

小结

预览地址:https://code.runjs.cool/pen/create

代码仓库:https://github.com/maqi1520/next-code-pen

本篇中浏览器编译的代码都很简单,但我却花了我几天时间,主要是这些代码都用的比较少,我又需要将编译的逻辑放入 web worker 中,然而 web worker 又没有 document 对象,所以不能直接使用 browser 版本的 js。当然目前还没实现 react typescript 的编译功能,先不卡在这了,把这项功能加入到 Todo List 中吧。

以上就是本文全部内容,希望这篇文章对大家有所帮助,也可以参考我往期的文章或者在评论区交流你的想法和心得,欢迎一起探索前端。

本文首发掘金平台,来源Ai知识分享博客

相关文章

简单介绍一下自己

我是一名 90 后,12 年毕业,工作 9 年,发过传单,做过运营,也把自己当成产品经理。目前是一名小厂前端工程师,日常负责公司 2B 产品开发迭代交付,还有一些公司内部组件、脚手架维护等。这一年很少加班,调休全靠年假了 😊 。

我的 2021

完成的

除了完成开发工作之外,还完成了:

掘金后台统计

  1. 在掘金发布 12 篇文章

  2. 【全栈】第三次重构我的个人博客

  3. 【全栈】模仿在线流程图  processon.com,目前已经下线,掘金实现文章

  4. 【全栈】实现一个简易版 react 低代码平台 https://low-code.runjs.cool/, 目前只实现了视图层,后面的逻辑有些复杂,没有继续下去。

  5. https://dev.to/ 发布 1 篇

9 年小厂老前端的年终总结

前言时光飞逝,岁月如梭,转眼来到 2021 年底,这一年少了些理性,多了点感性,少了些自由,多了一份责任,这一年视乎没做什么事情,但又过得非常充实,最欣慰的是回家有个人等待着我的拥抱,最快乐的是耳边多...

人工智能机器人发展史 - 探索人类未来的智慧助手与伙伴

人工智能机器人发展史 - 探索人类未来的智慧助手与伙伴

  随着科技的不断进步与创新,人工智能机器人已经成为现实中的一部分。从早期的机械人到如今的智能助手,人工智能机器人在过去几十年间取得了长足的进步。本文将以人工智能机器人发展史...

react + antd 实现

上面的代码只是实现了一个最简单的版本,样式也不够美观,因此我们可以使用 webpack + react + antd 来实现一个现代化的插件, 这里我使用一个之前创建的模版tampermonkey-starter

使用 antd 的 Popover 组件来显示,使用 react 重构下 js 代码,我们就可以实现如下效果。

image.png

基于 ChatGPT API 的划词翻译浏览器脚本实现

前言最近 GitHub 上有个基于 ChatGPT API 的浏览器脚本,openai-translator, 短时间内 star 冲到了 9.7k, 功能上除了支持翻译外,还支持润色和总结功能,除了...

智能AI人脸识别——打开未来智慧面孔的大门

智能AI人脸识别——打开未来智慧面孔的大门

  人工智能技术在如今的社会中得到了广泛的应用,其中之一便是人脸识别技术。借助这项技术,我们不仅能够确保个人信息和资产的安全,还能够提供更加个性化和便利的服务。针对此需求,许...

AI客服机器人哪个好?市场需求逐渐提升

AI客服机器人哪个好?市场需求逐渐提升

  近年来,AI客服机器人在以公开渠道为主的服务行业中逐渐得到了大众的认可和广泛的应用。然而,不同的AI客服机器人平台各有特点,产品质量和服务水平也不尽相同。本文将从市场需求...

AI绘画:探索数字时代的艺术创新

AI绘画:探索数字时代的艺术创新

  AI绘画是一种融合了人工智能和传统绘画艺术的新兴领域。利用AI技术进行绘画,不仅可以自动生成美观的画面效果,而且能够为人们带来更加便捷、高效的绘画体验。在数字时代中,AI...

发表评论    

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。