VuePress + TailwindCss + Netlify 重寫我的獨立博客

創建本身的我的獨立博客已經將近七年時間。從一開始的 WordPress 到更換輕量級的 Ghost 博客,又到 JAMStackVuePressxlbd.me 也見證了我從一名後端開發者轉型成爲一名前端開發者。css

時至2020,我仍然對前端技術有着癡迷的熱情和動力驅使我去發現和學習更有趣、更前沿的前端新技術。html

新的博客主題折騰了幾個月了,才於今天(2020-06-01)正式發佈。送給我本身這個大孩子的禮物🎁。拖延的最主要緣由多是個人思惟比較跳躍,寫着寫着發現好多 idea 均可以寫成plugin(瞎折騰),因而就先寫插件去了。不過還算有所收穫,嘗試了編寫 VuePress插件、TailwindCss 插件和部署 Netlify,都是些新鮮的嘗試。前端

新博客 xlbd.me 歡迎訪問vue

Why VuePress?

Vue 技術棧狂熱者的利器webpack

採用 VuePress 跟我使用的技術棧有很大關係了。工做中使用的是 Vue ,寫 React 相對較少,若是我比較熟悉 React,那麼我可能就使用 Gatsby 了。以前寫過的《前端技術棧月刊》就是基於 VuePress 搭建的。git

VuePress 1.x 開始,就支持了自定義主題的功能,社區也層出不窮出現了不少 VuePress 主題,使用 VuePress 寫博客主題對於熟悉 Vue 技術棧的開發者來講,真的是太舒服了。VuePress 主題開發給開發者提供了不少內置的方法能夠調用。VuePress 自己就是一個 Vue 應用,可使用編寫組件的方式開發博客主題,甚至編寫 VuePress 插件。github

從 Ghost 遷移至 VuePress

Ghost 3.x 發佈之初,就發佈了一篇文章 Working With VuePress ,講解了如何經過 Ghost Content API 將 Ghost 做爲 Headless CMS,VuePress 做爲 靜態頁面生成器,將內容輸出爲 .md 文件的方法。web

教程已經詳細講解,這裏就不展開了。vue-cli

VuePress 插件

在寫博客主題的時候,Post 頁面展現的是博文列表,卡片形式。想使用一種能自動生成的圖案(pattern)代替沒有博客主圖的博文卡片,重要的是能夠根據不一樣的 seed 生成和 seed 綁定的惟一圖案。因而找到了兩種生成 svg pattern 做爲背景圖片的庫:npm

因而乎造輪子開始

vuepress-plugin-geopattern

geopattern 是我爲 VuePress 寫的第一個插件,VuePress Plugin 官方文檔上有好四種插件能夠去編寫,geopattern 其實就是一個全局UI組件。

Edit vuepress-plugin-geopattern

vuepress-plugin-hero-pattern

hero-pattern 一樣是一個VuePress 全局UI組件,它是基於 Hero Pattern Plain Svg 編寫的插件,插件將可複用、可重複的 SVG,經過 mini-svg-data-uri 工具轉化爲 background-image 所需的 data-uri 實現背景圖功能。

Edit vuepress-plugin-hero-pattern

vuepress-plugin-svg-sprite

這是一個嘗試失敗的插件,我想將 SVG Sprite 功能遷移到 VuePress 上,本地固然是可用的。可是想作成一個成熟的插件遇到了一點點困難,可能我仍是沒有找到好的辦法。

  • 方案一

    我想使用 svg-sprite-loader 插件自動注入 SVG Sprite,並暴露一個全局UI組件 SvgIcon ,可是插件的路徑傳參數是個問題。

  • 方案二

    不借助 svg-sprite-loader 的狀況下,將 svg icon 生成 SVG Sprite,插入到 dom 結構,然而嘗試後發現,轉換 sprite 的類庫使用 svgo 將SVG 優化後,會丟掉一些圖形。致使 icon 變得難看,甚至就不顯示了。

不過我相信這個插件後續會寫好的。👀下面介紹在 VuePress 如何正確使用 SVG Sprite

VuePress 如何使用 SVG Sprite?

VuePress 編寫UI插件,還算比較好實現的,主要概念在 Option Api → enhanceAppFiles,還容許你像使用熟悉的 vue-cli 3/4 腳手架配置文件 vue.config.js 的方式去修改 webpack 配置,Option Api -> chainWebpack,有了這兩點,就能夠將 SVG Sprite 功能遷移過來。

配置 webpack

// .vuepress/plugins/svg-sprite/index.js
const path = require('path')
const fs = require('fs')
const resolve = dir => path.join(__dirname, dir)

module.exports = (options, context) => {
  // plugin options iconsDir, point to '.vuepress/public/icons'
  const { iconsDir = '.vuepress/public/icons' } = options
  const iconsPath = path.isAbsolute(iconsDir)
    ? iconsDir
    : path.resolve(context.sourceDir, iconsDir)

  if (!fs.existsSync(iconsPath)) {
    console.log(`svg-sprite: Folder ${iconsPath} does not exist`)
  }

  return {
    name: 'svg-sprite',
    enhanceAppFiles: [
      resolve('enhanceApp.js')
    ],
    chainWebpack (config) {
      config.module
        .rule('svg')
        .exclude.add(iconsPath)
        .end()
      config.module
        .rule('svg-sprite-loader')
        .test(/\.svg$/)
        .include.add(iconsPath)
        .end()
        .use('svg-sprite-loader')
        .loader('svg-sprite-loader')
        .options({
          symbolId: 'icon-[name]'
        })
        .end()
        .before('svg-sprite-loader')
        .use('svgo-loader')
        .loader('svgo-loader')
        .options({
          plugins: [
            { removeTitle: true },
            { convertColors: { shorthex: false } },
            { convertPathData: false }
          ]
        })
        .end()
    }
  }
}
複製代碼

編寫 UI 組件

編寫一個 functional Component 便可

// .vuepress/plugins/svg-sprite/enhanceApp.js
const importAllSvg = () => {
  // require.context api must provide a literal path, that's a limit for plugin
  // https://webpack.js.org/guides/dependency-management/#requirecontext
  const icons = require.context('../../public/icons', false, /\.svg$/)
  const importAll = r => r.keys().map(r)
  importAll(icons)
}

export default ({ Vue }) => {
  importAllSvg()

  // regisiter a svg-icon component
  Vue.component('svg-icon', {
    functional: true,
    props: {
      symbol: {
        type: String,
        required: true
      },
      className: {
        type: String,
        default: ''
      }
    },
    render: function (h, { data, props, children }) {
      return h(
        'svg',
        {
          ...data,
          class: [
            'svg-icon',
            `svg-icon-${props.className}`
          ],
          style: {
            width: '1em',
            height: '1em',
            'vertical-align': '-0.15em',
            fill: 'currentColor',
            overflow: 'hidden'
          },
          attrs: { 'aria-hidden': true }
        },
        [
          h('use', {
            attrs: {
              'xlink:href': `#icon-${props.symbol}`
            }
          })
        ]
      )
    }
  })
}
複製代碼

引用插件

// .vuepress/config.js
const SvgSprite = require('./plugins/svg-sprite/index')

module.exports = {
  title: '小蘿蔔丁',
  ...
  plugins: [
    ...
    [
      SvgSprite
    ]
  ]
}
複製代碼

使用 icon

<svg-icon symbol="heart" />
複製代碼

Why TailwindCSS?

「御風而行」的 CSS,開發體驗極佳

Tailwind CSS

TailwindCss 是一個很棒的CSS類庫,與其說是類庫,不如說是一個超大的樣式類工具集合,若是你掌握甚至習慣了 Tailwind 的語法。你會愛上它的。

Tailwind 提供了功能類至上移動端優先多種CSS僞類變體自定義插件等強大的核心功能。

功能類至上

Tailwind 提供大量的甚至說龐大的樣式類聲明,使得咱們在編寫頁面樣式的時候,能夠不用寫一行 style 就能實現大部分場景,好比咱們有一個div,想經過 flex 佈局實現垂直居中功能,咱們須要編寫以下CSS:

.flex-center {
	display: flex;
	justify-content: center;
	align-items: center;
}
複製代碼

使用 Tailwind CSS 只需在元素 class 上聲明以下:

<div class="flex justify-center items-center">I am a div</div>
複製代碼

不過讓開發者在元素 class 上編寫一堆 class 名字,稍微有點反人類,許多開發者也常常詬病於此,尤爲是使用 Vue 寫組件的時候,咱們的組件會變得很難看,甚至醜出天際。

還好 Tailwind 還有另外一種寫法,使用 @apply 指令,經過 @apply 指令編寫的 Tailwind 代碼以下:

// 單行聲明
.flex-center {
    @apply flex justify-center items-center
}

// 或者多行聲明
.flex-center {
    @apply flex
    @apply justify-center
    @apply items-center
}
複製代碼

這樣看上去好一些,把 Tailwind 提供的樣式類編寫在一個 class 聲明中,代替 style 的正常寫法,若是習慣了 Tailwind,它真的就是你快速開發頁面原型的利器了。

移動端優先?

Tailwind 默認使用了相似 Bootstrap 的移動端優先的斷點系統,在響應式頁面設計裏,咱們應該優先編寫移動端視口(最小的斷點)樣式,再去調試其餘視口的樣式。也就是說若是你是按着 PC 端大屏幕的樣式開發完成頁面的樣式,切換到移動端視口下,頁面的展示未必是你想要的樣子。

Tailwind CSS 默認的斷點設置:

/* Small (sm) */
@media (min-width: 640px) { /* ... */ }

/* Medium (md) */
@media (min-width: 768px) { /* ... */ }

/* Large (lg) */
@media (min-width: 1024px) { /* ... */ }

/* Extra Large (xl) */
@media (min-width: 1280px) { /* ... */ }
複製代碼

好比要讓一個標題在不一樣的視口下展現的字體大小不同:

<div class="text-lg sm:text-xl md:text-3xl lg:text-4xl xl:text-5xl">
	The Responsive Title
</div>
複製代碼

不一樣屏幕下看到的標題字體大小就會不一樣,響應式開發在 class 裏就能完成,並不須要寫在統一的媒體查詢(@media)裏了。這種開發體驗是否是很是爽。

CSS 僞類變體?

CSS 開發中咱們比較熟悉的一些 CSS 僞類,好比:hoverfocusactivefirst-childlast-child 等,在 Tailwind 中都提供了更方便的 class 聲明。

好比讓一個按鈕有 hover 效果,咱們須要編寫樣式以下:

.btn {
    background-color: #f4a;
}

.btn:hover {
    background-color: #4af;
}
複製代碼

使用 Tailwind CSS 僞類變體,只需在 class 前加上 hover: 便可實現:

<button class="bg-transparent hover:bg-blue-500...">
  Hover me
</button>
複製代碼

這樣是寫法真的是大大減小了 Style 的編寫量。

VuePress + TailwindCSS

如何在 VuePress 項目裏使用 TailwindCSS 呢,TailwindCSS Installation 官方文檔上說的其實很清楚了。一共四個步驟:

一、安裝 Tailwind

# Using npm
npm install tailwindcss

# Using Yarn
yarn add tailwindcss
複製代碼

二、在主題樣式裏引入 Tailwind

文件路徑: .vuepress/theme/styles/index.styl

// .vuepress/theme/styles/index.styl
@tailwind base;
@tailwind components;
@tailwind utilities;
複製代碼

三、初始化 Tailwind 配置文件

項目根目錄下:./tailwind.config.js

// tailwind.config.js
module.exports = {
  theme: {},
  variants: {},
  plugins: [],
}
複製代碼

四、使用 PostCSS 處理 Tailwind

VuePress 配置文件中有配置 postcss 的選項

// .vuepress/config.js
module.exports = {
	...
  postcss: {
    plugins: [
      require('tailwindcss'),
      require('autoprefixer')
    ]
  }
}
複製代碼

這樣就可使用 tailwind 的全部的樣式類了。

Tailwind Theme

Tailwind 可在 配置文件中聲明主題(theme)的配置,基本上全部默認主題提供的設置均可以覆蓋,這提供給咱們很大的自由度,好比聲明博客的色系,咱們能夠經過改變主題配置中的 colors 便可使用本身聲明的顏色 class 了。

// tailwind.config.js

module.exports = {
  theme: {
    ...
    extend: {
      colors: {
        ...
        primary: {
          default: '#139ce7',
          hover: '#53BAED',
          dark: '#4799eb'
        }
        ...
      }
    }
  }
}
複製代碼

colors 的配置 Tailwind 會幫你生成對應的 class 聲明,colors 影響到全部使用顏色的類,好比 textbgborderdivideplaceholder ,使用顏色類時以 text 舉例會這樣聲明:

<span class="text-gray-700 hover:text-primary">I am a span</span>
複製代碼

Dark Mode

暗色風格頁面都已經流行了一年多了。在蘋果系統 macOS、iOS 生態裏不少應用或者微信裏都提供了暗色風格都UI頁面,暗色風格UI已經成爲了一個流行趨勢,個人新博客也加上了暗色風格UI,採用的是 CSS 媒體查詢特性 prefers-color-scheme ,博客主題會根據系統是否切換成深色來自動切換UI風格。

這個功能實現歸功於 Tailwind,Tailwind 提供了優秀的配置主題擴展,只需聲明一個媒體查詢便可:

// tailwind.config.js

module.exports = {
  theme: {
    screens: {
			...
      dark: {
        raw: '(prefers-color-scheme: dark)'
      }
    }
	}
}
複製代碼

如何使用暗色風格的媒體查詢,就像使用 smmdlgxl 這種響應式媒體查詢同樣:

<span class="text-gray-700 dark:text-gray-200">Color will change in dark mode</span>
複製代碼

PS:關於暗色風格UI,目前我尚未作成可隨時切換的按鈕,這也是計劃中的功能。

TailwindCSS 插件

都寫過 VuePress 插件了。要不要也嘗試一個 Tailwind 插件呢,其實也很是簡單。好比博客中大量複用了漸變色文字,我徹底能夠寫一個工具類,讓這些可複用的樣式變爲一種模式。

插件代碼以下:

const plugin = require('tailwindcss/plugin')

module.exports = plugin(function ({ addUtilities, theme }) {
  const colors = theme('colors', {})

  const newUtilities = {
    '.text-neon': {
      'background-image': `linear-gradient(90deg, ${colors.switchNeon.pink} 0px, ${colors.switchNeon.lightBlue} 100%)`,
      'background-clip': 'text',
      '-webkit-background-clip': 'text',
      '-webkit-text-fill-color': 'transparent'
    },
    '.bg-neon': {
      'background-image': `linear-gradient(90deg, ${colors.switchNeon.pink} 0px, ${colors.switchNeon.lightBlue} 100%)`
    }
  }

  addUtilities(newUtilities, {
    variants: ['responsive', 'hover', 'focus']
  })
})
複製代碼

在 Tailwind 配置文件中引入插件:

// tailwind.config.js

module.exports = {
  ...
  plugins: [
    require('./.vuepress/plugins/tailwind/gradients')
  ]
}
複製代碼

實際使用的時候,只需在 class 上添加上 text-neon 便可讓文字變成漸變色,這也響應了 Tailwind 功能類優先的核心思想。

You don't need TailwindCss?

原諒我這裏標題黨了,我不須要使用 TailwindCss 嗎?不,我固然須要 TailwindCss 來開發個人新博客主題,我想說的是,你有沒有想過 TailwindCss 的樣式類,咱們本身也是能夠寫出來的,好比使用預處理器 Sass/SCSSStylus 又或者是 Less 語法。

拿 CSS 中的 display 屬性舉例 ,在 TailwindCss 咱們引用的源文件中,TailwindCss 生成的代碼實際是這樣的:

.block {
  display: block
}

.inline-block {
  display: inline-block
}

.inline {
  display: inline
}

.flex {
  display: flex
}

.inline-flex {
  display: inline-flex
}

.grid {
  display: grid
}

...
複製代碼

全部的 display 屬性,咱們平常開發都能用到嗎?其實並非。不過它就聲明在那裏。因此 TailwindCss 最終生成的文件體積接近 **1 MB** 級別。這也是讓不少開發者頭疼的地方,必定要搭配 Webpack + PostCss 進行處理才能減小實際使用的代碼體積。

其實想要獲得上看的類聲明很簡單,就是提早定義好了類的聲明,咱們隨時使用就行了。像這樣簡單的類聲明,咱們能夠輕易的使用CSS 預處理工具去實現。好比使用 SCSS:

// loop display list
$displayList: ('block', 'inline-block', 'inline', 'flex', 'inline-flex', 'grid');

@each $display in $displayList {
  .#{$display} {
    display: #{$display};
  }
}
複製代碼

想要深刻探索 TailwindCss 類生成的原理,其實很簡單,這裏不展開說明,我正在計劃寫另外一篇文章,介紹 TailwindCss 工具類是如何生成的?

VuePress + TailwindCSS Starter

VuePress + TailwindCSS 真的給了我很快捷的頁面開發體驗,不一樣於像使用 Element-UI、iView 這種 UI 庫,編寫出來的頁面風格幾乎千篇一概,咱們須要本身實現一些頁面樣式設計,這樣纔是屬於本身的獨立博客 😎。

爲了方便給本身寫的 VuePress 插件有個調試環境,一開始是在本身的博客 Repo 上調試,後來本身的博客 Repo 用於測試已發佈到 NPM 的 VuePress 插件。因而須要一個開發環境測試。

我創建了一個簡潔的、VuePress + TailwindCSS 初始模版。一樣適用於調試新寫的 VuePress 或者 TailwindCss 插件,建議想要嘗試的小夥伴看這裏:

vuepress-tailwind-theme-starter

Why Netlify?

Netlify 比你想象的還好用

Netlify 是一個一站式的網站構建平臺,雖然 GitHub Page 足夠支撐一個靜態頁面的發佈了,可是 Netlify 提供了更高性能的 Jamstack 頁面構建,同時個人新博客也是使用 Jamstack 構建的。Netlify 同時也支持綁定我的獨立域名,還能夠幫個人域名加上 https 證書。真是大愛。

VuePress + Netlify (CI / CD)

VuePress 如何在 Netlify 上發佈呢?VuePress 官方文檔上已經有說明文檔了。只須要兩步:

  • 鏈接你的 GitHub 項目,設定配置項:

    Build Command: yarn build

    Publish directory: .vuepress/dist

  • 設定自動構建的分支,好比: develop ,點擊 deploy 按鈕

Netlify 會自動幫你生成一個可訪問的網址,咱們也能夠改變站點的三級域名名稱,好比改爲 xlbd.netlify.app ,或者綁定本身的獨立域名。

就這樣每次咱們向指定分支 push 代碼的時候,Netlify 就會自動幫你構建併發布到域名上,完成自動部署。

綁定域名

xlbd.me 是使用 Godaddy 申請的域名,這裏是使用的 Netlify DNS Nameservers 進行配置的域名解析

Netlify DNS Nameservers

配置 Nameservers 後,Netlify 會自動生成四個 Netlify DNS Nameservers

dns1.p04.nsone.net
dns2.p04.nsone.net
dns3.p04.nsone.net
dns4.p04.nsone.net
複製代碼

202006_netlify_dns.jpg)

只需將 Netlify DNS Nameservers 配置在 Godaddy 上。

配置 Godaddy DNS

這種配置方式有個缺點,可能以前你的域名解析的記錄都不能使用了。

202006_godaddy.jpg)

HTTPS

令我歡喜的是,Netlify 提供了一鍵集成 https 的功能,使用的是 Let's Encrypt 提供證書,只要你的域名 DNS 解析成功後,https 域名就已經有證書能使用了。great!

202006_netlify_https.jpg)

如今就能夠經過 xlbd.me 來訪問個人博客了。

Notion Image Hub

感謝 notion 最近發佈的我的版免費計劃,取消了1000個塊的限制,notion 已經成爲我平常工做的夥伴,notion 顛覆了我全部使用過的 markdown 軟件,想要的功能基本都有,甚至能夠構建一個靜態頁面,發佈博客。

以前我一直在 Mac 上一直使用的是 MWeb,MWeb 也是一個很是棒的知識管理軟件,尤爲是對圖牀、發佈媒體的支持,我以前把寫好的博文,經過 MWeb 鏈接 Ghost 直接發佈。也是方便的很。

此次我使用 notion 做爲個人圖牀,能夠在 notion 創建一個 database,上傳一些博文圖片,將圖片鏈接做爲個人博文圖片輸入。也是個不錯的方法。

Ghost Theme Kaldorei

Ghost 博客主題 ghost-theme-kaldorei 已經運行了四年多,也見證了 Ghost 從 1.x 版本到如今的 Ghost 3.x 版本,Ghost 確實在不斷變化着,越變越好。不過 Ghost 的後臺文本編輯器一直對中文支持不太好,一直讓國內開發者所詬病,還好我有 MWeb 爲我發佈內容。

ghost-theme-kaldorei 如今仍然支持在最新版的 Ghost 3.x 中使用,4年來感謝 250+ star 的支持,在 GitHub Topics #ghost-theme 中,Kaldorei 以第九名排進前十。我會一直維護這個主題。雖然代碼有點老。仍是用的 jQuery!

我可能計劃後續會遷移一個 vuepress-theme-kaldorei 版本到 vuepress 上,或者出如今使用 JAMStack 技術棧的其餘方案上。延續 Kaldorei 的風格。

能夠經過 blog.xlbd.me 訪問它

博客後續規劃

  • Disqus Support
  • Book Corner
  • About Page
  • Switch Light / Dark Mode

前端技術的變化確實在快步向前,從刀耕火種到年代,到現代前端的模塊化、工程化前端工具層出不窮時代。咱們該感謝開源社區以及傑出優秀色的開發者們,是他們改變了歷史、影響了不少人的事業以及人生也不爲過,我就是其中一個幸運的被影響者 😄。

原文:www.xlbd.me/posts/2020-…

相關文章
相關標籤/搜索