当前位置: 移动技术网 > IT编程>开发语言>JavaScript > 干货!从0开始,0成本搭建个人动态博客

干货!从0开始,0成本搭建个人动态博客

2019年10月15日  | 移动技术网IT编程  | 我要评论
首发于微信公众号《前端成长记》,写于 2019.10.12 导读 有句老话说的好,好记性不如烂笔头。人生中,总有那么些东西你愿去执笔写下。 本文旨在把整个搭建的过程和遇到的问题及解决方案记录下来,希望能够给你带来些许帮助。 本文涉及的主要技术: "Vue3.0 Composition API" "G ...

首发于微信公众号《前端成长记》,写于 2019.10.12

导读

有句老话说的好,好记性不如烂笔头。人生中,总有那么些东西你愿去执笔写下。

本文旨在把整个搭建的过程和遇到的问题及解决方案记录下来,希望能够给你带来些许帮助。

本文涉及的主要技术:

在线查看

背景

我的博客的折腾史分成下面三个阶段:

  • 基于 搭建静态博客,结合 github pages 提供域名和服务器资源

  • 自行采购服务器和域名,进行页面和接口的开发及部署,搭建动态博客

  • 基于 github pagesgithub api 搭建动态博客

第1种方式,文章内容采用 markdown 编写,静态页面通过 生成,部署到 github pages 上。缺点很明显,每次有新内容,都需要重新编译部署。

第2种方式,灵活度极高,可以按需开发。缺点也很明显,开发和维护工作量大,同时还需要服务器和域名成本。

第3种方式,采用 issue 来记录文章,天然支持 markdown ,接口调用 github api,部署到 github pages 上。除了一次性开发外没有任何额外成本。

显而易见,本博客这次改版就是基于第3种方式来实现的,接下来我们从0开始一步步做。

技术选型

由于是个人博客,技术选型可以大胆尝试。

笔者选择了 进行项目结构的初始化,同时采用 vue3.x 的语法 composition-api 进行页面开发。采用 github api v4 ,也就是 graphql 语法进行 api 调用。

上手开发

环境准备

node

前往 node.js官网 下载,这里推荐下载 lts 稳定版。下载后按照步骤进行安装操作即可。

window 下记得选上 add to path ,保证全局命令可用

vue-cli

执行以下代码全局安装即可。

npm install -g @vue/cli

项目初始化

通过 vue-cli 来初始化项目,按照下面内容选择或自行按需选择。

vue create my-blog

init

完成初始化并安装依赖后,查看到的项目目录如下:

dir

其他依赖安装

  1. @vue/composition-api

使用 vue 3.0 语法必要依赖

npm install @vue/composition-api --save
  1. graphql-request

简单轻巧的的 graphql 客户端。同样还有 apollo, relay 等可以进行选择。选择它的理由是:简单轻巧,以及基于 promise

npm install graphql-request --save
  1. github-markdown-css

使用 github 的风格渲染 markdown,选择它的理由是原汁原味。

npm install github-markdown-css --save

项目开发

我的博客之前是使用的 风格主题,所以本次也是以此为ui依据进行开发。

项目整体分成几个页面:

  • /archives 文章列表
  • /archives/:id 文章详情
  • /labels 标签列表
  • /links 友链
  • /about 关于
  • /board 留言板
  • /search 搜索

ⅰ.请求封装

查看源码

import { graphqlclient } from 'graphql-request';

import config from '../../config/config';
import loading from '../components/loading/loading';

const endpoint = 'https://api.github.com/graphql';

const graphqlclient = new graphqlclient(endpoint, {
  headers: {
    authorization: `bearer ${config.tokena}${config.tokenb}`,
    'x-requested-with': 'xmlhttprequest',
    'content-type': 'application/x-www-form-urlencoded; charset=utf-8',
  },
});

const http = (query = {}, variables = {}, alive = false) => new promise((resolve, reject) => {
  graphqlclient.request(query, variables).then((res) => {
    if (!alive) {
      loading.hide();
    }
    resolve(res);
  }).catch((error) => {
    loading.hide();
    reject(error);
  });
});

export default http;

我们可以看到配置了 headers ,这里是 github api 要求的鉴权。

这里有两个坑,只有在打包提交代码后才发现:

  1. token 不能直接提交到 github,否则再使用时会发现失效。这里我猜测是安全扫描机制,所以我上面将 token 分成两部分拼接绕过这个。

  2. content-type 需要设置成 x-www-form-urlencoded ,否则会跨域请求失败。

接下来我们将修改 main.js 文件,将请求方法挂载到 vue实例 上。

...
import vue from 'vue';
import http from './api/api';

vue.prototype.$http = http;
...

ⅱ.文章列表开发

查看源码

主要将介绍 composition-apigraphqh 相关,其余部分请查阅 vue文档

我们首先需要引入 composition-api ,修改 main.js 文件

...
import vue from 'vue';
import vuecompositionapi from '@vue/composition-api';

vue.use(vuecompositionapi);
...

然后新建一个 archives.vue 去承接页面内容。

首先的变动是生命周期的变动,使用 setup 函数代替了之前的 beforecreatecreated 钩子。值得注意的有两点:

  1. 该函数和 templates 一起使用时返回一个给 template 使用的数据对象。
  2. 该函数内没有 this 对象,需使用 context.root 获取到根实例对象。
...
export default {
  setup (props, context) {
    // 通过context.root获取到根实例,找到之前挂载在vue实例上的请求方法
    context.root.$http(xxx)
  }
}
...

数据查询的语法参考 github api

我这里是以 blog 仓库 的 issue 来作为文章的,所以我这里的查询语法大致意思:

按照 ownername 去查仓库, ownergithub 账号,name 是仓库名称。
查询 issues 文章列表,按照创建时间倒序返回,first 表示每次返回多少条。after 表示从哪开始查。所以结合这个就很容易实现分页,代码如下:

...
// 引入,使用 reactive 创建响应式对象
import {
  reactive,
} from '@vue/composition-api';
export default {
  setup (props, context) {
    const archives = reactive({
      cursor: null
    });
    const query = `query {
      repository(owner: "chenjiah", name: "blog") {
        issues(orderby: {field: created_at, direction: desc}, labels: null, first: 10, after:${archives.cursor}) {
          nodes {
            title
            createdat
            number
            comments(first: null) {
              totalcount
            }
          }
          pageinfo {
            endcursor
            hasnextpage
          }
        }
      }
    }`;
    // 通过context.root获取到根实例,找到之前挂载在vue实例上的请求方法
    context.root.$http(query).then(res => {
      const { nodes, pageinfo } = res.repository.issues
      archives.cursor = pageinfo.endcursor  // 最后一条的标识
    })
  }
}
...

ⅲ.标签列表开发

查看源码

这里我没有找到 issues 中返回全部的 labels 数据,所以只能先查全部 label ,再默认查询第一项 label ,语法如下:

...
const getdata = () => {
    const query = `query {
        repository(owner: "chenjiah", name: "blog") {
          issues(filterby: {labels: ${archives.label}}, orderby: {field: created_at, direction: desc}, labels: null, first: 10, after: ${archives.cursor}) {
            nodes {
              title
              createdat
              number
              comments(first: null) {
                totalcount
              }
            }
            pageinfo {
              endcursor
              hasnextpage
            }
            totalcount
          }
        }
      }`;
    context.root.$http(query).then((res) => {
      ...
    });
  };
  const getlabels = () => {
    context.root.$loading.show('努力为您查询');
    const query = `query {
      repository(owner: "chenjiah", name: "blog") {
        labels(first: 100) {
          nodes {
            name
          }
        }
      }
    }`;
    context.root.$http(query).then((res) => {
      archives.loading = false;
      archives.labels = res.repository.labels.nodes;

      if (archives.labels.length) {
        archives.label = archives.labels[0].name;

        getdata();
      }
    });
  };
...

ⅳ.文章详情开发

查看源码

文章详情分成两部分:文章详情查询和文章评论。

  1. 文章详情查询

这里首先引入 github-markdown-css 的样式文件,然后给 markdown 的容器加上 markdown-body 的样式名,内部将会自动渲染成 github 风格的样式。

...
<template>
...
    <div class="markdown-body">
      <p class="cont" v-html="issue.bodyhtml"></p>
    </div>
...
</template>
<script>
import {
  reactive,
  onmounted,
} from '@vue/composition-api';
import { islightcolor, formattime } from '../utils/utils';

export default {
    const { id } = context.root.$route.params;  // 获取到issue id
    const getdata = () => {
      context.root.$loading.show('努力为您查询');
      const query = `query {
          repository(owner: "chenjiah", name: "blog") {
            issue(number: ${id}) {
              title
              bodyhtml
              labels (first: 10) {
                nodes {
                  name
                  color
                }
              }
            }
          }
        }`;
      context.root.$http(query).then((res) => {
        const { title, bodyhtml, labels } = res.repository.issue;
        issue.title = title;
        issue.bodyhtml = bodyhtml;
        issue.labels = labels.nodes;
      });
    };
};
</script>
<style lang="scss" scoped>
  @import "~github-markdown-css";
</style>
...

注意这里有个label颜色的获取

众所周知,github label 的字体颜色是根据背景色自动调节的,所以我这里封装了一个方法判断是否为亮色,来设置文字颜色。

// islightcolor
const islightcolor = (hex) => {
  const rgb = [parseint(`0x${hex.substr(0, 2)}`, 16), parseint(`0x${hex.substr(2, 2)}`, 16), parseint(`0x${hex.substr(4, 2)}`, 16)];
  const darkness = 1 - (0.299 * rgb[0] + 0.587 * rgb[1] + 0.114 * rgb[2]) / 255;
  return darkness < 0.5;
};
  1. 文章评论部分

这里我采用的是 ,请按照步骤初始化项目,blog post 请选择 specific issue number ,这样评论才会是基于该 issue 的,也就是当前文章的。然后在页面中按下面方式配置你的相关信息引入:

...
import {
  reactive,
  onmounted,
} from '@vue/composition-api';
export default {
  setup(props, context) {
    const { id } = context.root.$route.params;  // issue id
    const initcomment = () => {
      const utterances = document.createelement('script');
      utterances.type = 'text/javascript';
      utterances.async = true;
      utterances.setattribute('issue-number', id);
      utterances.setattribute('theme', 'github-light');
      utterances.setattribute('repo', 'chenjiah/blog');
      utterances.crossorigin = 'anonymous';
      utterances.src = 'https://utteranc.es/client.js';

      // 找到对应容器插入,我这里用的是 comment
      document.getelementbyid('comment').appendchild(utterances);
    };

    onmounted(() => {
      initcomment();
    });    
  }
}
...

这个方案的好处是:数据完全来自 github issue ,并且自带登录体系,非常方便。

ⅴ.留言板开发

查看源码

刚好上面部分提到了 utterances ,顺势基于这个开发留言板,只需要把 blog post 更换成其他方式即可,我这里选择的是 issue-term ,自定义标题的单条 issue 下留言。为了避免跟文章那里区分,所以我使用另外一个仓库来管理留言。实现代码如下:

...
import {
  onmounted,
} from '@vue/composition-api';

export default {
  setup(props, context) {
    context.root.$loading.show('努力为您查询');

    const initboard = () => {
      const utterances = document.createelement('script');
      utterances.type = 'text/javascript';
      utterances.async = true;
      utterances.setattribute('issue-term', '【留言板】');
      utterances.setattribute('label', ':speech_balloon:');
      utterances.setattribute('theme', 'github-light');
      utterances.setattribute('repo', 'chenjiah/chenjiah.github.io');
      utterances.crossorigin = 'anonymous';
      utterances.src = 'https://utteranc.es/client.js';

      document.getelementbyid('board').appendchild(utterances);

      utterances.onload = () => {
        context.root.$loading.hide();
      };
    };

    onmounted(() => {
      initboard();
    });
  },
};
...

ⅵ.搜索页开发

查看源码

这里碰到一个坑,找了很久没有找到模糊搜索对应的查询语法。

这里感谢一下 ,解决了查询语法的问题。具体查询如下:

...
      const query = `query {
        search(query: "${search.value} repo:chenjiah/blog", type: issue, first: 10, after: ${archives.cursor}) {
          issuecount
          pageinfo {
            endcursor
            hasnextpage
          }
          nodes {
            ... on issue {
              title
              bodytext
              number
            }
          }
        }
      }`;
...

还好有 ... 拓展运算符,要不然 nodes 这里面的解析格式又不知道该怎么写了。

ⅶ.其他页面开发

其他页面多数为静态页面,所以按照相关的语法文档开发即可,没有什么特别的难点。

另外我这也未使用 composition-api 的全部语法,只是根据项目需要进行了一个基本的尝试。

项目发布和部署

项目的提交

项目的提交采用 ,采用的理由是:提交格式规范化,可以快速生成变更日志等,后期可做成自动化。参考对应使用使用步骤使用即可。

项目的版本管理

项目的版本管理采用 semantic versioning 2.0.0

项目的部署

编写了一个 deploy.sh 脚本,并配置到 package.json 中。执行 npm run deploy 将自动打包并推送到 gh-pages 分支进行页面的更新。

// package.json
{
  ...
  "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "lint": "vue-cli-service lint",
    "inspect": "vue-cli-service inspect",
    "deploy": "sh build/deploy.sh",
    "changelog": "conventional-changelog -p angular -i changelog.md -s -r 0 && git add changelog.md"
  },
 ...
}
#!/usr/bin/env sh

set -e

npm run build

cd dist

git init
git config user.name 'mcchen'
git config user.email 'chenjiahao.xyz@gmail.com'
git add -a
git commit -m 'deploy'

git push -f git@github.com:chenjiah/blog.git master:gh-pages

cd -

gh-pages 的使用需要先创建 用户名.github.io 的仓库

结尾

至此,一个0成本的动态博客已经完全搭建好了。开发过程中还遇到了一些 eslint 相关的提示和报错,直接搜索基本可解决。

如有疑问或不对之处,欢迎留言。

(完)


本文为原创文章,可能会更新知识点及修正错误,因此转载请保留原出处,方便溯源,避免陈旧错误知识的误导,同时有更好的阅读体验
如果能给您带去些许帮助,欢迎 ⭐️star 或 ✏️ fork
(转载请注明出处:https://chenjiahao.xyz)

如您对本文有疑问或者有任何想说的,请点击进行留言回复,万千网友为您解惑!

相关文章:

验证码:
移动技术网