如何实现一个简版的码上掘金?

3,096 阅读4分钟

我正在参加「码上掘金挑战赛」详情请看:码上掘金挑战赛来了!

技术点概览

一句话概括,文章包含编辑器渲染器两个码上掘金代码块,采用 BroadcastChannel 实现代码块通信,将编辑器中内容传递到渲染器中,解析代码,注册为组件使用。

前言

相信在座各位都体验过码上掘金了,什么?没玩过,那快点这里 ➡️ 码上掘金开始表演你的 hello world吧。

最近看(白嫖)站内大佬们文章时,看到码上掘金的时候,墙上的📅日历就像是哗哗作响,脑子里闪过在CodePenJSFiddleRunJS这些名字,多年前的今天,在整理前端的入门文章时,想嵌入可预览的代码块,可选择的线上代码分享平台还是 CodePen,但是因为这服务吧,远在他国,网络来去不自如。

😮‍💨当时也是无奈,只能试着自己搞一搞。

现在嘛,技能熟练度涨进了那么一丢丢,可以试着带大家伙一起搞一搞😘。

3,2,1 上链接

前往文章下方体验效果 去体验

屏幕录制2022-09-13-16.46.52.gif

言归正传

要做什么?

这是个好问题,我们需要一个编辑区域和一个预览区域。

编辑器(编辑区域)

  • 文章中可以展示编写的 Hello World 组件效果
  • 文章浏览者可以自由的把 Hello World 改成 已点赞👍

渲染器(预览区域)

  • 代码块 Hello World组件,可以在Import Hello World代码块中使用
  • 代码块 Hello World组件的改动,同步在Import Hello World代码块中

在其他场景下,这些一般在一个项目中,通信的方式有很多,但是在文章中的码上掘金代码块场景下,两个代码块要如何通信呢?

怎么做?

这不是问题

如何通信

还记得上面说的BroadcastChannel么,不熟悉的同学请在公屏上扣1,呸呸呸,请自行复制,右上方点击搜索🔍。

/**
 * 初始化跨页面通信
 * @return {BroadcastChannel}
 */
function initMessage () {
  const broadcastChannel = new BroadcastChannel('shareComponent')
  return broadcastChannel
}

也许,有些事情,就像爱情一样,只需要简简单单就可以了。

通信

那么同学们,通信的问题就解答完了,接下来让我们来 new 一个 编辑器 组件,再 new 一个 渲染器组件。

编辑器里,我们初始化编辑器,和通信钩子,对外提供切换主题重置代码的事件,当然还有很重要的代码更新通知。

# 编辑器
const editor = initEditor()
const message = initMessage()

message.onmessage = (e) => {
  const { type } = e.data
  switch (type) {
    case 'toggle-theme': {
      randomTheme()
      break
    }
    case 'reset': {
      Object.assign(data, initData())
      editor.updateCode(data.content)
      break
    }
  }
}

editor.onUpdate(() => {
  message.postMessage({
    type: 'update-code',
    data: data.content
  })
})

渲染器里,我们同样初始化通信钩子,监听代码更新的通知,用来接收编辑器的代码。

# 渲染器
const message = initMessage()
message.onmessage = (e) => {
  const { type, data } = e.data
  switch (type) {
    case 'update-code': {
      renderComponent('myButton', data)
      break
    }
  }
}

到这里,两个代码块已经可以愉快的交流了,代码块联动后的码上掘金会有怎样的魔力呢,请尽情释放你的光吧~

附带赠送的组件渲染

同学们,到这里我们已经实现了,在编辑器中编辑代码,渲染器中同步获取代码,那么接下来,我们把获取的组件代码,更新到对应的组件。

// 注册组件
app.component(name, formatComponent(content))

我们拿到的是SFC格式的Vue组件,还需要一丢丢的转换工作。

const ComponentRegexs = {
  template: {
    match: /<template.*?>([\s\S]+?)</template>/gi,
    replace: /<template>|</template>/g
  },
  script: {
    match: /<script.*?>([\s\S]+?)</script>/gi,
    replace: /<script>|</script>|export default/g
  },
  style: {
    match: /<style.*?>([\s\S]+?)</style>/gi,
    replace: /<style>|</style>/g
  }
}
/**
* 格式化组件内容
* @param content
*/
function formatComponent (content) {
  const formatRegexs = Object.keys(ComponentRegexs)
  const result = {}
  formatRegexs.forEach(k => {
    const waitRegex = ComponentRegexs[k]
    const canMatch = content.match(waitRegex.match)
    if (canMatch) {
      result[k] = canMatch.at(0).replace(waitRegex.replace, '')
    }
  })
  let script
  script = eval(`script = ${result.script}`)
  return Object.assign({
    template: result.template
  }, typeof script === 'object' ? script : {})
}

在这里,大家如果体验过示例,就会发现样式并没有生效,因为就没有进行处理呀😄。

简单说下,之前实践的思路是,动态将style插入DOM,配合组件名称锁定CSS作用域,但是考虑到时过境迁,也许有其他更好的方案,就暂时搁置。

好了,今天的小葵花课堂就到这里结束了,有什么想法,我们在评论区进行讨论,感谢🎉。

效果体验

编辑器

渲染器

幕后花絮

本节目由码上掘金行业独家赞助(蹭蹭),欲知更多代码详情,请点击文章中码上掘金代码块了解更多。

CodeFlask传统色彩Vue及我本人提供技术支持。

🎉另外感谢大家伙收看本节目,祝各位中秋快乐,万事顺心。

对了,班里最后走的同学记得把灯关了,围观的大家伙也要记得点个赞哦。

彩蛋

最后,找一找,如何切换编辑器主题颜色呢?