基于vite2+Vue3如何编写一个在线帮助文档工具
本篇内容主要讲解"基于vite2+Vue3如何编写一个在线帮助文档工具",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"基于vite2+Vue3如何编写一个在线帮助文档工具"吧!
技术栈
vite: ^2.7.0
vue: ^3.2.23
axios: ^0.25.0 获取json格式的配置和文档
element-plus: ^2.0.2 UI库
nf-ui-elp": ^0.1.0 二次封装的UI库
@element-plus/icons-vue: ^0.2.4 图标
@kangc/v-md-editor:"^2.3.13 md 编辑器
vite-plugin-prismjs: ^0.0.8 代码高亮
nf-state": ^0.2.4 状态管理
nf-web-storage": ^0.2.3 访问 indexedDB
建立库项目(@naturefw/press-edit)实现文档的编写、浏览功能
首先使用 vite2 建立一个 Vue3 的项目:
安装 elementPlus 实现页面效果;
安装 v-md-editor 实现 Markdown 的编辑和显示;
安装 @naturefw/storage 操作 indexedDB ,实现帮助文档的存储;
安装 @naturefw/nf-state 实现状态管理;
安装axios 用于加载 json文件,实现导入功能。
用node写一个后端API,实现写入json文件的功能。
注意:库项目需要安装以上插件,帮助文档项目只需要安装 @naturefw/press-edit 即可。
两个状态:编辑和浏览
一开始做了两个项目,分别实现编辑文档和显示文档的功能,但是后来发现,内部代码大部分是相同的,维护的时候有点麻烦,所以改为在编辑文档的项目里加入"浏览"的状态,然后设置切换的功能,这样便于内部代码的维护,以后成熟了可能会分为两个单独的项目。
编辑状态的功能
菜单维护
文档维护
文档展示
导入导出
在线编写/执行代码
我喜欢在线编辑的方式,这样更省心,于是我用 el-menu 实现导航和左侧的菜单,然后加上了维护功能。 使用 v-md-editor 实现 Markdown 的编辑和显示。 然后用node写了一个后端API,实现保存 json文件的功能,这样就完美了。
浏览状态的功能
导航
菜单
文档展示
执行代码
就是在编辑状态的功能的基础上,去掉一些功能。或者其实可以反过来思考。
实现导航
首先参考 VuePress 设置一个json文件,用于加载和保存网站信息、导航信息。
/public/docs/.nfpress/project.json
{ "projectId": "1000", "title": "nf-press-edit !", "description": "这是一个在线编辑、展示文档的小工具", "navi": [ { "naviId": "1010", "text": "指南", "link": "menu" }, { "naviId": "1020", "text": "组件", "link": "menu" }, { "naviId": "1380", "text": "Gitee", "link": "https://gitee.com/nfpress/nf-press-edit" }, { "naviId": "1390", "text": "在线演示", "link": "https://nfpress.gitee.io/nf-press-edit/" }, { "naviId": "1395", "text": "我要提意见", "link": "https://gitee.com/nfpress/nf-press-edit/issues" } ]}
projectId:项目ID,可以用于区分不同的帮助文档项目。
navi: 存放导航项。
naviId: 关联到菜单。
text: 导航上显示的文字。
link: 连接方式或链接地址。menu:表示要打开对应的菜单;URL:在新页面里打开连接。
然后做一个组件,用 el-menu 绑定数据渲染出来即可实现导航效果。
/lib/navi/navi.vue
{{item.text}}
可以是多级的导航,暂时没有实现在线维护功能。
import { ref } from 'vue' import { ElMenu, ElMenuItem } from 'element-plus' import { state } from '@naturefw/nf-state' const props = defineProps({ 'background-color': { // 默认背景色 type: String, default: '#ece5d9' }, itemProps: Object }) // 获取状态和导航内容 const { current, naviList } = state // 激活第一个导航项 const activeIndex2 = ref(naviList[0].naviId) const handleSelect = (key, keyPath) => { const navi = naviList.find((item) => item.naviId === key) if (navi.link === 'menu') { // 打开菜单 current.naviId = key } else { // 打开连接 window.open(navi.link, '_blank') } }
@naturefw/nf-state
自己写的一个轻量级状态管理,可以当做大号 reactive 来使用,通过状态管理加载 project.json 然后绑定渲染。
naviList
导航列表,由状态管理加载。
current
当前激活的各种信息,比如"current.naviId"表示激活的导航项。
实现菜单
和导航类似,只是需要增加两个功能:n级分组和维护。
首先参考 VuePress 设置一个json文件,保存菜单信息。
/public/docs/.nfpress/menu.json
[ { "naviId": "1010", "menus": [ { "menuId": "110100", "text": "介绍", "description": "描述", "icon": "FolderOpened", "children": [] }, { "menuId": "111100", "text": "快速上手", "description": "描述", "icon": "FolderOpened", "children": [ { "menuId": 111120, "text": "编辑文档项目", "description": "", "icon": "UserFilled", "children": [] }, { "menuId": 111130, "text": "展示文档项目", "description": "", "icon": "UserFilled" } ] } ], "ver": 1.6 }, { "naviId": "1020", "menus": [ { "menuId": "21000", "text": "导航(docNavi)", "description": "描述", "icon": "Star", "children": [] } ], "ver": 1.5 }]
naviId: 关联导航项ID,可以是数字,也可以是其他字符。需要和导航项ID对应。
menus: 导航项对应的菜单项集合。
menuId: 菜单项ID,关联一个文档,可以是数字或者英文。
text: 菜单项名称。
description: 描述,考虑以后用于查询。
icon: 菜单使用的图标名称。
children: 子菜单项目,没有的话可以去掉。
ver: 版本号,便于更新文档。
然后用 el-menu 绑定数据渲染,因为要实现n级分组,所以做一个递归组件实现n级菜单的效果。
实现n级分组菜单
做一个递归组件实现n级分组的功能:
/lib/menu/menu-sub-edit.vue
{{item.text}} {{item.text}}
import { ElMenuItem, ElSubMenu } from 'element-plus' // 展示子菜单 - 递归 import mySubMenu2 from './menu-sub.vue' const props = defineProps({ subMenu: Array, // 要显示的菜单,可以n级 dialogAddInfo: Object, // 添加菜单 dialogModInfo: Object // 修改菜单 })
subMenu 要显示的子菜单项
dialogAddInfo 添加菜单的信息
dialogModInfo 修改菜单的信息
实现菜单的维护功能
这个就比较简单了,做个表单实现菜单的增删改即可,篇幅有限跳过。
实现 Markdown 的编辑
使用 v-md-editor 实现 Markdown 的编辑和展示,首先该插件非常好用,其次支持VuePress的主题。
建立 /lib/md/md-edit.vue 实现编辑 Markdown 的功能:
import { watch,ref } from 'vue' import { ElMessage, ElRadioGroup, ElRadioButton } from 'element-plus' import mdController from '../service/md.js' // 状态 import { state } from '@naturefw/nf-state' // 获取当前激活的信息 const current = state.current // 文档的加载和保存 const { loadDocById, saveDoc } = mdController() // 可见的高度 const editHeight = document.documentElement.clientHeight - 200 // 单击 保存 按钮,实现保存功能 const mySave = (text, html) => { saveDoc(current) } // 定时保存 let timeout = null let isSaved = true const timeSave = () => { if (isSaved) { // 保存过了,重新计时 isSaved = false } else { return // 有计时,退出 } timeout = setTimeout(() => { // 保存文档 saveDoc(current).then(() => { ElMessage({ message: '自动保存文档成功!', type: 'success', }) }) isSaved = true }, 10000) } // 定时保存文档 watch(() => current.docInfo.md, () => { timeSave() }) // 根据激活的菜单项,加载对应的文档 watch( () => current.menuId, async (id) => { const ver = current.ver loadDocById(id, ver).then((res) => { // 找到了文档 Object.assign(current.docInfo, res) }).catch((res) => { // 没有文档 Object.assign(current.docInfo, res) }) })
mdController 实现文档的增删改查的controller
timeSave 定时保存文档,避免忘记点保存按钮
是不是挺简单的。
实现在线编写代码并且运行的功能
因为是基于Vue3建立的项目,而且也是为了写vue3相关的帮助文档,那么就有一个很实用的要求:在线写代码并且可以运行。
个人感觉这个功能还是很实用的,我知道有第三方网站提供了这种功能,但是网速有点慢,另外有一种大炮打蚊子的感觉,我只需要实现简单的代码演示。
于是我基于 vue 的 defineAsyncComponent 写了一个简单版的在线编写代码且运行的功能:
/lib/runCode/run.vue
import { defineAsyncComponent, ref, reactive,... // 其他常用的vue内置指令 } from 'vue' // 使用 eval编译js代码 const mysetup = ` (function setup () { {[code]} }) ` // 通过属性传入需要运行的代码和模板 const props = defineProps({ code: { type: Object, default: () => { return { js: '', template: '', style: '' } } } }) const code = props.code // 使用 defineAsyncComponent 让代码运行起来 const AsyncComp = defineAsyncComponent( () => new Promise((resolve, reject) => { resolve({ template: code.template, // 设置模板 style: [code.style], // 大概是样式设置,但是好像没啥效果 setup: (props, ctx) => { const tmpJs = code.js // 获取js代码 let fun = null // 转换后的函数 try { if (tmpJs) fun = eval(mysetup.replace('{[code]}', tmpJs)) // 用 eval 把 字符串 变成 函数 } catch (error) { console.error('转换出现异常:', error) } const re = typeof fun === 'function' ? fun : () => {} return { ...re(props, ctx) // 运行函数,解构返回对象 } } }) }) )
defineAsyncComponent
实用 defineAsyncComponent 加载组件,需要设置三个部分:模板、setup和style。
template: 字符串形式,可以直接传入
setup: js代码,可以用eval的方式进行动态编译。
style: 可以设置样式。
这样即可让在线编写的代码运行起来,当然功能有限,只能用于一些简单的代码演示。
导出
以上这些功能都是基于 indexedDB 进行的,想要发布的话,需要先导出为json文件。
因为浏览器里不能直接写文件,所以需要使用折中的方式:
复制粘贴
下载
导出
复制粘贴
这个简单,用文本域显示json即可。
下载
使用 chrome 浏览器提供的下载功能下载文件。
const uri = 'data:text/json;charset=utf-8,\ufeff' + encodeURIComponent(show.navi) //通过创建a标签实现 var link = document.createElement("a") link.href = uri //对下载的文件命名 link.download = fileName document.body.appendChild(link) link.click() document.body.removeChild(link)
以上介绍的是内部原理,如果只是想简单使用的话,可以跳过,直接看下面的介绍。
用后端写文件
以上两种都不太方便,于是用node做了个简单的后端API,用于实现写入json文件的功能。
代码放在了 api文件夹里,可以使用 yarn api
运行。当然需要在 package.json 里做一下设置。
"scripts": { "dev": "vite", "build": "vite build --mode project", "lib": "vite build --mode lib", "serve": "vite preview", "api": "node api/server.js" },
实现一个帮助文档的项目
上面介绍的是库项目的基本原理,我们要做帮助文档的时候,并不需要那么复杂。
使用 vite2 建立一个vue3的项目,然后安装 @naturefw/press-edit,使用提供的组件即可方便的实现。
main.js
首先需要在 main.js 里面做一些设置。
import { createApp } from 'vue'import App from './App.vue'// 设置 axios 的 baseUrlconst baseUrl = _(document.location.host.includes('.gitee.io')) ? '/doc-ui-core/' : '/'// 轻量级状态// 设置 indexedDB 数据库,存放文档的各种信息。import { setupIndexedDB, setupStore } from '@naturefw/press-edit'// 初始化 indexedDB 数据库setupIndexedDB(baseUrl) // UI库import ElementPlus from 'element-plus'// import 'element-plus/lib/theme-chalk/index.css'// import 'dayjs/locale/zh-cn'import zhCn from 'element-plus/es/locale/lang/zh-cn'// 二次封装import { nfElementPlus } from '@naturefw/ui-elp'// 设置iconimport installIcon from './icon/index.js'// 设置 Markdown 的配置函数import setMarkDown from './main-md.js'// 主题import vuepressTheme from '@kangc/v-md-editor/lib/theme/vuepress.js'const { VueMarkdownEditor, // Markdown 的编辑器 VMdPreview // Markdown 的浏览器} = setMarkDown(vuepressTheme)const app = createApp(App)app.config.globalProperties.$ELEMENT = { locale: zhCn, size: 'small'}app.use(setupStore) // 状态管理 .use(nfElementPlus) // 二次封装的组件 .use(installIcon) // 注册全局图标 .use(ElementPlus, { locale: zhCn, size: 'small' }) // UI库 .use(VueMarkdownEditor) // markDown编辑器 .use(VMdPreview) // markDown 显示 .mount('#app')
baseUrl: 根据发布平台的情况进行设置,比如这里需要设置为:"/doc-ui-core/"
setupIndexedDB: 初始化 indexedDB 数据库
setupStore: 设置状态
element-plus:element-plus 可以不挂载,但是css需要 import 进来,这里采用CDN的方式引入。
nfElementPlus: 二次封装的组件,便于实现增删改查。
setMarkDown: 加载 v-md-editor ,以及需要的插件。
vuepressTheme: 设置主题。
设置 Markdown
因为 v-md-editor 相关设置比较多,所以设置了一个单独文件进行管理:
/src/main-md.js
// Markdown 编辑器import VueMarkdownEditor from '@kangc/v-md-editor'import '@kangc/v-md-editor/lib/style/base-editor.css'// 在这里引入,不被识别?// import vuepressTheme from '@kangc/v-md-editor/lib/theme/vuepress.js'import '@kangc/v-md-editor/lib/theme/style/vuepress.css'// 代码高亮import Prism from 'prismjs'// emojiimport createEmojiPlugin from '@kangc/v-md-editor/lib/plugins/emoji/index'import '@kangc/v-md-editor/lib/plugins/emoji/emoji.css'// 流程图// import createMermaidPlugin from '@kangc/v-md-editor/lib/plugins/mermaid/cdn'// import '@kangc/v-md-editor/lib/plugins/mermaid/mermaid.css'// todoListimport createTodoListPlugin from '@kangc/v-md-editor/lib/plugins/todo-list/index'import '@kangc/v-md-editor/lib/plugins/todo-list/todo-list.css'// 代码行号import createLineNumbertPlugin from '@kangc/v-md-editor/lib/plugins/line-number/index';// 高亮代码行import createHighlightLinesPlugin from '@kangc/v-md-editor/lib/plugins/highlight-lines/index'import '@kangc/v-md-editor/lib/plugins/highlight-lines/highlight-lines.css'// import createCopyCodePlugin from '@kangc/v-md-editor/lib/plugins/copy-code/index'import '@kangc/v-md-editor/lib/plugins/copy-code/copy-code.css'// markdown 显示器import VMdPreview from '@kangc/v-md-editor/lib/preview'// import '@kangc/v-md-editor/lib/style/preview.css'/** * 设置 Markdown 编辑器 和浏览器 * @param {*} vuepressTheme * @returns */export default function setMarkDown (vuepressTheme) { // 设置 vuePress 主题 VueMarkdownEditor.use(vuepressTheme, { Prism, extend(md) { // md为 markdown-it 实例,可以在此处进行修改配置,并使用 plugin 进行语法扩展 // md.set(option).use(plugin); }, } ) // 预览 VMdPreview.use(vuepressTheme, { Prism, extend(md) { // md为 markdown-it 实例,可以在此处进行修改配置,并使用 plugin 进行语法扩展 // md.set(option).use(plugin); }, } ) // emoji VueMarkdownEditor.use(createEmojiPlugin()) // 流程图 // VueMarkdownEditor.use(createMermaidPlugin()) // todoList VueMarkdownEditor.use(createTodoListPlugin()) // 代码行号 VueMarkdownEditor.use(createLineNumbertPlugin()) // 高亮代码行 VueMarkdownEditor.use(createHighlightLinesPlugin()) // VueMarkdownEditor.use(createCopyCodePlugin()) // 预览的插件 VMdPreview.use(createEmojiPlugin()) VMdPreview.use(createTodoListPlugin()) VMdPreview.use(createLineNumbertPlugin()) VMdPreview.use(createHighlightLinesPlugin()) VMdPreview.use(createCopyCodePlugin()) return { VueMarkdownEditor, VMdPreview }}
不多介绍了,可以根据需要选择插件。
布局
在App.vue文件里面进行整体布局
nf-press
import { reactive, defineAsyncComponent } from 'vue' import { ElHeader, ElContainer ,ElAside, ElMain } from 'element-plus' import { docMenu, docNavi, config } from '@naturefw/press-edit' // 菜单 导航 import docView from './views/doc.vue' // 显示文档 // 加载菜单子控件 const docControl = { true: docView, false: defineAsyncComponent(() => import('./views/main.vue')) // 修改文档 } const itemProps = reactive({ 'inline-prompt': true, 'active-text': '看', 'inactive-text': '写', 'active-color': '#378FEB', 'inactive-color': '#EA9712' })
$state:全局状态,$state.current.isView 设置是否是浏览状态。
doc-navi:导航组件
doc-menu:菜单组件
docControl:根据状态选择加载显示组件或者编辑组件的字典。
这种方式虽然有点麻烦,但是比较灵活,可以根据需要进行各种灵活设置,比如添加版权信息、备案信息、广告等内容。
到此,相信大家对"基于vite2+Vue3如何编写一个在线帮助文档工具"有了更深的了解,不妨来实际操作一番吧!这里是网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!