创建本身的我的独立博客已经将近七年时间。从一开始的 WordPress
到更换轻量级的 Ghost
博客,又到 JAMStack
的 VuePress
,xlbd.me 也见证了我从一名后端开发者转型成为一名前端开发者。css
时至2020,我仍然对前端技术有着痴迷的热情和动力驱使我去发现和学习更有趣、更前沿的前端新技术。html
新的博客主题折腾了几个月了,才于今天(2020-06-01)正式发布。送给我本身这个大孩子的礼物🎁。拖延的最主要缘由多是个人思惟比较跳跃,写着写着发现好多 idea 均可以写成plugin(瞎折腾),因而就先写插件去了。不过还算有所收获,尝试了编写 VuePress插件、TailwindCss 插件和部署 Netlify,都是些新鲜的尝试。前端
新博客 https://xlbd.me 欢迎访问vue
Vue 技术栈狂热者的利器
采用 VuePress
跟我使用的技术栈有很大关系了。工做中使用的是 Vue
,写 React
相对较少,若是我比较熟悉 React,那么我可能就使用 Gatsby
了。以前写过的《前端技术栈月刊》就是基于 VuePress 搭建的。webpack
从 VuePress 1.x
开始,就支持了自定义主题的功能,社区也层出不穷出现了不少 VuePress 主题,使用 VuePress 写博客主题对于熟悉 Vue 技术栈的开发者来讲,真的是太舒服了。VuePress 主题开发给开发者提供了不少内置的方法能够调用。VuePress 自己就是一个 Vue 应用,可使用编写组件的方式开发博客主题,甚至编写 VuePress 插件。git
Ghost 3.x 发布之初,就发布了一篇文章 Working With VuePress ,讲解了如何经过 Ghost Content API 将 Ghost 做为 Headless CMS,VuePress 做为 静态页面生成器,将内容输出为 .md
文件的方法。github
教程已经详细讲解,这里就不展开了。web
在写博客主题的时候,Post 页面展现的是博文列表,卡片形式。想使用一种能自动生成的图案(pattern)代替没有博客主图的博文卡片,重要的是能够根据不一样的 seed 生成和 seed 绑定的惟一图案。因而找到了两种生成 svg pattern 做为背景图片的库:vue-cli
因而乎造轮子开始npm
geopattern 是我为 VuePress 写的第一个插件,VuePress Plugin 官方文档上有好四种插件能够去编写,geopattern 其实就是一个全局UI组件。
hero-pattern 一样是一个VuePress 全局UI组件,它是基于 Hero Pattern Plain Svg
编写的插件,插件将可复用、可重复的 SVG,经过 mini-svg-data-uri 工具转化为 background-image
所需的 data-uri
实现背景图功能。
这是一个尝试失败的插件,我想将 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 编写UI插件,还算比较好实现的,主要概念在 Option Api → enhanceAppFiles,还容许你像使用熟悉的 vue-cli 3/4 脚手架配置文件 vue.config.js 的方式去修改 webpack 配置,Option Api -> chainWebpack,有了这两点,就能够将 SVG Sprite 功能迁移过来。
// .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() } } }
编写一个 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 ] ] }
<svg-icon symbol="heart" />
“御风而行”的 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 伪类,好比:hover
、focus
、active
、first-child
、last-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 呢,TailwindCSS Installation 官方文档上说的其实很清楚了。一共四个步骤:
# Using npm npm install tailwindcss # Using Yarn yarn add tailwindcss
文件路径: .vuepress/theme/styles/index.styl
// .vuepress/theme/styles/index.styl @tailwind base; @tailwind components; @tailwind utilities;
项目根目录下:./tailwind.config.js
// tailwind.config.js module.exports = { theme: {}, variants: {}, plugins: [], }
VuePress 配置文件中有配置 postcss
的选项
// .vuepress/config.js module.exports = { ... postcss: { plugins: [ require('tailwindcss'), require('autoprefixer') ] } }
这样就可使用 tailwind 的全部的样式类了。
Tailwind 可在 配置文件中声明主题(theme)的配置,基本上全部默认主题提供的设置均可以覆盖,这提供给咱们很大的自由度,好比声明博客的色系,咱们能够经过改变主题配置中的 colors
便可使用本身声明的颜色 class 了。
// tailwind.config.js module.exports = { theme: { ... extend: { colors: { ... primary: { default: '#139ce7', hover: '#53BAED', dark: '#4799eb' } ... } } } }
colors 的配置 Tailwind 会帮你生成对应的 class 声明,colors 影响到全部使用颜色的类,好比 text
、bg
、border
、divide
、placeholder
,使用颜色类时以 text
举例会这样声明:
<span class="text-gray-700 hover:text-primary">I am a span</span>
暗色风格页面都已经流行了一年多了。在苹果系统 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)' } } } }
如何使用暗色风格的媒体查询,就像使用 sm
、md
、lg
、xl
这种响应式媒体查询同样:
<span class="text-gray-700 dark:text-gray-200">Color will change in dark mode</span>
PS:关于暗色风格UI,目前我尚未作成可随时切换的按钮,这也是计划中的功能。
都写过 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 功能类优先的核心思想。
原谅我这里标题党了,我不须要使用 TailwindCss 吗?不,我固然须要 TailwindCss 来开发个人新博客主题,我想说的是,你有没有想过 TailwindCss 的样式类,咱们本身也是能够写出来的,好比使用预处理器 Sass/SCSS
、Stylus
又或者是 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 真的给了我很快捷的页面开发体验,不一样于像使用 Element-UI、iView 这种 UI 库,编写出来的页面风格几乎千篇一概,咱们须要本身实现一些页面样式设计,这样才是属于本身的独立博客 😎。
为了方便给本身写的 VuePress 插件有个调试环境,一开始是在本身的博客 Repo 上调试,后来本身的博客 Repo 用于测试已发布到 NPM
的 VuePress 插件。因而须要一个开发环境测试。
我创建了一个简洁的、VuePress + TailwindCSS 初始模版。一样适用于调试新写的 VuePress 或者 TailwindCss 插件,建议想要尝试的小伙伴看这里:
vuepress-tailwind-theme-starter
Netlify
比你想象的还好用
Netlify 是一个一站式的网站构建平台,虽然 GitHub Page 足够支撑一个静态页面的发布了,可是 Netlify 提供了更高性能的 Jamstack 页面构建,同时个人新博客也是使用 Jamstack 构建的。Netlify 同时也支持绑定我的独立域名,还能够帮个人域名加上 https 证书。真是大爱。
VuePress 如何在 Netlify 上发布呢?VuePress 官方文档上已经有说明文档了。只须要两步:
Build Command: yarn build
Publish directory: .vuepress/dist
develop
,点击 deploy
按钮Netlify 会自动帮你生成一个可访问的网址,咱们也能够改变站点的三级域名名称,好比改为 xlbd.netlify.app ,或者绑定本身的独立域名。
就这样每次咱们向指定分支 push 代码的时候,Netlify 就会自动帮你构建并发布到域名上,完成自动部署。
xlbd.me 是使用 Godaddy
申请的域名,这里是使用的 Netlify DNS Nameservers 进行配置的域名解析
配置 Nameservers 后,Netlify 会自动生成四个 Netlify DNS Nameservers
:
dns1.p04.nsone.net dns2.p04.nsone.net dns3.p04.nsone.net dns4.p04.nsone.net
只需将 Netlify DNS Nameservers 配置在 Godaddy 上。
这种配置方式有个缺点,可能以前你的域名解析的记录都不能使用了。
令我欢喜的是,Netlify 提供了一键集成 https 的功能,使用的是 Let's Encrypt
提供证书,只要你的域名 DNS 解析成功后,https 域名就已经有证书能使用了。great!
如今就能够经过 https://xlbd.me 来访问个人博客了。
感谢 notion 最近发布的我的版免费计划,取消了1000个块的限制,notion 已经成为我平常工做的伙伴,notion 颠覆了我全部使用过的 markdown
软件,想要的功能基本都有,甚至能够构建一个静态页面,发布博客。
以前我一直在 Mac 上一直使用的是 MWeb,MWeb 也是一个很是棒的知识管理软件,尤为是对图床、发布媒体的支持,我以前把写好的博文,经过 MWeb 链接 Ghost 直接发布。也是方便的很。
此次我使用 notion 做为个人图床,能够在 notion 创建一个 database,上传一些博文图片,将图片链接做为个人博文图片输入。也是个不错的方法。
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 的风格。
能够经过 http://blog.xlbd.me 访问它
前端技术的变化确实在快步向前,从刀耕火种到年代,到现代前端的模块化、工程化前端工具层出不穷时代。咱们该感谢开源社区以及杰出优秀色的开发者们,是他们改变了历史、影响了不少人的事业以及人生也不为过,我就是其中一个幸运的被影响者 😄。
原文: https://www.xlbd.me/posts/202...