We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
1. 全局安装 vuepress
npm install -g vuepress
2. 运行编写好的 docs 文件,编译后的浏览器显示文档网页
vuepress dev docs
3. 将编写好的 docs md文档文件 build 打包成静态 html 网页
docs
html
vuepress build docs
接下来教你如何自实现 uabpress ,并实现以上功能!
1. 首先我们来实现自己编写一个库,发布到npm后 可通过npm i 自己库名安装
npm i 自己库名
//创建一个项目名文件夹,在文件夹内初始化这个项目 npm init -y // 项目初始化 npm adduser //添加你的 npm 账号 目的:指定发布库的账号 npm publish //将当前库发布到 npm 内 (开发完成这个库后通过这个命名发布)
发布后过一会儿即可去 npm 内查看, 这是我发布后的库: uabpress 如果 npm 你的账号的 packages 内以生成该库即可通过npm install -g 你的库名 安装了
npm
packages
npm install -g 你的库名
2. 全局安装库后,直接通过库名在cmd中运行你的库
这里我们需要用到一个命令行插件 commander, 下面是commander的使用方法 commander 的功能很简单配置如下:
const { program } = require('commander') //生成一个获取命令行输入内容的全局对象 program.version('1.0.0') //设置你的库当前版本 //下面就是通过 获取命令行输入内容 做一系列操作 运行对应执行文件等 ...
这是 uabpress 内获取命令行输入内容进行的操作: github.com/uabjs/uabpress/blob/main/bin/index.js 那为什么 uabpress dev docs 会首先运行 bin/index.js 文件呢? 见 package.json
uabpress
uabpress dev docs
bin/index.js
{ "name": "uabpress", "version": "1.0.0", "description": "基于 Vue3.0 SSR 的一个快速高效的 Markdown 网站制作工具", "main": "index.js", "bin": { //npm全局安装库会将package.json内的bin内容自动设置成电脑全局变量 "uabpress": "./bin/index.js" //这一行的目的就是向你的电脑全局一个uabpress的环境变量 }, ....
这样下来我们全局安装uabpress:npm i -g uabpress后, 在命令行中就可以通过uabpress -V命令来查看版本号,或者uabpress dev docs运行对应文档文件
npm i -g uabpress
uabpress -V
可见当执行vuepress dev docs后就自动打开了一个浏览器窗口,运行出了通过docs文件夹编译成的文档网页,这里是项目中最核心的环节,详情如下:
koa
对应项目文件 45行
app.start(options.port, () => { console.log('编译的docs目录: ' + path.resolve(options.sourceDir)) })
对应项目文件 15行
app.use(async (ctx, next) => { if (ctx.url.startsWith('/assets')) { try { const buffer = fs.readFileSync(path.resolve(__dirname, './' + ctx.url)) ctx.type = path.extname(ctx.url).slice(1) ctx.body = buffer } catch(e) { ctx.body = '' } } else { await next() } })
对应项目文件 12行
const glob = require('glob') //sourceDir 是docs文件夹路径 function getFolder(sourceDir) { return glob.sync(path.join(sourceDir, '/**/*.md'), { absolute: false }) .filter(v => v.indexOf('node_modules') === -1 ) .map(v => path.relative(sourceDir, v)) } function createMiddleware (options) { return async (ctx, next) => { ctx.menu = getFolder(options.sourceDir) //获取文件名菜单 返回路径数组 await next() } }
通过ctx.menu路径判断是否 新增、更新、删除 该fileNode节点,从而生成一棵树在nodes数组上的文件结构树 对应项目文件 27行
ctx.menu
nodes
async patch(filePaths) { const treeFlags = this.treekey() const newFiles = {} filePaths.forEach(filePath => { newFiles[this.formatFilePath(filePath)] = null }) Object.keys(this.nodes) .filter(filePaths => filePaths.indexOf(treeFlags) === -1) .forEach(async (filePath) => { if (filePath in newFiles) { await this.updateFile(filePath) delete newFiles[filePath] } else { await this.removeFile(filePath) } }) //剩下的 newFiles 则是新增的文件 Object.keys(newFiles).forEach(async (filePath) => { await this.addFile(filePath) }) }
对应项目文件 3行
const Provider = require('./Provider') module.exports = function () { const provider = new Provider() Array.from([ require('./middleware/title'), // 解析标题 require('./middleware/prefix'), // 标题层级 require('./middleware/breadcrumb'), // 计算面包屑 require('./middleware/autoNumber'), // 自动生成序号 require('./middleware/marked'), // markdown转html require('./middleware/themes') // 添加样式 ]).forEach(middleware => { provider.useMiddleware(middleware) }) return provider } /** * root: TreeNode { path:'', children:[], parent: [TreeNode] }, * nodes 内有两种形式: 标题集, 文件集 * nodes: { * '标题集/?': { * path: 'level1B/levelB', children: [ [FileNode], ... ], parent: TreeNode { path: 'level1B', children: [Array], parent: [TreeNode] } * } * '标题集/文件集.md': FileNode { resolvePath: [Function], path: '文件集.md', isFileNode: true, parent: [TreeNode], body: '11111111111', lastModified: 1606828666908, title: '文件集.md', prefix: '', breadcrumb: [Object], catalogs: [], html: '<p>11111111111</p>\n'+ ...<h1~3>, themes: [Array], getTheme: [Function] }, ... }, middlewares: [], resolvePath: fn */
koa获取浏览器请求时的url,通过 url 我们可以找到nodes节点树上对应该文件的fileNode文件节点, 获取该文件节点里面编译好的html内容,在通过vue3, ssr 生成网页html,传入ctx.body内返回给前端
url
fileNode
vue3
ssr
对应项目文件 14行
const Vue = require('vue') //这是vue3 const compilerSsr = require('@vue/compiler-ssr') //将vue文件template模板编译成 render 方法 const compilerSfc = require('@vue/compiler-sfc') // 解析vue文件的内容 const serverRenderer = require('@vue/server-renderer') //将createApp的实例转换成html字符串 const createRender = sfcPath => { sfcPath = sfcPath ? sfcPath : path.resolve(__dirname, '../template/App.vue') const { descriptor } = compilerSfc.parse(fs.readFileSync(sfcPath, 'utf-8')) const render = compilerSsr.compile(descriptor.template.content).code return async (data) => { const app = Vue.createApp({ ssrRender: new Function('require', render)(require), data: () => data }) return serverRenderer.renderToString(app) //将编译好的html字符串传入ctx.body相应出去 } } const renderMarkdown = async ({ reqFile, template, provider, options }) => { const skin = options.theme || '默认皮肤' // 获取 menu 菜单数据及其页面数据 const data = { menu: provider.toArray(fileNode => ({ path: fileNode.path, name: fileNode.title, prefix: fileNode.prefix || '' })), skinPath: '', breadcrumb: null, catalogs: [], markdown: '' } //解析该路径页面的 data 数据 await provider.getItem(reqFile, fileNode => { if (!fileNode) { data.markdown = `<h3>${reqFile} 不存在<h3>` } else { // console.log("fileNode----", fileNode); data.breadcrumb = fileNode.breadcrumb.html data.catalogs = fileNode.catalogs data.skinPath = fileNode.getTheme(skin).path data.markdown = [ fileNode.html ].join('') } }) return await createRender(template)(data) }
watch
socket.io
对应项目文件 13行
const watch = require('watch') const io = require('socket.io') //options.sourceDir就是docs文件夹路径,目的:监听该路径内的文件修改时间如果改变则触发页面刷新 watch.watchTree(options.sourceDir, changePath => { progress.init() //刷新的进度条 初始化 progress.step() //刷新的进度条 加载动画 socket.emit('reload', changePath) //docs 改变重新加载 }) app.use(async (ctx, next) => { await next() //当所有中间件都执行完成后给最后生成的body添加socket监听 if (ctx.type === 'text/html') { ctx.body = ` <!DOCTYPE html> <html> <script src="https://cdn.bootcss.com/socket.io/2.2.0/socket.io.js"></script> <script> var socket = io() socket.on("reload", function(changePath) { window.location.reload() }) </script> ${ctx.body} </html> ` } })
最后一步就是通过 open 打开浏览器以及对应端口 对应项目文件 36行
const open = require("open") //这个库传入url默认打开浏览器 open(`http://localhost:${port}`)
到此全部工序就完成了!
命令执行在: 对应项目文件 43行
// 2. 解析到指令为 build 则执行下面操作 program .command('build') .description('编译页面文件(生成html)') .option('-t, --theme [theme]', 'Markdown样式,可选 default、techo') .option('-o, --output [output]', '输出目录') .action(async (options) => { console.log('') // 打包生成静态html页面 await build({ theme: options.theme || 'default', //打包后的html样式 root: path.resolve(options.args.length > 0 ? options.args[0] : '.'), //打包路径 output: path.resolve(options.output || 'dist') //默认输出到dist文件夹 }) process.exit() }) // 1. 将命令传递给 program 解析 program.parse(process.argv) // process.argv是命令行输入的命令
核心打包实现: 对应项目文件 66行
The text was updated successfully, but these errors were encountered:
No branches or pull requests
uabpress 运行效果图如下:
uabpress 是防 vuepress 的一个库,了解uabpress 之前,首先我们来看看 vuepress 是怎么工作的:
1. 全局安装 vuepress
2. 运行编写好的 docs 文件,编译后的浏览器显示文档网页
3. 将编写好的
docs
md文档文件 build 打包成静态html
网页接下来教你如何自实现 uabpress ,并实现以上功能!
1.实现npm全局安装 uabpress 库并且 命令行 可执行
1. 首先我们来实现自己编写一个库,发布到npm后 可通过
npm i 自己库名
安装发布后过一会儿即可去
npm
内查看, 这是我发布后的库: uabpress如果
npm
你的账号的packages
内以生成该库即可通过npm install -g 你的库名
安装了2. 全局安装库后,直接通过库名在cmd中运行你的库
这里我们需要用到一个命令行插件 commander, 下面是commander的使用方法
commander 的功能很简单配置如下:
这是
uabpress
内获取命令行输入内容进行的操作: github.com/uabjs/uabpress/blob/main/bin/index.js那为什么
uabpress dev docs
会首先运行bin/index.js
文件呢? 见 package.json这样下来我们全局安装uabpress:
npm i -g uabpress
后, 在命令行中就可以通过uabpress -V
命令来查看版本号,或者uabpress dev docs
运行对应文档文件2.实现
vuepress dev docs
功能可见当执行
vuepress dev docs
后就自动打开了一个浏览器窗口,运行出了通过docs文件夹编译成的文档网页,这里是项目中最核心的环节,详情如下:1. 开启一个
koa
服务默认开启端口为3000端口对应项目文件 45行
2. 静态文件加载 如:image、css、js
对应项目文件 15行
3. 通过docs文件夹路径 获取该文件夹所有文件路径
对应项目文件 12行
4. 通过路径判断是否 新增、更新、删除 该fileNode(文件节点)
通过
ctx.menu
路径判断是否 新增、更新、删除 该fileNode节点,从而生成一棵树在nodes
数组上的文件结构树对应项目文件 27行
5. 以docs文件路径 编译文档结构树的具体实现如下
对应项目文件 3行
6. 获取浏览器url 查找文档结构树中对应页面 ssr渲染
koa
获取浏览器请求时的url
,通过url
我们可以找到nodes
节点树上对应该文件的fileNode
文件节点, 获取该文件节点里面编译好的html内容,在通过vue3
,ssr
生成网页html,传入ctx.body内返回给前端对应项目文件 14行
7. 通过
watch
监听文件变化利用socket.io
实现热加载对应项目文件 13行
最后一步就是通过 open 打开浏览器以及对应端口
对应项目文件 36行
到此全部工序就完成了!
3.实现
vuepress build docs
打包功能命令执行在:
对应项目文件 43行
核心打包实现:
对应项目文件 66行
The text was updated successfully, but these errors were encountered: