我的博客搭建

我的博客搭建

通過 1 個月的咕咕咕,總算把博客初步搭建好了,按照慣例先丟個連接你們有興趣能夠來逛逛: blog.mytyiluo.cnjavascript

博客首頁

主要特色以下:css

  1. Modernized - 基於 Gatsbyjs,React,Typescript 構建;
  2. Opinionated - 以約束優先,減小博客中的必需參數;
  3. Git-based - 基於 GitHub 以及 Netlify 的自動化構建/部署;

爲何不使用現有的博客框架?

以前其實一直用的是 GitBook,但說實話,GitBook 仍是更加適合書籍/教程等的寫做,對隨筆來講仍是感受少了點功能。並且官方也表示以後再也不維護命令行程序了,所以決定慢慢的從這平臺遷移出來。html

在這過程當中,有考慮過使用 Hexo 做爲博客引擎,想本身寫一套主題,但無奈不習慣它的模板語法,最終仍是選擇了放棄。java

而後,考慮到開發的難度和靈活性,決定使用基於 React 的靜態網頁引擎,也就是 GatsbyJSnode

Gatsby工做流

其中,最吸引個人即是它對多數據源的內建支持,並以 GrapQL 的形式統一提供數據,很是符合我對後期拓展的預期。此外,豐富的社區插件倒算是額外的驚喜,特別是支持 TypeScript 和 Less 對開發帶來了極大的方便,支持響應式圖片和 PWA 也省去了我大量的精力。react

我但願的博客體驗

  1. 在本地用 VS Code 來編寫 Markdown,並將博客內容託管在 GitHub 上;
  2. 博客應該支持豐富的元信息,但大多數參數應該根據約定生成默認值;git

    例如,博客標題通常會和文件名相同; 同一文件夾下的博客應該爲同一個主題;github

  3. 博客對各種代碼應該有良好的展現,且支持插入多媒體內容,例如:Gist,B 站視頻等;typescript

搭建過程

廢話了那麼多,仍是得講些技術性的話題。shell

參考資料

關於 Gatsby 的參考資料其實並不須要太大,官方文檔再加官方教程已經足夠了。若涉及到插件的話,相應的文檔和源碼也均可以在 GitHub 找到。

GraphQL 數據的 TypeScript 支持

默認 GraphQL 執行的結果類型,不出意外都是 any 。但既然在使用 Ts 了,我仍是但願它是強類型的變量。可問題是根據不一樣的查詢語句返回的類型也都不同,因此單獨爲每個查詢定義類型工程量太大(懶)。

解決方案就是採用現成的代碼生成器:

yarn add --dev @graphql-codegen/cli @graphql-codegen/typescript

在根目錄下添加配置文件 codegen.yml :

# 這裏端口爲 gatsby develop 的端口
schema: http://localhost:8000/___graphql
generates:
  src/types.ts:
    plugins:
      - typescript
    # 默認類型爲 T | null | undefined
    # 這裏爲了偷懶就直接改成了 T
    config:
      avoidOptionals: true
      maybeValue: T

而後運行:

graphql-codegen --config codegen.yml

便可,咱們能夠在 src 文件夾中找到定義文件 types.ts 。

CSS Module 的 TypeScript 支持

CSS Module 是 Gatsby 所支持的一種樣式表導入方式,其優勢是在獨立的 css/less 文件中編寫,能夠得到完美的 IDE 支持,且不一樣模塊之間的樣式不會相互干擾。

但當其導入 ts 文件中後,會被提示沒法解析類型

我採用的解決方案相對暴力,在 src 文件夾下創建一個 global.d.ts 文件:

// 若你使用的不是less的話,改爲相應的後綴就行
declare module "*.less" {
  const content: { [className: string]: string };
  export = content;
}

若是 IDE 還不能識別其類型,能夠在根目錄的 tsconfig.json 中添加:

{
  // ...
  "typeAcquisition": {
    "include": ["./src/global.d.ts"]
  }
}

若還不行能夠嘗試重啓下 IDE。

CSS Module 生成的樣式名過長

默認的狀況下,CSS Module 生成的樣式名的格式爲:

[path][name]__[local]--[hash:base64:5]

顯然在多級目錄鑲嵌以後,其長度會是很是嚇人的,並且會影響傳輸效率(即便用 Gzip)

總之,我但願這樣式名在生產環境下短一些,解決方案以下:

在 gatsby-config.js 中配置 gatsby-plugin-less 選項:

{
  resolve: `gatsby-plugin-less`,
  options: {
    cssLoaderOptions: {
      minifyClassNames: true,
      localIdentName:
        process.env.NODE_ENV === "development"
          ? "[path][name]__[local]--[hash:base64:5]"
          : "[hash:base64:5]",
    },
  },
}

依賴項 sharp 安裝太慢

sharp 是 gatsby-plugin-sharp 的依賴項,提供圖像編輯處理的能力。 經過分析其安裝過程和文檔,咱們能夠將問題定位到 libvips 上,sharp 在 build 階段會從 github 下載 libvips 的預編譯版本(大概 14MB),但因爲國內網速的緣由致使其加載時間過長甚至加載失敗。

那解決方案就是經過任意手段下載對應版本的 libvips 將其複製到對應的緩存目錄下,通常爲:

C:\Users\[你的用戶名]\AppData\Roaming\npm-cache\_libvips

添加百度統計

考慮到 GA 在國內可能有些問題,這裏仍是選用了百度統計做爲統計分析平臺。同時,百度統計平臺還能夠主動將站點信息提交到百度搜索引擎,提升站點收錄的效率。

在 Gatsby 的社區插件中已經有實現了添加百度統計的插件,但我看了下它的源碼感受挺簡單的,且爲了方便以後再更換其餘統計平臺,這裏打算是本身實現一下。

首先,咱們須要註冊一個百度統計的帳號,而後在管理界面中獲取對應的代碼:

獲取百度統計代碼

根據文檔說明,咱們須要將這段代碼添加到每一個頁面的<head>中。

這裏,就須要用到 gatsby-ssr.js 中的接口 onRenderBody ,具體信息能夠查看文檔。 代碼以下:

// 由於在下方用到了JSX,因此須要導入React
var React = require("react");

exports.onRenderBody = ({ setHeadComponents }) => {
  if (process.env.NODE_ENV === `production`) {
    setHeadComponents([
      // 這裏的形式是爲了異步加載
      <script
        key="baidu-analytics-script"
        dangerouslySetInnerHTML={{
          __html: `
            var _hmt = _hmt || [];
            (function() {
              var hm = document.createElement("script");
              hm.src = "https://hm.baidu.com/hm.js?ff306397dbce7dab4357bd037fa286ca";
              var s = document.getElementsByTagName("script")[0];
              s.parentNode.insertBefore(hm, s);
            })();
          `
        }}
      />
    ]);
  }
};

統計博客標籤

在我設置的字段中,每篇博客都會包含一個 tags,類型爲 string[]。在首頁中,我但願根據標籤計數的降序顯示計數大於 2 的標籤。

其實,這個問題就至關於處理鑲嵌數組,處理難度並不高,但此前每每須要寫很多的代碼,這裏基於 lodash 的 chain 實現了一個相對「簡潔」的版本:

// data 就是根據 GraphQL 獲得的數據
// const data = this.props.data.statistics.nodes;
//
// Scheme 定義以下:
// statistics: allMarkdownRemark(
//   filter: { fields: { name: { ne: "README" }, posted: { ne: false } } }
// ) {
//   nodes {
//     fields {
//       topic
//       tags
//     }
//   }
// }
const tags = _
  // 包裝爲 chain 對象
  .chain(data)
  // 只須要 tags 字段
  .map(e => e.fields.tags)
  // 展開二維數組
  .flatten()
  // 對標籤計數
  // 這裏返回的爲對象 { tag: count }
  .countBy()
  // 篩選計數大於 2 的標籤
  .pickBy((value, key) => value > 2)
  // 將對象轉爲數組
  .map((value, key) => ({ tag: key, count: value }))
  // 根據計數降序排列
  .orderBy("count", "desc")
  // 只須要 tag 字段
  .map(e => e.tag)
  // 解除 chain 包裝
  .value();

小結

至此,主要記錄的是在開發階段遇到的一些坑和小技巧。下一篇,我想簡要敘述一下設計這個博客的思路,若是我還記得這個坑的話

相關文章
相關標籤/搜索