react中使用@bytemd-react实现掘金同款换主题

509 阅读3分钟

前言

项目技术栈 vite+react,

因为自己最近在写个博客相关的项目写着玩算练手react了 其中用到与掘金同款的@bytemd插件来写md文档

项目先安装juejin-markdown-themes然后去把里面的样式提取出来放到项目根目录的public(你也可以直接放到服务器上这里我是因为自己懒得弄直接放到项目中了)在删掉就juejin-markdown-themes就可以了

代码高亮的样式文件在@bytemd/plugin-highlight的插件中自己去提取出来就可以了 我是因为直接引用引不进去我就自己提出来了

废话不多说直接上代码

image.png

手写插件

根据官网写插件的方法手写一个插件函数 bytemd官网

image.png

插件文件mathPlugin

import { icons, mdThemes, highlights,changeTheme,changeCodeTheme } from "@/utils/mdTheme"
export default function mathPlugin() {
  return {
    actions: [
      {
        title: '主题',
        icon: icons.theme, // 16x16 SVG icon
        handler: {
          type: "dropdown",
          actions: mdThemes.map((theme) => ({
            title: theme,
            icon: "",
            cheatsheet: undefined,
            handler: {
              type: "action",
              click(e) {                
                changeTheme(theme);
              },
            },
          })),
        },
      },
      {
        title: '代码高亮主题',
        icon: icons.code_theme, // 16x16 SVG icon
        handler: {
          type: "dropdown",
          actions: highlights.map((theme) => ({
            title: theme,
            icon: "",
            cheatsheet: undefined,
            handler: {
              type: "action",
              click(e) {            
                changeCodeTheme(theme)
              },
            },
          })),
        },
      },
    ],
  }
}

mdTheme文件(用于处理图标以及列表和主题样式引入的)

export const icons = {
  theme:
    '<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M6 2H2.66667C2.29848 2 2 2.29848 2 2.66667V6C2 6.36819 2.29848 6.66667 2.66667 6.66667H6C6.36819 6.66667 6.66667 6.36819 6.66667 6V2.66667C6.66667 2.29848 6.36819 2 6 2Z" stroke="#1D2129" stroke-width="1.33" stroke-linejoin="round"></path><path d="M6 9.3335H2.66667C2.29848 9.3335 2 9.63197 2 10.0002V13.3335C2 13.7017 2.29848 14.0002 2.66667 14.0002H6C6.36819 14.0002 6.66667 13.7017 6.66667 13.3335V10.0002C6.66667 9.63197 6.36819 9.3335 6 9.3335Z" stroke="#1D2129" stroke-width="1.33" stroke-linejoin="round"></path><path d="M13.3334 2H10C9.63185 2 9.33337 2.29848 9.33337 2.66667V6C9.33337 6.36819 9.63185 6.66667 10 6.66667H13.3334C13.7016 6.66667 14 6.36819 14 6V2.66667C14 2.29848 13.7016 2 13.3334 2Z" stroke="#1D2129" stroke-width="1.33" stroke-linejoin="round"></path><path d="M13.3334 9.3335H10C9.63185 9.3335 9.33337 9.63197 9.33337 10.0002V13.3335C9.33337 13.7017 9.63185 14.0002 10 14.0002H13.3334C13.7016 14.0002 14 13.7017 14 13.3335V10.0002C14 9.63197 13.7016 9.3335 13.3334 9.3335Z" stroke="#1D2129" stroke-width="1.33" stroke-linejoin="round"></path></svg>',
  code_theme:
    '<svg width="24" height="24" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg"><rect width="48" height="48" fill="white" fill-opacity="0.01"></rect><path d="M6 44L6 25H12V17H36V25H42V44H6Z" fill="none" stroke="#333" stroke-width="4" stroke-linejoin="round"></path><path d="M17 17V8L31 4V17" stroke="#333" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"></path></svg>',
};
export const mdThemes = [
  "default",
  "smartblue",
  "cyanosis",
  "channing-cyan",
  "fancy",
  "hydrogen",
  "condensed-night-purple",
  "greenwillow",
  "v-green",
  "healer-readable",
  "mk-cute",
  "jzman",
  "geek-black",
  "awesome-green",
  "orange",
  "scrolls",
  "simplicity-green",
  "arknights",
  "vue-pro",
  "vuepress",
  "Chinese-red",
  "nico",
  "devui-blue",
  "z-blue",
  "koi",
  "juejin",
  "github"
];
export const highlights = [
  "default",
  "a11y-dark",
  "a11y-light",
  "agate",
  "an-old-hope",
  "androidstudio",
  "arduino-light",
  "arta",
  "ascetic",
  "atom-one-dark-reasonable",
  "atom-one-dark",
  "atom-one-light",
  "brown-paper",
  "brown-papersq",
  "codepen-embed",
  "color-brewer",
  "dark",
  "devibeans",
  "docco",
  "far",
  "felipec",
  "foundation",
  "github-dark-dimmed",
  "github-dark",
  "github",
  "gml",
  "googlecode",
  "gradient-dark",
  "gradient-light",
  "grayscale",
  "hybrid",
  "idea",
  "intellij-light",
  "ir-black",
  "isbl-editor-dark",
  "isbl-editor-light",
  "kimbie-dark",
  "kimbie-light",
  "lightfair",
  "lioshi",
  "magula",
  "mono-blue",
  "monokai-sublime",
  "monokai",
  "night-owl",
  "nnfx-dark",
  "nnfx-light",
  "nord",
  "obsidian",
  "panda-syntax-dark",
  "panda-syntax-light",
  "paraiso-dark",
  "paraiso-light",
  "pojoaque",
  "purebasic",
  "qtcreator-dark",
  "qtcreator-light",
  "rainbow",
  "routeros",
  "school-book",
  "shades-of-purple",
  "srcery",
  "stackoverflow-dark",
  "stackoverflow-light",
  "sunburst",
  "tokyo-night-dark",
  "tokyo-night-light",
  "tomorrow-night-blue",
  "tomorrow-night-bright",
  "vs",
  "vs2015",
  "xcode",
  "xt256"
];
/*
如果你是直接放到服务上的你直接把这里的地址改成你服务器上的地址
*/
const Theme_url = "/editorTheme/"
const CodeTheme_url = "/highlightTheme/"
export let theme = ""
export let codeTheme =""
export const changeTheme = (themename: string) => {
  const mdTheme = document.head.querySelector("#MD_THEME")    
  if (themename === "default") {     
    theme=""
    mdTheme !=null&&document.head.removeChild(mdTheme);
    return 
  }
  const dom = document.createElement("link");  
  dom.id = "MD_THEME";
  dom.rel = "stylesheet";
  dom.href = `${Theme_url}${themename}.min.css`;
  theme=themename
  document.head.contains(dom) ? "" : document.head.appendChild(dom);
};
export const changeCodeTheme = (themename: string) => {
  const mdTheme = document.head.querySelector("#Code_THEME")   
  if (themename === "default") { 
    codeTheme = ""    
    mdTheme !=null&&document.head.removeChild(mdTheme);
    return 
  }
  const dom= document.createElement("link"); 
  dom.id = "Code_THEME";
  dom.rel = "stylesheet";
  dom.href = `${CodeTheme_url}${themename}.css`;
  codeTheme=themename
  document.head.contains(dom) ? "" : document.head.appendChild(dom);
};

使用

代码太多了就不直接全部引进来了我缩减掉 懂意思就行

import { FC, useCallback, useEffect, useState } from "react";
import gfm from "@bytemd/plugin-gfm";
import mediumZoom from '@bytemd/plugin-medium-zoom'
import frontmatter from '@bytemd/plugin-frontmatter';
import gemoji from "@bytemd/plugin-gemoji";
import zh_Hans from "bytemd/locales/zh_Hans.json";
import highlight from "@bytemd/plugin-highlight";
import { Editor } from "@bytemd/react";
import mathPlugin from "./mathPlugin" // 引入自己手写的插件
import { theme,codeTheme } from "@/utils/mdTheme"
import WButton from "@/components/WButton";
import Popover from "@/components/Popover";
import CreatePacking from "./createPacking";
import { UserStore } from "@/store/user_store";
import "bytemd/dist/index.css";
import "@/styles/default.css"
import "@/styles/editor.less" 
import styles from "./index.module.less"
import img from "@/assets/image/defaultAvatar.jpg"
import { createPosts } from "@/api/post";
// import icon from "@/assets/svg/del.svg?raw"

const plugins = [
  gfm({
    locale: {
      strike: "删除线",
      table: "表格",
      task: "任务列表",
      
    },
  }
  ),
  highlight(),
  mediumZoom(),
  gemoji(),
  frontmatter(),
  mathPlugin() //放到插件这里注册进去
];
const WritePosts: FC = () => {
  const [posts, setPosts] = useState<createPost>(new newPosts())
  const filterContent = (v: string) => { 
    setPosts({
      ...posts, content: v, abstract: v.replace(markdownRegex, '').substring(0, 100)})
  }
  const onChange = useCallback((state:any) => { 
    setPosts({...posts,...state})
  }) 
  return (
    <div className={`${styles.bytemd_wrap}`}>
      <Head onChange={onChange} posts={ posts} />
        <Editor
        locale={zh_Hans}
        value={posts.content}
        plugins={plugins}
        mode="split"
        onChange={(v: string) => filterContent(v)}
        uploadImages={(e) => {console.log(e);
         }}
        />
      </div>

  );
};
export default WritePosts;

image.png

在Viewer组件中使用

因为自己还没写就没办法放代码了

image.png 掘金的话是用前面的解析: 我的思路是直接后端用两个字段接收主题和代码高亮主题的名称 在调用上面的两个函数去动态引入主题就好了 反正意思是一样的效果也是一样的你也可以去弄解析前面文本的字段只不过我比较懒用最简单的两个字段去解决反正是自己练手的项目