首發於微信公衆號《前端成長記》,寫於 2019.10.12
有句老話說的好,好記性不如爛筆頭。人生中,總有那麼些東西你願去執筆寫下。javascript
本文旨在把整個搭建的過程和遇到的問題及解決方案記錄下來,但願可以給你帶來些許幫助。css
本文涉及的主要技術:html
個人博客前端
個人博客的折騰史分紅下面三個階段:vue
第1種方式,文章內容採用 Markdown
編寫,靜態頁面經過 hexo 生成,部署到 Github Pages 上。缺點很明顯,每次有新內容,都須要從新編譯部署。java
第2種方式,靈活度極高,能夠按需開發。缺點也很明顯,開發和維護工做量大,同時還須要服務器和域名成本。node
第3種方式,採用 ISSUE
來記錄文章,自然支持 Markdown
,接口調用 Github Api,部署到 Github Pages 上。除了一次性開發外沒有任何額外成本。react
顯而易見,本博客此次改版就是基於第3種方式來實現的,接下來咱們從0開始一步步作。git
因爲是我的博客,技術選型能夠大膽嘗試。github
筆者選擇了 vue-cli 進行項目結構的初始化,同時採用 vue3.x
的語法 Composition-Api 進行頁面開發。採用 Github API v4
,也就是 GraphQL
語法進行 API
調用。
前往 Node.js官網 下載,這裏推薦下載 LTS
穩定版。下載後按照步驟進行安裝操做便可。
Window 下記得選上
Add To Path
,保證全局命令可用
執行如下代碼全局安裝便可。
npm install -g @vue/cli
經過 vue-cli
來初始化項目,按照下面內容選擇或自行按需選擇。
vue create my-blog
完成初始化並安裝依賴後,查看到的項目目錄以下:
@vue/composition-api
使用 Vue 3.0
語法必要依賴
npm install @vue/composition-api --save
graphql-request
簡單輕巧的的 graphQL
客戶端。一樣還有 Apollo
, Relay
等能夠進行選擇。選擇它的理由是:簡單輕巧,以及基於 Promise
。
npm install graphql-request --save
github-markdown-css
使用 Github
的風格渲染 Markdown
,選擇它的理由是原汁原味。
npm install github-markdown-css --save
個人博客以前是使用的 fexo 風格主題,因此本次也是以此爲UI依據進行開發。
項目總體分紅幾個頁面:
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 要求的鑑權。
這裏有兩個坑,只有在打包提交代碼後才發現:
token
不能直接提交到 Github
,不然再使用時會發現失效。這裏我猜想是安全掃描機制,因此我上面將 token
分紅兩部分拼接繞過這個。Content-Type
須要設置成 x-www-form-urlencoded
,不然會跨域請求失敗。接下來咱們將修改 main.js
文件,將請求方法掛載到 Vue實例
上。
... import Vue from 'vue'; import Http from './api/api'; Vue.prototype.$http = Http; ...
主要將介紹 composition-api
和 graphqh
相關,其他部分請查閱 Vue文檔
咱們首先須要引入 composition-api
,修改 main.js
文件
... import Vue from 'vue'; import VueCompositionApi from '@vue/composition-api'; Vue.use(VueCompositionApi); ...
而後新建一個 Archives.vue
去承接頁面內容。
首先的變更是生命週期的變更,使用 setup
函數代替了以前的 beforeCreate
和 created
鉤子。值得注意的有兩點:
Templates
一塊兒使用時返回一個給 template
使用的數據對象。this
對象,需使用 context.root
獲取到根實例對象。... export default { setup (props, context) { // 經過context.root獲取到根實例,找到以前掛載在Vue實例上的請求方法 context.root.$http(xxx) } } ...
數據查詢的語法參考 Github Api。
我這裏是以 blog
倉庫 的 issue
來做爲文章的,因此我這裏的查詢語法大體意思:
按照 owner
和 name
去查倉庫, owner
是 Github
帳號,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(); } }); }; ...
文章詳情分紅兩部分:文章詳情查詢和文章評論。
這裏首先引入 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; };
這裏我採用的是 utterances ,請按照步驟初始化項目,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(); }); }, }; ...
這裏碰到一個坑,找了好久沒有找到模糊搜索對應的查詢語法。
這裏感謝一下 simbawus ,解決了查詢語法的問題。具體查詢以下:
... 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
的所有語法,只是根據項目須要進行了一個基本的嘗試。
項目的提交採用 commitizen ,採用的理由是:提交格式規範化,能夠快速生成變動日誌等,後期可作成自動化。參考對應使用使用步驟使用便可。
項目的版本管理採用 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)