nextjs集成掘金 bytemd 编辑器

480 阅读3分钟

最近在做一个的个人博客的 nextjs 项目使用到了掘金 md 编辑器这里记录下集成的过程

ByteMD 仓库 猛击访问 虽然已经好久没有更新了但是不影响使用该有的都有了

支持多种框架

image.png

支持多种插件

image.png

所有代码已上传 github 猛击访问

初始化一个 nextjs 项目

执行 npx create-next-app@latest 初始化一个 nextjs 项目

image.png

打开项目执行 npm run dev 启动项目看一下,一般都是没什么问题的

开始集成

安装掘金编辑器依赖

执行 npm i bytemd @bytemd/react 安装编辑器

执行 npm i @bytemd/plugin-mermaid @bytemd/plugin-medium-zoom @bytemd/plugin-math-ssr @bytemd/plugin-math @bytemd/plugin-highlight-ssr @bytemd/plugin-gfm @bytemd/plugin-gemoji @bytemd/plugin-frontmatter @bytemd/plugin-footnotes @bytemd/plugin-breaks 安装依赖

执行 npm i juejin-markdown-themes 安装掘金主题

执行 npm i sass 安装 sass 依赖后边需要使用 scss 来写样式

创建组件目录

app/components/bytemd 创建对应的文件夹

创建 plugins.ts 文件

plugins.ts 文件把所有的插件统一引入导出, 这里这样做的原因是会创建一个编辑器和预览组件统一导出后就不需要写两遍了

import breaks from '@bytemd/plugin-breaks'
import frontmatter from '@bytemd/plugin-frontmatter'
import gemoji from '@bytemd/plugin-gemoji'
import gfm from '@bytemd/plugin-gfm'
import highlightSsr from '@bytemd/plugin-highlight-ssr'
import mediumZoom from '@bytemd/plugin-medium-zoom'
import mermaid from '@bytemd/plugin-mermaid'
import footnotes from '@bytemd/plugin-footnotes'
import math from '@bytemd/plugin-math-ssr'
import mermaid_zhHans from '@bytemd/plugin-mermaid/lib/locales/zh_Hans.json'
import math_zhHans from '@bytemd/plugin-math/lib/locales/zh_Hans.json'
import gfm_zhHans from '@bytemd/plugin-gfm/lib/locales/zh_Hans.json'

const plugins = [
  breaks(),
  frontmatter(),
  gemoji(),
  gfm({ locale: gfm_zhHans }),
  math({ locale: math_zhHans }),
  highlightSsr(),
  mermaid({ locale: mermaid_zhHans }),
  mediumZoom(),
  footnotes()
]

export default plugins

编辑器样式

在官方仓库没有找到预览的深色主题的样式,自己写了一下 猛击访问

创建 editor.scss

@import 'bytemd/dist/index.css';
@import 'juejin-markdown-themes/dist/juejin.css';
// @import 'highlight.js/styles/default.css';
@import 'highlight.js/styles/atom-one-dark.css'; // 代码高亮主题 掘金编辑器已经依赖这个 highlight.js 这里无需单独安装
// @import 'highlight.js/styles/a11y-dark.css';

.bytemd {
  // 设置默认高度,防止内容过少时,高度不够显示
  height: 100vh;
  border: 0;
  overflow: hidden;
}

创建 editor.tsx 编辑组件

'use client'

import './editor.scss' // 编辑器样式
import plugins from './plugins' // 引入统一导出的插件
import { Editor } from '@bytemd/react'
import zh_Hans from 'bytemd/locales/zh_Hans.json' // 中文语言包

interface UploadImage {
  url: string,
  alt: string,
  title: string
}

// 上传图片
async function uploadImages(files: File[]) {
  const resultData:UploadImage[] = []

  for (const item of files) {
    const formData = new FormData()
    formData.append('file', item)
    const res = await fetch('/api/upload', {
      method: 'POST',
      body: formData
    })
    const data = await res.json()

    if (data?.code === 0) {
      // 上传成功后安装指定格式返回
      resultData.push({
        url: data?.data,
        alt: item.name,
        title: item.name
      })
    }
  }
  return resultData
}

interface BytemdEditorProps {
  content: string
  setContent: (content: string) => void
}

export function BytemdEditor({ content, setContent }: BytemdEditorProps) {
  return (
    <Editor
      value={content}
      locale={zh_Hans}
      plugins={plugins}
      onChange={(v) => {
        setContent(v)
      }}
      uploadImages={uploadImages}
    />
  )
}

编辑器组件的使用

'use client'

import { useState } from 'react';
import { BytemdEditor } from './components/bytemd/editor'

export default function Home() {

  const [content, setContent] = useState('')
  
  return (
    <BytemdEditor content={content} setContent={setContent}></BytemdEditor>
  );
}

创建 viewer.tsx 预览组件

'use client'

import { Viewer } from '@bytemd/react'
import plugins from './plugins'
import './editor.scss'
import './dark-theme.scss'

export function BytemdViewer({ content }: { content: string }) {
  return <Viewer value={content} plugins={plugins}></Viewer>
}

预览组件的使用

'use client'

import { useEffect, useState } from 'react';
import { BytemdViewer } from '../components/bytemd/viewer'

export default function Home() {
  const [content, setContent] = useState('')
  
  useEffect(() => {
    fetch('/api/md').then((res) => res.json()).then(res => {
      console.log(res)
      setContent(res.content)
    })
  }, [])

  return (
    <div>
      <div className='max-w-5xl mx-auto'>
        <BytemdViewer content={content} ></BytemdViewer>
      </div>
    </div>
  );
}

成果展示

可以看到与官方示例一样!

image.png

预览组件的渲染也符合预期

image.png

看下自定义的深色主题也还行

image.png

总结

本文实践了在 nextjs 中如何集成掘金的 bytemd 编辑器以及插件的集成

封装了一个编辑器组件和预览组件

因为官方没有提供深色主题所以还自行实现了深色主题的样式

预览组件的样式切换是通过引入不同的 css 来实现 juejin-markdown-themes 这个包提供一些已经定义好的主题

往期文章