提起帮助文档,想必大家都会想到 VuePress等,我也体验了一下,但是感觉和我的思路不太一样,我希望的是那种可以直接在线编辑文档,然后无需编译就可以直接发布的方式,另外可以在线写(修改)代码并且运行的效果。

VuePress 是“静态网站生成器”,需要我们自行编写文档,然后交给VuePress变成网站,VuePress 并没有提供编写环境,我知道有很多编写 Markdown 的方式,但是我还是喜欢编写、浏览合为“一体”的方式。

似乎没有,那么 —— 自己动手丰衣足食吧,开干!

技术栈

  • 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 即可。

基本功能就是这样,心急的可以先看在线演示和源码。

  1. 在线演示:https://nfpress.gitee.io/nf-press-edit/
  2. 源码:https://gitee.com/nfpress/nf-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

  <el-menu
:default-active="activeIndex2"
class="el-menu-demo"
mode="horizontal"
v-bind="$attrs"
:background-color="backgroundColor"
@select="handleSelect"
>
<el-menu-item
v-for="(item, index) in naviList"
:key="index"
:index="item.naviId"
>
{{item.text}}
</el-menu-item>
</el-menu>

可以是多级的导航,暂时没有实现在线维护功能。

  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

  <template v-for="(item, index) in subMenu">
<!--树枝-->
<template v-if="item.children && item.children.length > 0">
<el-sub-menu
:key="item.menuId + '_' + index"
:index="item.menuId"
style="vertical-align: middle;"
>
<template #title>
<div style="display:inline;width: 100%;">
<component
:is="$icon[item.icon]"
style="width: 1.5em; height: 1.5em; margin-right: 8px;vertical-align: middle;"
>
</component>
<span>{{item.text}}</span>
</div>
</template>
<!--递归子菜单-->
<my-sub-menu2
:subMenu="item.children"
:dialogAddInfo="dialogAddInfo"
:dialogModInfo="dialogModInfo"
/>
</el-sub-menu>
</template>
<!--树叶-->
<el-menu-item v-else
:index="item.menuId"
:key="item.menuId + 'son_' + index"
>
<template #title>
<div style="display:inline;width: 100%;">
<span style="float: left;">
<component
:is="$icon[item.icon]"
style="width: 1.5em; height: 1.5em; margin-right: 8px;vertical-align: middle;"
>
</component>
<span >{{item.text}}</span>
</span>
</div>
</template>
</el-menu-item>
</template>
  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 的功能:

  <v-md-editor
:toolbar="toolbar"
left-toolbar="undo redo clear | tip emoji code | h bold italic strikethrough quote | ul ol table hr | link image | save | customToolbar"
:include-level="[1, 2, 3, 4]"
v-model="current.docInfo.md"
:height="editHeight + 'px'"
@save="mySave"
>
</v-md-editor>
  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

  <div style="padding: 5px; border: 1px solid #ccc!important;">
<async-comp></async-comp>
</div>

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 的 baseUrl
const 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'
// 设置icon
import 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' // emoji
import 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' // todoList
import 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文件里面进行整体布局

  <el-container>
<el-header>
<!--导航-->
<div style="float: left;">
<!--写网站logo、标题等-->
<h1>nf-press</h1>
</div>
<div style="float: right;min-width: 100px;height: 60px;padding-top: 13px;">
<!--写网站logo、标题等-->
<el-switch v-model="$state.current.isView" v-bind="itemProps"></el-switch>
</div>
<div style="float: right;min-width: 600px;height: 60px;">
<!--网站导航-->
<doc-navi ></doc-navi>
</div>
</el-header>
<el-container>
<!--左侧边栏-->
<el-aside width="330px">
<!--菜单-->
<doc-menu ></doc-menu>
</el-aside>
<el-main>
<!--文档区域-->
<component
:is="docControl[$state.current.isView]"
/>
</el-main>
</el-container>
</el-container>
  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:根据状态选择加载显示组件或者编辑组件的字典。

这种方式虽然有点麻烦,但是比较灵活,可以根据需要进行各种灵活设置,比如添加版权信息、备案信息、广告等内容。

导航、菜单、编辑和浏览

直接使用组件实现,比较简单不搬运了,直接看源码即可。

打包发布与版本管理

需要打包的情况分为两种:第一次打包、修改代码(非在线编辑的代码)后打包。

如果只是文档内容有变化的话,只需要直接上传json文件即可,不需要再次打包。

内置了一个简单的版本管理功能,可以通过 ver.json文件里的版本号实现更新功能。

源码

https://gitee.com/nfpress/nf-press-edit

在线演示

https://nfpress.gitee.io/nf-press-edit/

demo

https://gitee.com/nfpress/doc-ui-elp

基于 vite2 + Vue3 写一个在线帮助文档工具的更多相关文章

  1. 基于 React 开发了一个 Markdown 文档站点生成工具

    Create React Doc 是一个使用 React 的 markdown 文档站点生成工具.就像 create-react-app 一样,开发者可以使用 Create React Doc 来开发 ...

  2. Swagger解决你手写API接口文档的痛

    首先,老规矩,我们在接触新事物的时候, 要对之前学习和了解过的东西做一个总结. 01 痛     苦 不做.不行 之前,前后端分离的系统由前端和后端不同的编写,我们苦逼的后端工程师会把自己已经写完的A ...

  3. Spring Boot (十五): 优雅的使用 API 文档工具 Swagger2

    1. 引言 各位在开发的过程中肯定遇到过被接口文档折磨的经历,由于 RESTful 接口的轻量化以及低耦合性,我们在修改接口后文档更新不及时,导致接口的调用方(无论是前端还是后端)经常抱怨接口与文档不 ...

  4. C# 复制PDF页面到另一个PDF文档

    C# 复制PDF页面到另一个PDF文档 有时候我们可能有这样一个需求,那就是把PDF页面从一个PDF文档复制到另一个PDF文档中.由于PDF文档并不像word文档那样好编辑,因此复制也相对没有那么容易 ...

  5. 放弃antd table,基于React手写一个虚拟滚动的表格

    缘起 标题有点夸张,并不是完全放弃antd-table,毕竟在react的生态圈里,对国人来说,比较好用的PC端组件库,也就antd了.即便经历了2018年圣诞彩蛋事件,antd的使用者也不仅不减,反 ...

  6. C# 复制一个Word文档的部分或全部内容到另一个Word文档

    C# 复制一个Word文档的部分或全部内容到另一个Word文档 我最近喜欢折腾Office软件相关的东西,想把很多Office软件提供的功能用.NET来实现,如果后期能把它用来开发一点我自己的小应用程 ...

  7. 根据Schema写出XML文档四部曲

    Schema约束文档本身就是一个XML文档,扩展名为xsd 难点:XML文档的根元素怎么写? 如下4步曲: a.首先看Schema文档,找到根元素 <?xml version="1.0 ...

  8. Java:API文档;文档注释中的javadoc标记;官方API;自己动手给项目建一个API文档

    1.什么是API文档 在Java语言中有3种注释 //单行注释 /* 多行注释 */ /** * 文档注释 */ API(应用程序接口)文档就是用javadoc命令提取文档注释生成的,html格式,用 ...

  9. 一个非常适合IT团队的在线API文档、技术文档工具 (ShowDoc)

    在逸橙呆了不到两年,开发时后端都有开发接口API,来到数库,好多后端开发和前端沟通是还是发doc文档,很不方便,我向cto反应,自己找到这个,老乡田雷(php,隔壁村的)也用过,可能某些原因选择其他的 ...

随机推荐

  1. Redis 源码简洁剖析 05 - ziplist 压缩列表

    ziplist 是什么 Redis 哪些数据结构使用了 ziplist? ziplist 特点 优点 缺点 ziplist 数据结构 ziplist 节点 pre_entry_length encod ...

  2. ApacheCN JavaScript 译文集(二) 20211123 更新

    使用 Meteor 构建单页 Web 应用 零.前言 一.制作 Meteor 应用 二.构建 HTML 模板 三.存储数据和处理集合 四.控制数据流 五.使我们的应用与路由通用 六.保持会话状态 七. ...

  3. 【第十二期】腾讯后台实习初试、复试、HR面经 (许愿OC)

    楼主投的很晚属于正常批才开始,初试面试官比较重基础,复试面试官比较看综合能力,HR小姐姐声音好听,腾讯面试官都特别nice! 一面: 看你项目很多,你挨个给我介绍一遍吧 我:一大堆按着简历介绍 日志文 ...

  4. 【转】Mysql相关子查询&&MySQL获取分组后的TOP N记录

    https://www.cnblogs.com/Yiran583/p/6743870.html select * from test1 a where 2 > (select count(*) ...

  5. [转载] IOS 获取网络图片的大小 改变 图片色值 灰度什么的方法集合

    IOS 获取网络图片的大小 改变 图片色值 灰度什么的方法集合

  6. 区段统计 mysql 语句 case when then end as

    EXPLAIN SELECT COUNT(*),CASEWHEN device_width > 729 THEN '>729'WHEN device_width BETWEEN '720' ...

  7. pytest-html 测试报告

    前言 上一篇文章pytest简介中,执行测试用例后,在 pycharm 控制台(方式一)或 Terminal(方式二)中可以查看测试结果.但是在实际的接口自动化项目中一般需要生成直观的测试报告,这个测 ...

  8. fuzz——AFL基础使用方法

    最近打 ctf 的时候感觉有点遇到瓶颈,就来 fuzz 这块看看. AFL 全称为 American huzzy loop,是 Fuzzing 最高级的测试工具之一.这个工具对有源码和无源码的二进制程 ...

  9. 《PHP程序员面试笔试宝典》——在被企业拒绝后是否可以再申请?

    如何巧妙地回答面试官的问题? 本文摘自<PHP程序员面试笔试宝典> 很多企业为了能够在一年一度的招聘季节中,提前将优秀的程序员锁定到自己的麾下,往往会先下手为强.他们通常采取的措施有两种: ...

  10. 有个姑娘叫history

    文章目录 常用参数 history的一些用法 修改history命令默认保存的数量 来给history穿衣服 让我们重新认识一下history history命令用于显示用户以前执行过的历史命令,并且 ...