背景
面向普通用户的应用系统,包含很多产品的使用说明,属于静态页面
一方面占据整体项目的体积,打包时间用时长,打包结果体积大
另一方面,不断新增的不同产品的使用说明,对于前端开发人员来说,
相对开发真正的产品功能来说优先级较低,且占用开发时间
所以希望将产品使用说明与当前项目隔离,建立静态网站,存放使用说明
再由原系统进行跳转
将新增使用说明的任务交由相关产品负责人等非开发人员进行新增
问题
1.对于非开发人员来说,选择何种文档格式,可以既方便编写,又可以轻松转成html
2.原有系统用vue编写的批量文件如何迁移到新的静态网站
知识
vuepress
vuepress是一款使用vue驱动的静态网站生成器
用户可以使用markdown格式文件编辑文本,然后转义成html
另外页面中功能还可以使用vue进行嵌入
批量迁移
原系统vue编写的批量页面含有vue语法,不能直接迁移
所以需要将生成好的html爬下来转成md文件,应用到新系统
‘html-to-md’的npm 包可以帮助将html转换成md
解决
vuepress搭建
因为要与原系统主题风格样式类似,故需要在原来主题代码上进行修改
安装好vuepress包之后
将包里面的默认主题@vuepress/theme-default复制粘贴到doc/.vuepress/theme文件夹下面
修改里面的代码就可以直接应用了
配置config.js文件
在.vuepress文件夹下新增config.js1
2
3
4
5
6
7
8title:标签页名称
head:在页面头部引入的文件
base:当不在域名根目录下部署时,配置部署路径
themeConfig:{ 主题配置
logo:标题栏左上角logo
nav:右上角搜索后面的按钮
sidebar:左侧侧边栏
}
enhanceApp.js
在.vuepress文件夹下新增enhanceApp.js
该文件,可以添加对项目的额外处理
比如当前项目右上角三个按钮根据权限显示1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46export default ({
Vue, // VUE构造函数对象
options, // vue实例选项
router, // 路由对象
siteData//配置的数据
}) => {
async function getAuthorization(){
if(location.hostname.includes('localhost')){
return true
}
let cookie_str = document.cookie
let cookie_parts = cookie_str.split(';')
let auths = []
cookie_parts.forEach(c=>{
if (c){
let kv = c.split('=')
let k = kv[0].trim()
let v = kv[1].trim()
if (k == '_a'){
auths.push(v)
}
}
})
if(auths.length<1){
return false
}
for (var c of auths){
try {
let response = await axios.get(location.origin + '/api/v1/edit-user', {headers: {'X-Auth-Token': c}})
if(response.status < 400){
return true
}
} catch (err) {
console.log(err)
return false
}
}
}
getAuthorization().then(flag=>{
let divs = document.getElementsByClassName('nav-item')
if(flag){
divs[1].style.display = 'none'
divs[2].style.display = 'none'
}
})
}
更改页面呈现样式
增加悬浮锚点定位1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111page.vue
<template>
<main class="page">
<div v-show="$page.headers && isShowAncher && $page.frontmatter.ancherShow" class='ancherSection'>
<template v-for="(item,i) in headers">
<div :key="i">
<a @click="gotoAncher(item.slug)">{{item.title}}</a>
</div>
</template>
</div>
<slot name="top" />
<Content class="theme-default-content" />
<PageEdit />
<PageNav v-bind="{ sidebarItems }" />
<slot name="bottom" />
</main>
</template>
export default {
components: { PageEdit, PageNav},
props: ['sidebarItems'],
data () {
return {
isShowAncher: false, //滚动过程控制悬浮锚点
}
},
computed:{
headers:function(){//计算锚点内容
let ancherLevel = this.$page.frontmatter.ancherLevel
let sidebarDepth = this.$page.frontmatter.sidebarDepth?this.$page.frontmatter.sidebarDepth:this.$themeConfig.sidebarDepth
let levelStr = ancherLevel?ancherLevel:(sidebarDepth>1?'h2,h3':'h2')
if(ancherLevel && !(/^(h2|h3|h2\,\s*\h3)$/ig.test(ancherLevel.trim()))){
return []
}
if(levelStr.includes(',')){
return this.$page.headers && groupHeaders(this.$page.headers).length>0?this.$page.headers:[]
}else{
let level = +levelStr[1]
return this.$page.headers.filter(h => h.level === level)
}
}
},
updated(){
this.setPictureZoom()
},
mounted () {
let handleScroll= ()=>{
let scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop // 滚动条偏移量
this.isShowAncher = !!scrollTop
}
window.addEventListener('scroll',handleScroll,false)
this.setPictureZoom()
},
methods:{
//锚点定位
gotoAncher(ancher){
let el = document.getElementById(ancher)
let anchersHeight = document.getElementsByClassName('ancherSection')[0].clientHeight
window.scrollTo({
top: el.offsetTop-anchersHeight,
behavior: "smooth"
})
},
//添加图片放大功能
setPictureZoom () {
let img = document.querySelectorAll('p > img')
for (let i = 0; i < img.length; i++) {
if (img[i]) {
let a = document.createElement("a");
let parent = img[i].parentNode
parent.appendChild(a)
let src = img[i].getAttribute('src')
a.setAttribute('data-fancybox', '')
a.setAttribute('href', src)
a.appendChild(img[i])
}
}
}
}
}
</script>
<style lang="stylus">
@require '../styles/wrapper.styl'
.page
padding-bottom 2rem
display block
.ancherSection
position: fixed
background-color: white
box-shadow: rgb(234, 229, 229) 0px 1px 2px;
top: 64px
min-height: 42px
width: calc(100% - 320px);
display flex
align-items center
justify-content left
flex-wrap: wrap;
padding: 10px 0px 0px;
z-index:2
div
margin-left 20px
margin-bottom: 10px;
a
color black
cursor pointer
</style>
更换图标
在sidebarGroup.vue文件同级别新增arrow.svg图片,使用相对路径可插入1
<img src='./arrow.svg' v-if="collapsable" :class="open ? 'down' : ''" style='width:20px' />
批量迁移
目录改造
原有系统页面图片按照产品名命名文件夹,故先将整体文件结构进行改造
原来目录结构1
2
3
4
5
6
7
8
9
10
11--content
--a
--a1.png
--a2.png
......省略若干
--a13.png
--b
--b1.png
--b2.png
......省略若干
--b13.png
改造成1
2
3
4
5
6
7
8
9
10
11
12
13--content
--a
--images
--a1.png
--a2.png
......省略若干
--a13.png
--b
--images
--b1.png
--b2.png
......省略若干
--b13.png
就是在当前父文件夹下新增images文件夹,然后把图片移进去1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32var fs = require('fs');//引用文件系统模块
function readFileList(path, filesList) {
var files = fs.readdirSync(path);
files.forEach(function (itm) {
var obj = {};//定义一个对象存放文件的路径和名字
obj.path = path;//路径
obj.filename = itm//名字
filesList.push(obj);
})
}
var getFiles = {
//获取文件夹下的所有文件
getFileList: function (path) {
var filesList = [];
readFileList(path, filesList);
return filesList;
}
}
let files = getFiles.getFileList("./content/") //拿到a,b这一级别文件
for(let i=0;i<files.length;i++){
let filepath = files[i].path+files[i].filename+'/' //拼接a,b路径
let images = getFiles.getFileList(filepath) //拿到a,b下一级的文件,即所有图片
let imgpath = filepath+'images'
fs.mkdirSync(imgpath);//新建文件夹
for(let j = 0;j<images.length;j++){ //将图片移到新文件夹中
let oldpath = images[j].path+images[j].filename
let newpath = imgpath+'/'+images[j].filename
fs.rename(oldpath,newpath,function(err){
console.log(err)
})
}
}
转义文件
因为原项目为单页面文件,无法进行爬虫批量获取
最终实现也还是手动copy页面中dom,一个页面一个页面的copy
然后处理
安装转义包1
npm install html-to-md
开始使用1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24const html2md=require('html-to-md')
const fs = require('fs');
//在页面中找到使用说明的dom,copy之后存放到temp.txt中
//当做字符串被读取
var contentText = fs.readFileSync('temp.txt','utf-8');
let res = html2md(contentText)
//匹配md的‘[]()’书写形式
let reg1=/\[([\S ]*?)]\s?()\( *<?([^\s'"]*?(?:\([\S]*?\)[\S]*?)?)>?\s*(?:()(['"])(.*?)\5)? *\)/g
//匹配md的‘![]()’书写形式
let reg2 = /!\[([^\]]*?)][ \t]*()\([ \t]?<?([\S]+?(?:\([\S]*?\)[\S]*?)?)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*(?:(["'])([^"]*?)\6)?[ \t]?\)/g
//在每篇开头注入转义规则front matter
res = '---\nsidebarDepth: 2 \nancherShow: true \nancherLevel: h3 \n--- \n' + res.replace(reg1,function(group,text){ //html-to-md包转义结果将标题转移成没有连接但有连接名的书写形式[xxx](),所以这里将其替换成 md写法
return text?'## '+text:group
}).replace(reg2,function(...res){ //将图片引用路径做统一处理
let arr = res[3].slice(res[3].lastIndexOf('/')+1).split('.')
return '![](./images/'+arr[0]+'.'+arr[2]+')'
})
//将处理后的文件写入相应的文件夹中的manual.md文件中,即生成md文件
fs.writeFile("./content/xxxx/manual.md", res, function(err) {
if(err) {
return console.log(err);
}
console.log("The file was saved!");
});
所以最终文件夹目录为1
2
3
4
5
6
7--content
--a
--images
--manual.md
--b
--images
--manual.md
现在将content目录下文件全都粘贴到docs文件夹下面,与.vuepres平级1
2
3
4
5
6
7
8--docs
--.vuepress
--a
--images
--manual.md
--b
--images
--manual.md
就可以在.vuepress文件夹下面的config.js中配置sidebar了1
2
3
4
5
6
7
8
9 sidebar: [
{
title: '产品a',
collapsable: true,
children:[
['/a/manual.md', 'a操作手册'],
]
}
}
小结
1.如何将公共开源的项目更契合的应用到公司级项目,对原项目的破坏是避免不了的,比如替换主题颜色,替换相关图标,以及为了新增一些原来项目中没有的功能,而去更改原有处理逻辑,所谓二次开发,实质上,可能应该叫破坏性开发,不遵循原来的使用规则,在原来基础上,造自己的规则
2.对于大批量重复性工作的处理,要学会使用工具,
比如局部大量替换,借助vscode的局部锁定(先选取范围再点击下图1,锁定按钮)
再比如规则类似的替换,又要善于利用vscode的正则匹配去替换,如下图2
如果属于大规模处理,还要学会自己造工具
比如上述批量处理目录结构,改造md文件
人之所以为人,学会使用工具才能算是进入了文明社会