# 风之家博客

# 1: 背景

作为一名开发人员,都希望拥有一个属于个人的博客网站,但是考虑到部署服务器、数据库那些比较麻烦,而博客主要是记录,分享一些文档,这时候静态博客网站就得心应手些。

曾几何时,Wordpress 平台在博客领域占据重要地位,它基于PHP动态语言 和 MySQL 数据库动态创建站点,丰富的插件模板及主题样式,得到了广大的程序员的追捧, 然而对于一名前端开发人员来说:后端语言基于php,对于一个前端开发人员来说,还是习惯使用 Node.js,于是乎我又去了解了基于 Hexo 框架,

Hexo 是一个快速、简洁且高效的博客框架,底层基于Node.js技术实现,Hexo 使用 Markdown(或其他渲染引擎)解析文章,在几秒内,即可利用靓丽的主题生成静态网页,也拥有丰富的主题模板及插件,很适合前端开发人员上手。

在了解到Hexo框架的时候,欣喜若狂,终于找到一个合适的框架来搭建自己的博客站点,网上学一些文档教程,很快就启动了一个本地站点,然而在浏览文档的时候,又发现了一个新起之秀的博客框架 vuepress,咋一看跟Vue 有什么关系,于是乎打开了vuepress 文档,瞬间被惊艳了.

Vue作为现在前端界最流行的前端框架,能用Vue技术快速搭建文档站点,简直就是巨大的福利,

VuePress 由两部分组成:第一部分是一个极简静态网站生成器,它包含由 Vue 驱动的主题系统和插件 API,另一个部分是为书写技术文档而优化的默认主题,它的诞生初衷是为了支持 Vue 及其子项目的文档需求。

每一个由 VuePress 生成的页面都带有预渲染好的 HTML,也因此具有非常好的加载性能和搜索引擎优化(SEO)。同时,一旦页面被加载,Vue 将接管这些静态内容,并将其转换成一个完整的单页应用(SPA),其他的页面则会只在用户浏览到的时候才按需加载。更多特性 vuepress

在经历一番折腾后,终于选定风之家博客的技术栈---- vuepress

# 2:踩坑之旅

1: 按照官网步骤,本地初始化了项目 2: 进行一些简单的配置后,站点很快的就运行起来 3: 发现一些存在的问题:

  • 侧边栏菜单 sideBar 属性每次需要手动配置
  • npm run docs:build 命令生成的文件夹dist 目录每次都会重新删除,提交git 部署后git 日志完全丢失

# 3:自动生成sideBar菜单

siderBar.js

windhome
└───docs
    └───.vuepress
        └───modules
            └───`siderBar.js`

/**
 * @description 读取docs 文件夹目录文件自动创建菜单数据结构
 * 
 */
const fs = require("fs");
const path = require("path");


// 读取.md 文件YAML front matter title 值或者sideBarTitle 内容设置导航栏标题
function readDocsTitle(filepath) {
    if (fs.existsSync(filepath)) {
        // -- 同步读取文件
        let content = fs.readFileSync(filepath, 'utf8');
        let match = content.match(/(sideBarTitle|title):(.*)/);
        if (match && match[2]) {
            return match[2]
        } else {
            return "";
        }
    } else {
        return "";
    }
}


// 截取文件路径设置为 路由path 值
function splitPath(p) {
    let regMatch = p.replace(/\\/g,"/").match(/docs(.*)/);
    return regMatch && regMatch[1];
}

// 读取文件夹
function readDocs(dir) {
    let result = [];
    (function readDocsFolder(dir_path) {
        if (fs.existsSync(dir_path)) {
            return fs.readdirSync(dir_path).forEach((item,index) => {
                // 过滤以点开头的文件夹,mac os 系统存在 .DS_Store 隐藏文件夹,过滤 node_modules 文件夹
                if (/^(\.|node_modules)/.test(item)) return;
                let current = `/${item}`;
                let subFolder = path.join(dir_path, `/${item}`);
                let prevFolder = "/" + path.parse(path.resolve(subFolder, "..")).name;
                if(prevFolder === "/docs") prevFolder = "";
                let info = fs.statSync(subFolder);
                if (info.isDirectory()) {
                    result.push({
                        id:current,parent_id:
                        prevFolder,fileName:"",title:"",
                        collapsable:true,sidebarDepth:1,isFile:false,path:splitPath(subFolder)});
                    readDocsFolder(subFolder);
                } else {
                    if (path.extname(item) === ".md") {
                        let title = readDocsTitle(subFolder);
                        result.push({id:current,parent_id:prevFolder,
                        fileName:item,title:title,
                        collapsable:true,sidebarDepth:1,isFile:true,path:splitPath(subFolder)});
                    }
                }
            });
        }
    })(dir);
    return result;
}

function toTree(data) {
    // 删除 所有 children,以防止多次调用
    data.forEach(function (item) {
        delete item.children;
    });
    // 将数据存储为 以 path 为 KEY 的 map 索引数据列
    var map = {};
    data.forEach(function (item) {
        map[item.id] = item;
    });
    var val = [];
    data.forEach(function (item) {
        // 以当前遍历项,的parent_id,去map对象中找到索引的id
        var parent = map[item.parent_id];
  
        // 修改README.md 文件名路径为空
        if(item.fileName === "README.md"){
            item.path = item.path.replace(/README\.md/,"");
        }
        if (parent) {
            if(item.parent_id + "/" !== item.path){
                (parent.children || (parent.children = [])).push(item);
            }
            if(item.id === "/README.md"){
                parent.title = item.title;
            }
        } else {
            //如果没有在map中找到对应的索引ID,那么直接把 当前的item添加到 val结果集中,作为顶级
            val.push(item);
        }
    });

    return  val;
}



function transformData(data){

    var result = {};

    // 侧边栏菜单过滤的文件夹路径
    const filterFolders = ["/resume","/about"]
    data.forEach(item => {
        // 过滤不需要生成侧边栏菜单的目录
        if(!filterFolders.includes(item.id)){
            result[item.id] = [item];
        }
    });

    return result;
}    

let sideBar = () => {
    // docs 根目录下
    let docsDir = path.resolve(__dirname, "../../");

    // 排除 .vuepress、node_modules 文件夹
    let folders = readDocs(docsDir);
    let tree = toTree(folders);
    return transformData(tree);
}

module.exports = sideBar;

# 4:提交GitHub

npm run docs:build 命令生成的文件夹dist 目录每次都会删除,然后导致 .git 日志文件完全丢失 代码提交每次都需要往GitHub重新创建项目,vuepress 官网提供的部署教程是使用 sh脚本文件,无法将项目开发目录同时提交至GitHub,于是乎自己写了一个task 脚本命令,将dist 目录下的文件copy 至文档跟目录,方便提交git 进行版本维护

const shell = require('shelljs');
const path = require("path");
const fs = require("fs");

const rootDir = path.resolve(__dirname);

const distDir = path.resolve(__dirname, "./docs/.vuepress/dist");

function fsExistsSync(path) {
    try{
        fs.accessSync(path,fs.F_OK);
    }catch(e){
        return false;
    }
    return true;
}

function findCopyFolders(dir) {
    let copyDistFolders = [];
    let files = fs.readdirSync(dir);
    files.forEach((item,index) => {
        copyDistFolders.push(path.join(rootDir,item));
    });
    return copyDistFolders;
}


function removeMoveFolders(folders){
    folders.forEach((item) => {
        if(fsExistsSync(item)){
            shell.rm("-rf",item);
        }
    });
}


removeMoveFolders(findCopyFolders(distDir));


shell.cp("-R",path.resolve(__dirname,"./docs/.vuepress/dist/*"), path.resolve(__dirname));  // 将move文件移动到target文件夹

# 5:部署GitHub Pages

  • 准备购买备案好的域名 www.windhome.win

  • GitHub ---> Setting ----> master branch ----> Custom domain (www.windhome.win)

# 6:TODO

  • 完善截图自动上传服务器,提供在线链接
  • 接入百度统计
  • 接入leancloud 云端API