搭建基于Hexo+Butterfly主题的个人博客站


前言

Hexo是基于NodeJS构建简洁高效的博客框架。使用 Markdown 编写文章内容,几秒内即可利用靓丽的主题生成静态网页。支持丰富的主题与插件,主题用于页面展示效果,插件用于实现博客的特殊功能 如:文章及内容搜索、图片懒加载、一键远程部署等。

Butterfly是Hexo的主题之一,也是本文将要进行集成的主题。官方网站也是该主题的演示页面,其教程更加详细,本文为入门级教学,使用该主题不需要对hexo框架有过多了解,遵循butterfly的写作规范开发即可。

安装

安装Hexo之前需要先安装nodejs(建议版本12.13.0)。没安装nodejs请自行查询安装方法,本文将不对nodejs做说明。

全局安装Hexo命令脚本

hexo全局命令用于 初始化Hexo项目、生成静态页面、一键远程部署、本地运行Web页面调试等。

$ npm install -g hexo-cli

创建Hexo项目

开发工具推荐使用VsCode,喜欢用别的工具也可以。

1. 创建工程目录

 在本地任意文件夹下创建一个空目录 如下

2. 初始化目录结构

使用vscode打开该目录,然后在控制台执行以下命令 (控制台快捷键 Ctrl + ~)

$ hexo init

上面命令执行完毕,等待目录结构构建完成即可

3. 目录介绍

下面为 hexo init 命令生成的目录结构

需要关心的几个文件夹及目录如下

  • sourece 源码数据目录,一般存放博客markdown源文件数据和资源数据,这个目录下的数据才会被打包编译。
    • sources/_posts 目录存放博客源文件(md)
    • 后期我们主题需要的页面也会在source目录下创建。
  • themes 主题存放目录,主题的引入共两种方式,一种源码方式引入,一种npm方式,这个目录下存放的为源码方式引入的主题。
  • _config.yml 核心配置文件,此文件可配置博客标题、输出目录结构、访问路径规则、主题的引用配置以及一键发布的相关信息配置。
    • 该文件中的配置不固定,取决于使用的插件,如果使用图片懒加载或本地内容搜索功能也都需要在该文件里配置信息。
    • 一般主题配置会独立出一个单独的yml文件,如_config.landscape.yml文件就是主题相关配置文件。

4. 启动默认主题项目

执行如下命令,会在本地启动一个web服务来访问静态博客站。暂时不对主题做任何更改,先看下默认界面

$ hexo server

出现如下字样表示启动完毕 访问 http://localhost:4000/ 

默认主题的Hexo项目

  • 如上几步操作就运行了一个hexo的博客项目。还是挺简单的,下面我们进行butterfly主题配置使用(一般都不使用默认主题,默认主题不美观且没有定制化的东西)。

Butterfly主题配置

1. 使用npm方式安装主题

执行如下命令进行主题依赖的安装, npm可能安装缓慢,如果你知道怎么使用yarn 可以使用yarn安装它

卸载默认主题

$ npm uninstall hexo-theme-landscape

安装主题

$ npm install hexo-theme-butterfly --save

安装依赖插件

$ npm install hexo-renderer-pug hexo-renderer-stylus --save

2. 修改必要配置

修改主题引用

修改_config.yml 中 theme 值改为 butterfly

主题配置文件

修改 _config.landscape.yml 名称为 _config.butterfly.yml ,将下面内容复制进_config.butterfly.yml中

查看代码
# Main menu navigation (導航目錄)
# see https://butterfly.js.org/posts/4aa8abbe/#導航菜單
# --------------------------------------

menu:
  # Home: / || fas fa-home
  # Archives: /archives/ || fas fa-archive
  # Tags: /tags/ || fas fa-tags
  # Categories: /categories/ || fas fa-folder-open
  # List||fas fa-list:
  #   Music: /music/ || fas fa-music
  #   Movie: /movies/ || fas fa-video
  # Link: /link/ || fas fa-link
  # About: /about/ || fas fa-heart

# Code Blocks (代碼相關)
# --------------------------------------

highlight_theme: light #  darker / pale night / light / ocean / mac / mac light / false
highlight_copy: true # copy button
highlight_lang: true # show the code language
highlight_shrink: false # true: shrink the code blocks / false: expand the code blocks | none: expand code blocks and hide the button
highlight_height_limit: false # unit: px
code_word_wrap: false

# copy settings
# copyright: Add the copyright information after copied content (複製的內容後面加上版權信息)
copy:
  enable: true
  copyright:
    enable: false
    limit_count: 50

# social settings (社交圖標設置)
# formal:
#   icon: link || the description
social:
  # fab fa-github: https://github.com/xxxxx || Github
  # fas fa-envelope: mailto:xxxxxx@gmail.com || Email

# search (搜索)
# --------------------------------------

# Algolia search
algolia_search:
  enable: false
  hits:
    per_page: 6

# Local search
local_search:
  enable: false

# Math (數學)
# --------------------------------------
# About the per_page
# if you set it to true, it will load mathjax/katex script in each page (true 表示每一頁都加載js)
# if you set it to false, it will load mathjax/katex script according to your setting (add the 'mathjax: true' in page's front-matter)
# (false 需要時加載,須在使用的 Markdown Front-matter 加上 mathjax: true)

# MathJax
mathjax:
  enable: false
  per_page: false

# KaTeX
katex:
  enable: false
  per_page: false
  hide_scrollbar: true

# Image (圖片設置)
# --------------------------------------

# Favicon(網站圖標)
favicon: /img/favicon.png

# Avatar (頭像)
avatar:
  img: https://i.loli.net/2021/02/24/5O1day2nriDzjSu.png
  effect: false

# Disable all banner image
disable_top_img: false

# The banner image of home page
index_img:

# If the banner of page not setting, it will show the top_img
default_top_img:

# The banner image of archive page
archive_img:

# If the banner of tag page not setting, it will show the top_img
# note: tag page, not tags page (子標籤頁面的 top_img)
tag_img:

# The banner image of tag page
# format:
#  - tag name: xxxxx
tag_per_img:

# If the banner of category page not setting, it will show the top_img
# note: category page, not categories page (子分類頁面的 top_img)
category_img:

# The banner image of category page
# format:
#  - category name: xxxxx
category_per_img:

cover:
  # display the cover or not (是否顯示文章封面)
  index_enable: true
  aside_enable: true
  archives_enable: true
  # the position of cover in home page (封面顯示的位置)
  # left/right/both
  position: both
  # When cover is not set, the default cover is displayed (當沒有設置cover時,默認的封面顯示)
  default_cover:
    # - https://i.loli.net/2020/05/01/gkihqEjXxJ5UZ1C.jpg

# Replace Broken Images (替換無法顯示的圖片)
error_img:
  flink: /img/friend_404.gif
  post_page: /img/404.jpg

# A simple 404 page
error_404:
  enable: false
  subtitle: 'Page Not Found'
  background: https://i.loli.net/2020/05/19/aKOcLiyPl2JQdFD.png

post_meta:
  page: # Home Page
    date_type: created # created or updated or both 主頁文章日期是創建日或者更新日或都顯示
    date_format: date # date/relative 顯示日期還是相對日期
    categories: true # true or false 主頁是否顯示分類
    tags: false # true or false 主頁是否顯示標籤
    label: true # true or false 顯示描述性文字
  post:
    date_type: both # created or updated or both 文章頁日期是創建日或者更新日或都顯示
    date_format: date # date/relative 顯示日期還是相對日期
    categories: true # true or false 文章頁是否顯示分類
    tags: true # true or false 文章頁是否顯示標籤
    label: true # true or false 顯示描述性文字

# wordcount (字數統計)
wordcount:
  enable: false
  post_wordcount: true
  min2read: true
  total_wordcount: true

# Display the article introduction on homepage
# 1: description
# 2: both (if the description exists, it will show description, or show the auto_excerpt)
# 3: auto_excerpt (default)
# false: do not show the article introduction
index_post_content:
  method: 3
  length: 500 # if you set method to 2 or 3, the length need to config

# anchor
# when you scroll in post, the URL will update according to header id.
anchor: false

# Post
# --------------------------------------

# toc (目錄)
toc:
  post: true
  page: false
  number: true
  expand: false
  style_simple: false # for post

post_copyright:
  enable: true
  decode: false
  license: CC BY-NC-SA 4.0
  license_url: https://creativecommons.org/licenses/by-nc-sa/4.0/

# Sponsor/reward
reward:
  enable: false
  QR_code:
    # - img: /img/wechat.jpg
    #   link:
    #   text: wechat
    # - img: /img/alipay.jpg
    #   link:
    #   text: alipay

# Post edit
# Easily browse and edit blog source code online.
post_edit:
  enable: false
  # url: https://github.com/user-name/repo-name/edit/branch-name/subdirectory-name/
  # For example: https://github.com/jerryc127/butterfly.js.org/edit/main/source/
  url:

# Related Articles
related_post:
  enable: true
  limit: 6 # Number of posts displayed
  date_type: created # or created or updated 文章日期顯示創建日或者更新日

# figcaption (圖片描述文字)
photofigcaption: false

# post_pagination (分頁)
# value: 1 || 2 || false
# 1: The 'next post' will link to old post
# 2: The 'next post' will link to new post
# false: disable pagination
post_pagination: 1

# Displays outdated notice for a post (文章過期提醒)
noticeOutdate:
  enable: false
  style: flat # style: simple/flat
  limit_day: 500 # When will it be shown
  position: top # position: top/bottom
  message_prev: It has been
  message_next: days since the last update, the content of the article may be outdated.

# Share System (分享功能)
# --------------------------------------

# AddThis
# https://www.addthis.com/
addThis:
  enable: false
  pubid:

# Share.js
# https://github.com/overtrue/share.js
sharejs:
  enable: true
  sites: facebook,twitter,wechat,weibo,qq

# AddToAny
# https://www.addtoany.com/
addtoany:
  enable: false
  item: facebook,twitter,wechat,sina_weibo,facebook_messenger,email,copy_link

# Comments System
# --------------------------------------

comments:
  # Up to two comments system, the first will be shown as default
  # Choose: Disqus/Disqusjs/Livere/Gitalk/Valine/Waline/Utterances/Facebook Comments/Twikoo/Giscus
  use: # Valine,Disqus
  text: true # Display the comment name next to the button
  # lazyload: The comment system will be load when comment element enters the browser's viewport.
  # If you set it to true, the comment count will be invalid
  lazyload: false
  count: false # Display comment count in post's top_img
  card_post_count: false # Display comment count in Home Page

# disqus
# https://disqus.com/
disqus:
  shortname:
  apikey: # For newest comments widget

# Alternative Disqus - Render comments with Disqus API
# DisqusJS 評論系統,可以實現在網路審查地區載入 Disqus 評論列表,兼容原版
# https://github.com/SukkaW/DisqusJS
disqusjs:
  shortname:
  apikey:
  option:

# livere (來必力)
# https://www.livere.com/
livere:
  uid:

# gitalk
# https://github.com/gitalk/gitalk
gitalk:
  client_id:
  client_secret:
  repo:
  owner:
  admin:
  option:

# valine
# https://valine.js.org
valine:
  appId: # leancloud application app id
  appKey: # leancloud application app key
  avatar: monsterid # gravatar style https://valine.js.org/#/avatar
  serverURLs: # This configuration is suitable for domestic custom domain name users, overseas version will be automatically detected (no need to manually fill in)
  bg: # valine background
  visitor: false
  option:

# waline - A simple comment system with backend support fork from Valine
# https://waline.js.org/
waline:
  serverURL: # Waline server address url
  bg: # waline background
  visitor: false
  option:

# utterances
# https://utteranc.es/
utterances:
  repo:
  # Issue Mapping: pathname/url/title/og:title
  issue_term: pathname
  # Theme: github-light/github-dark/github-dark-orange/icy-dark/dark-blue/photon-dark
  light_theme: github-light
  dark_theme: photon-dark

# Facebook Comments Plugin
# https://developers.facebook.com/docs/plugins/comments/
facebook_comments:
  app_id:
  user_id: # optional
  pageSize: 10 # The number of comments to show
  order_by: social # social/time/reverse_time
  lang: en_US # Language en_US/zh_CN/zh_TW and so on

# Twikoo
# https://github.com/imaegoo/twikoo
twikoo:
  envId:
  region:
  visitor: false
  option:

# Giscus
# https://giscus.app/
giscus:
  repo:
  repo_id:
  category_id:
  theme:
    light: light
    dark: dark
  option:

# Chat Services
# --------------------------------------

# Chat Button [recommend]
# It will create a button in the bottom right corner of website, and hide the origin button
chat_btn: false

# The origin chat button is displayed when scrolling up, and the button is hidden when scrolling down
chat_hide_show: false

# chatra
# https://chatra.io/
chatra:
  enable: false
  id:

# tidio
# https://www.tidio.com/
tidio:
  enable: false
  public_key:

# daovoice
# http://daovoice.io/
daovoice:
  enable: false
  app_id:

# gitter
# https://gitter.im/
gitter:
  enable: false
  room:

# crisp
# https://crisp.chat/en/
crisp:
  enable: false
  website_id:

# Footer Settings
# --------------------------------------
footer:
  owner:
    enable: true
    since: 2020
  custom_text:
  copyright: true # Copyright of theme and framework

# Analysis
# --------------------------------------

# Baidu Analytics
# https://tongji.baidu.com/web/welcome/login
baidu_analytics:

# Google Analytics
# https://analytics.google.com/analytics/web/
google_analytics:

# CNZZ Analytics
# https://www.umeng.com/
cnzz_analytics:

# Cloudflare Analytics
# https://www.cloudflare.com/zh-tw/web-analytics/
cloudflare_analytics:

# Microsoft Clarity
# https://clarity.microsoft.com/
microsoft_clarity:

# Advertisement
# --------------------------------------

# Google Adsense (谷歌廣告)
google_adsense:
  enable: false
  auto_ads: true
  js: https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js
  client:
  enable_page_level_ads: true

# Insert ads manually (手動插入廣告)
# ad:
#   index:
#   aside:
#   post:

# Verification (站長驗證)
# --------------------------------------

site_verification:
  # - name: google-site-verification
  #   content: xxxxxx
  # - name: baidu-site-verification
  #   content: xxxxxxx

# Beautify/Effect (美化/效果)
# --------------------------------------

# Theme color for customize
# Notice: color value must in double quotes like "#000" or may cause error!

# theme_color:
#   enable: true
#   main: "#49B1F5"
#   paginator: "#00c4b6"
#   button_hover: "#FF7242"
#   text_selection: "#00c4b6"
#   link_color: "#99a9bf"
#   meta_color: "#858585"
#   hr_color: "#A4D8FA"
#   code_foreground: "#F47466"
#   code_background: "rgba(27, 31, 35, .05)"
#   toc_color: "#00c4b6"
#   blockquote_padding_color: "#49b1f5"
#   blockquote_background_color: "#49b1f5"
#   scrollbar_color: "#49b1f5"

# The top_img settings of home page
# default: top img - full screen, site info - middle (默認top_img全屏,site_info在中間)
# The position of site info, eg: 300px/300em/300rem/10% (主頁標題距離頂部距離)
index_site_info_top:
# The height of top_img, eg: 300px/300em/300rem (主頁top_img高度)
index_top_img_height:

# The user interface setting of category and tag page (category和tag頁的UI設置)
# index - same as Homepage UI (index 值代表 UI將與首頁的UI一樣)
# default - same as archives UI 默認跟archives頁面UI一樣
category_ui: # 留空或 index
tag_ui: # 留空或 index

# Website Background (設置網站背景)
# can set it to color or image (可設置圖片 或者 顔色)
# The formal of image: url(http://xxxxxx.com/xxx.jpg)
background:

# Footer Background
footer_bg: false

# the position of bottom right button/default unit: px (右下角按鈕距離底部的距離/默認單位為px)
rightside-bottom:

# Enter transitions (開啓網頁進入效果)
enter_transitions: true

# Background effects (背景特效)
# --------------------------------------

# canvas_ribbon (靜止彩帶背景)
# See: https://github.com/hustcc/ribbon.js
canvas_ribbon:
  enable: false
  size: 150
  alpha: 0.6
  zIndex: -1
  click_to_change: false
  mobile: false

# Fluttering Ribbon (動態彩帶)
canvas_fluttering_ribbon:
  enable: false
  mobile: false

# canvas_nest
# https://github.com/hustcc/canvas-nest.js
canvas_nest:
  enable: false
  color: '0,0,255' #color of lines, default: '0,0,0'; RGB values: (R,G,B).(note: use ',' to separate.)
  opacity: 0.7 # the opacity of line (0~1), default: 0.5.
  zIndex: -1 # z-index property of the background, default: -1.
  count: 99 # the number of lines, default: 99.
  mobile: false

# Typewriter Effect (打字效果)
# https://github.com/disjukr/activate-power-mode
activate_power_mode:
  enable: false
  colorful: true # open particle animation (冒光特效)
  shake: true #  open shake (抖動特效)
  mobile: false

# Mouse click effects: fireworks (鼠標點擊效果: 煙火特效)
fireworks:
  enable: false
  zIndex: 9999 # -1 or 9999
  mobile: false

# Mouse click effects: Heart symbol (鼠標點擊效果: 愛心)
click_heart:
  enable: false
  mobile: false

# Mouse click effects: words (鼠標點擊效果: 文字)
ClickShowText:
  enable: false
  text:
    # - I
    # - LOVE
    # - YOU
  fontSize: 15px
  random: false
  mobile: false

# Default display mode (網站默認的顯示模式)
# light (default) / dark
display_mode: light

# Beautify (美化頁面顯示)
beautify:
  enable: false
  field: post # site/post
  title-prefix-icon: # '\f0c1'
  title-prefix-icon-color: # '#F47466'

# Global font settings
# Don't modify the following settings unless you know how they work (非必要不要修改)
font:
  global-font-size:
  code-font-size:
  font-family:
  code-font-family:

# Font settings for the site title and site subtitle
# 左上角網站名字 主頁居中網站名字
blog_title_font:
  font_link:
  font-family:

# The setting of divider icon (水平分隔線圖標設置)
hr_icon:
  enable: true
  icon: # the unicode value of Font Awesome icon, such as '\3423'
  icon-top:

# the subtitle on homepage (主頁subtitle)
subtitle:
  enable: false
  # Typewriter Effect (打字效果)
  effect: true
  # loop (循環打字)
  loop: true
  # source 調用第三方服務
  # source: false 關閉調用
  # source: 1  調用一言網的一句話(簡體) https://hitokoto.cn/
  # source: 2  調用一句網(簡體) http://yijuzhan.com/
  # source: 3  調用今日詩詞(簡體) https://www.jinrishici.com/
  # subtitle 會先顯示 source , 再顯示 sub 的內容
  source: false
  # 如果關閉打字效果,subtitle 只會顯示 sub 的第一行文字
  sub:

# Loading Animation (加載動畫)
preloader: false

# aside (側邊欄)
# --------------------------------------

aside:
  enable: true
  hide: false
  button: true
  mobile: true # display on mobile
  position: right # left or right
  card_author:
    enable: true
    description:
    button:
      enable: true
      icon: fab fa-github
      text: Follow Me
      link: https://github.com/xxxxxx
  card_announcement:
    enable: true
    content: This is my Blog
  card_recent_post:
    enable: true
    limit: 5 # if set 0 will show all
    sort: date # date or updated
    sort_order: # Don't modify the setting unless you know how it works
  card_categories:
    enable: true
    limit: 8 # if set 0 will show all
    expand: none # none/true/false
    sort_order: # Don't modify the setting unless you know how it works
  card_tags:
    enable: true
    limit: 40 # if set 0 will show all
    color: false
    sort_order: # Don't modify the setting unless you know how it works
  card_archives:
    enable: true
    type: monthly # yearly or monthly
    format: MMMM YYYY # eg: YYYY年MM月
    order: -1 # Sort of order. 1, asc for ascending; -1, desc for descending
    limit: 8 # if set 0 will show all
    sort_order: # Don't modify the setting unless you know how it works
  card_webinfo:
    enable: true
    post_count: true
    last_push_date: true
    sort_order: # Don't modify the setting unless you know how it works

# busuanzi count for PV / UV in site
# 訪問人數
busuanzi:
  site_uv: true
  site_pv: true
  page_pv: true

# Time difference between publish date and now (網頁運行時間)
# Formal: Month/Day/Year Time or Year/Month/Day Time
runtimeshow:
  enable: false
  publish_date:

# Aside widget - Newest Comments
newest_comments:
  enable: false
  sort_order: # Don't modify the setting unless you know how it works
  limit: 6
  storage: 10 # unit: mins, save data to localStorage
  avatar: true

# Bottom right button (右下角按鈕)
# --------------------------------------

# Conversion between Traditional and Simplified Chinese (簡繁轉換)
translate:
  enable: false
  # The text of a button
  default: 繁
  # the language of website (1 - Traditional Chinese/ 2 - Simplified Chinese)
  defaultEncoding: 2
  # Time delay
  translateDelay: 0
  # The text of the button when the language is Simplified Chinese
  msgToTraditionalChinese: '繁'
  # The text of the button when the language is Traditional Chinese
  msgToSimplifiedChinese: '簡'

# Read Mode (閲讀模式)
readmode: true

# dark mode
darkmode:
  enable: true
  # Toggle Button to switch dark/light mode
  button: true
  # Switch dark/light mode automatically (自動切換 dark mode和 light mode)
  # autoChangeMode: 1  Following System Settings, if the system doesn't support dark mode, it will switch dark mode between 6 pm to 6 am
  # autoChangeMode: 2  Switch dark mode between 6 pm to 6 am
  # autoChangeMode: false
  autoChangeMode: false

# Don't modify the following settings unless you know how they work (非必要請不要修改 )
# Choose: readmode,translate,darkmode,hideAside,toc,chat,comment
# Don't repeat 不要重複
rightside_item_order:
  enable: false
  hide: # readmode,translate,darkmode,hideAside
  show: # toc,chat,comment

# Lightbox (圖片大圖查看模式)
# --------------------------------------
# You can only choose one, or neither (只能選擇一個 或者 兩個都不選)

# medium-zoom
# https://github.com/francoischalifour/medium-zoom
medium_zoom: false

# fancybox
# http://fancyapps.com/fancybox/3/
fancybox: true

# Tag Plugins settings (標籤外掛)
# --------------------------------------

# mermaid
# see https://github.com/mermaid-js/mermaid
mermaid:
  enable: false
  # built-in themes: default/forest/dark/neutral
  theme:
    light: default
    dark: dark

# Note (Bootstrap Callout)
note:
  # Note tag style values:
  #  - simple    bs-callout old alert style. Default.
  #  - modern    bs-callout new (v2-v3) alert style.
  #  - flat      flat callout style with background, like on Mozilla or StackOverflow.
  #  - disabled  disable all CSS styles import of note tag.
  style: flat
  icons: true
  border_radius: 3
  # Offset lighter of background in % for modern and flat styles (modern: -12 | 12; flat: -18 | 6).
  # Offset also applied to label tag variables. This option can work with disabled note tag.
  light_bg_offset: 0

# other
# --------------------------------------

# Pjax
# It may contain bugs and unstable, give feedback when you find the bugs.
# https://github.com/MoOx/pjax
pjax:
  enable: false
  exclude:
    # - xxxx
    # - xxxx

# Inject the css and script (aplayer/meting)
aplayerInject:
  enable: false
  per_page: true

# Snackbar (Toast Notification 彈窗)
# https://github.com/polonel/SnackBar
# position 彈窗位置
# 可選 top-left / top-center / top-right / bottom-left / bottom-center / bottom-right
snackbar:
  enable: false
  position: bottom-left
  bg_light: '#49b1f5' # The background color of Toast Notification in light mode
  bg_dark: '#1f1f1f' # The background color of Toast Notification in dark mode

# https://instant.page/
# prefetch (預加載)
instantpage: false

# https://github.com/vinta/pangu.js
# Insert a space between Chinese character and English character (中英文之間添加空格)
pangu:
  enable: false
  field: site # site/post

# Lazyload (圖片懶加載)
# https://github.com/verlok/vanilla-lazyload
lazyload:
  enable: false
  field: site # site/post
  placeholder:
  blur: false

# PWA
# See https://github.com/JLHwung/hexo-offline
# ---------------
# pwa:
#   enable: false
#   manifest: /pwa/manifest.json
#   apple_touch_icon: /pwa/apple-touch-icon.png
#   favicon_32_32: /pwa/32.png
#   favicon_16_16: /pwa/16.png
#   mask_icon: /pwa/safari-pinned-tab.svg

# Open graph meta tags
# https://developers.facebook.com/docs/sharing/webmasters/
Open_Graph_meta: true

# Add the vendor prefixes to ensure compatibility
css_prefix: true

# Inject
# Insert the code to head (before '' tag) and the bottom (before '' tag)
# 插入代码到头部  之前 和 底部  之前
inject:
  head:
    # - 
  bottom:
    # - <script src="xxxx"></script>

# CDN
# Don't modify the following settings unless you know how they work
# 非必要請不要修改
CDN:
  # main
  main_css:
  main:
  utils:

  # pjax
  pjax:

  # comments
  gitalk:
  gitalk_css:
  blueimp_md5:
  valine:
  disqusjs:
  disqusjs_css:
  utterances:
  twikoo:
  waline:
  giscus:

  # share
  addtoany:
  sharejs:
  sharejs_css:

  # search
  local_search:
  algolia_js:
  algolia_search_v4:
  instantsearch_v4:

  # math
  mathjax:
  katex:
  katex_copytex:
  katex_copytex_css:
  mermaid:

  # count
  busuanzi:

  # background effect
  canvas_ribbon:
  canvas_fluttering_ribbon:
  canvas_nest:

  lazyload:
  instantpage:
  typed:
  pangu:

  # photo
  fancybox_css_v4:
  fancybox_v4:
  medium_zoom:

  # snackbar
  snackbar_css:
  snackbar:

  # effect
  activate_power_mode:
  fireworks:
  click_heart:
  ClickShowText:

  # fontawesome
  fontawesomeV6:

  # Conversion between Traditional and Simplified Chinese
  translate:

  # flickr-justified-gallery
  flickr_justified_gallery_js:
  flickr_justified_gallery_css:

  # aplayer
  aplayer_css:
  aplayer_js:
  meting_js:

  # Prism.js
  prismjs_js:
  prismjs_lineNumber_js:
  prismjs_autoloader:

配置中文语言

  修改_config.yml文件中language值为 zh_CN  

配置菜单项

需要在_config.butterfly.yml文件中修改menu内容,默认是被注释掉了,配置如下

上面菜单根据自己需要修改,英文可以直接修改成中文,后面的url不能修改,否则会匹配不到路由,展示不出菜单,|| 符号后面的是图标,如果需要修改可以到https://fa5.dashgame.com/#/网站中查询自己需要的图标。

如下就配置了一些菜单,项目启动时就能在右上角看到这些菜单

创建标签页、分类页  (该主题默认未创建标签页和分类页,如果不创建,并且开启该菜单会报404错误)

3. 创建标签页

$ hexo new page tags

执行上面命令会在 source 目录中创建 tags/index.md 文件,需要修改文件的部分内容

如上,1 需要修改title,标签页面的标题 ,2 新增值项 type : "tags" (固定写法,用于butterfly主题对路由匹配及博客tags信息的生成)

4. 创建分类页

$ hexo new page categories

执行后,修改 categories/index.md 文件内容为

  • 和标签页一样重要的type属性一定要配置categories

本文为了内容简单且易入门,没有对音乐、视频、友情链接等页面创建做出描述,可到相关网站查找创建方法

  • ??用户信息修改 butterfly官方#网站信息配置
  • ??友情链接创建 butterfly官方#友情链接页面创建
  • ??音乐页面创建 butterfly官方#音乐页面创建
  • ??视频页面创建 butterfly#视频页面创建

博客内容写作

上面对博客项目进行了简单配置,详细配置需要参照官方文档来配置。配置完主题后,以后就只需要对文章内容进行修改或添加新文章,无需改任何配置。

Hexo的文章内容编写都需要在本地Nodejs的项目中进行,内容一般由 标准的markdown语法 和 butterfly标签外挂来构建。

首先了解下futterfly标签外挂的作用及使用,然后编写一篇完整的博文。

标签外挂

除了标准的mackdown语法以外,butterfly还提供了额外的一些组件标签(非标准mackdown语法,butterfly以外的地方使用可能不生效且会报错)

  查看官方介绍  

Front-matter

markdown允许在页面中定义一些额外属性(相当于变量),Hexo框架会读取每个md文件的front-matter传给butterfly主题框架使用。每个页面或文章的一些相关信息设置需要用到。如文章分类,创建时间以及文章顶部标题都需要在 front-matter中配置。

front-matter 定义在md文件顶部,由 --- 符号包括。

page matter

hexo new page [页面名称] 命令创建的页面中的front-matter都属于 page matter,

---
title:
date:
updated:
type:
comments:
description:
keywords:
top_img:
mathjax:
katex:
aside:
aplayer:
highlight_shrink:
---

如下为每个字段的解释,在开发时也不是所有字段都能用到,根据需要书写

post matter

使用 hexo new post [文章名称] 创建的md文件

---
title:
date:
updated:
tags:
categories:
keywords:
description:
top_img:
comments:
cover:
toc:
toc_number:
toc_style_simple:
copyright:
copyright_author:
copyright_author_href:
copyright_url:
copyright_info:
mathjax:
katex:
aplayer:
highlight_shrink:
aside:
---
  • 上方为文章页面可以定义的一些相关信息,如 文章分类、文章标签、创建时间、文章作者、文章封面图片等信息。

创建新文章

执行如下命令创建一个新的文章

$ hexo new post [文章名称] 

完整的文章页面文件

查看代码

---

title: java8函数式编程
date: 2022-02-19 14:17:52
cover: /res/java8函数式编程/cover.jpg
tags: [java,java8,函数式编程]
categories:
    - java
---
**什么是函数式编程?**

百度百科: 函数式编程,是一种编程范式,它将电脑运算视为函数运算,并且避免使用程序状态以及易变对象。其中,λ演算为该语言最重要的基础。而且,λ演算的函数可以接受函数作为输入参数和输出返回值。

讲人话: 函数式编程一种编程范式,允许使用一种表达式(lambda表达式)来表示一个函数

# 函数式接口

> Java语言中函数式编程,通过Java8版本提供的`函数式接口规范`来实现,它指的是有且只有一个未实现的方法的接口,一般通过`FunctionalInterface`这个注解来表明某个接口是一个函数式接口(非必须,如果标注了该注解,编译器检查接口是否符合函数式接口规范,如出现两个及以上数量的普通方法,编译器会报错)。函数式接口是Java支持函数式编程的基础。java8及以上版本才支持。



## 入门

使用Consumer作为示例,它是一个函数式接口,包含一个抽象方法accept,这个方法只有输入而无输出。
现在我们要定义一个Consumer对象,传统的方式是这样定义的:

```java
Consumer c = new Consumer() {
    @Override
    public void accept(Object o) {
        System.out.println(o);
    }
};
```

而在Java8中,针对函数式编程接口,可以这样定义:

```java
Consumer c = (o) -> {
    System.out.println(o);
};  
```



上面已说明,函数式编程接口都只有一个抽象方法,因此在采用这种写法时,编译器会将这段函数编译后当作该抽象方法的实现。
如果接口有多个抽象方法,编译器就不知道这段函数应该是实现哪个方法的了。
因此,=后面的函数体我们就可以看成是accept函数的实现。

- 输入:->前面的部分,即被()包围的部分。此处只有一个输入参数,实际上输入是可以有多个的,如两个参数时写法:(a, b);当然也可以没有输入,此时直接就可以是()
- 函数体:->后面的部分,即被{}包围的部分;可以是一段代码。
- 输出:函数式编程可以没有返回值,也可以有返回值。如果有返回值时,需要代码段的最后一句通过return的方式返回对应的值

当函数体中只有一个语句时,可以去掉{}进一步简化:

```java
Consumer c = (o) -> System.out.println(o);
```

然而这还不是最简的,由于此处只是进行打印,调用了System.out中的println静态方法对输入参数直接进行打印,因此可以简化成以下写法:

```java
Consumer c = System.out::println;
```

它表示的意思就是针对输入的参数将其调用System.out中的静态方法println进行打印。
到这一步就可以感受到函数式编程的强大能力。
通过最后一段代码,我们可以简单的理解函数式编程,Consumer接口直接就可以当成一个函数了,这个函数接收一个输入参数,然后针对这个输入进行处理;当然其本质上仍旧是一个对象,但我们已经省去了诸如老方式中的对象定义过程,直接使用一段代码来给函数式接口对象赋值。
而且最为关键的是,这个函数式对象因为本质上仍旧是一个对象,因此可以做为其它方法的参数或者返回值,可以与原有的代码实现无缝集成!

## Java预设函数式接口

> 下面对Java中的几个预先定义的函数式接口及其经常使用的类进行分析学习。在`java.util.function`包下提供了很多函数式接口,这里挑几个常用的进行讲解。

### Consumer

Consumer是一个函数式编程接口; 顾名思义,Consumer的意思就是消费,即针对某个东西我们来使用它,因此它包含有一个有输入而无输出的accept接口方法;
除accept方法,它还包含有andThen这个方法;
其定义如下:

```java
default Consumer andThen(Consumer<? super T> after) {
    Objects.requireNonNull(after);
    return (T t) -> { accept(t); after.accept(t); };
}
```

可见这个方法就是指定在调用当前Consumer后是否还要调用其它的Consumer;
使用示例:

```java
public static void consumerTest() {
    Consumer f = System.out::println;
    Consumer f2 = n -> System.out.println(n + "-F2");

    //执行完F后再执行F2的Accept方法
    f.andThen(f2).accept("test");

    //连续执行F的Accept方法
    f.andThen(f).andThen(f).andThen(f).accept("test1");
}
```



### Function

Function也是一个函数式编程接口;它代表的含义是“函数”,而函数经常是有输入输出的,因此它含有一个apply方法,包含一个输入与一个输出;
除apply方法外,它还有compose与andThen及indentity三个方法,其使用见下述示例;

```java
/**
 * Function测试
 */
public static void functionTest() {
    Function f = s -> s++;
    Function g = s -> s * 2;

    /**
     * 下面表示在执行F时,先执行G,并且执行F时使用G的输出当作输入。
     * 相当于以下代码:
     * Integer a = g.apply(1);
     * System.out.println(f.apply(a));
     */
    System.out.println(f.compose(g).apply(1));

    /**
     * 表示执行F的Apply后使用其返回的值当作输入再执行G的Apply;
     * 相当于以下代码
     * Integer a = f.apply(1);
     * System.out.println(g.apply(a));
     */
    System.out.println(f.andThen(g).apply(1));

    /**
     * identity方法会返回一个不进行任何处理的Function,即输出与输入值相等; 
     */
    System.out.println(Function.identity().apply("a"));
}
```



### Predicate

Predicate为函数式接口,predicate的中文意思是“断定”,即判断的意思,判断某个东西是否满足某种条件; 因此它包含test方法,根据输入值来做逻辑判断,其结果为True或者False。
它的使用方法示例如下:

```
/**
 * Predicate测试
 */
private static void predicateTest() {
    Predicate p = o -> o.equals("test");
    Predicate g = o -> o.startsWith("t");

    /**
     * negate: 用于对原来的Predicate做取反处理;
     * 如当调用p.test("test")为True时,调用p.negate().test("test")就会是False;
     */
    Assert.assertFalse(p.negate().test("test"));

    /**
     * and: 针对同一输入值,多个Predicate均返回True时返回True,否则返回False;
     */
    Assert.assertTrue(p.and(g).test("test"));

    /**
     * or: 针对同一输入值,多个Predicate只要有一个返回True则返回True,否则返回False
     */
    Assert.assertTrue(p.or(g).test("ta"));
}
```

# Stream

> Stream可以对多个元素进行一系列的操作,也可以支持对某些操作进行并发处理。Java8中对集合、数组进行了stream的实现,方便开发人员通过stream对集合或数组进行数据操作。



##  Stream对象的创建

Stream对象的创建途径有以下几种

a. 创建空的Stream对象

```java
Stream stream = Stream.empty();
```

b. 通过集合类中的stream或者parallelStream方法创建;

```java
Stream stream = Arrays.stream(new String[]{"1", "2", "3"});
List list = Arrays.asList("a", "b", "c", "d");
Stream listStream = list.stream();                   //获取串行的Stream对象
Stream parallelListStream = list.parallelStream();   //获取并行的Stream对象  
```

c. 通过Stream中的of方法创建:

```
Stream s = Stream.of("test");
Stream s1 = Stream.of("a", "b", "c", "d");
```

d. 通过Stream中的iterate方法创建:
iterate方法有两个不同参数的方法:

```java
public static Stream iterate(final T seed, final UnaryOperator f);  
public static Stream iterate(T seed, Predicate<? super T> hasNext, UnaryOperator next)
```

其中第一个方法将会返回一个无限有序值的Stream对象:它的第一个元素是seed,第二个元素是f.apply(seed); 第N个元素是f.apply(n-1个元素的值);生成无限值的方法实际上与Stream的中间方法类似,在遇到中止方法前一般是不真正的执行的。因此无限值的这个方法一般与limit等方法一起使用,来获取前多少个元素。
当然获取前多少个元素也可以使用第二个方法。
第二个方法与第一个方法生成元素的方式类似,不同的是它返回的是一个有限值的Stream;中止条件是由hasNext来断定的。

第二种方法的使用示例如下:

```
/**
 * 本示例表示从1开始组装一个序列,第一个是1,第二个是1+1即2,第三个是2+1即3..,直接10时中止;
 * 也可简化成以下形式:
 *        Stream.iterate(1,
 *        n -> n <= 10,
 *        n -> n+1).forEach(System.out::println);
 * 写成以下方式是为简化理解
 */
Stream.iterate(1,
        new Predicate() {
            @Override
            public boolean test(Integer integer) {
                return integer <= 10;
            }
        },
    new UnaryOperator() {
        @Override
        public Integer apply(Integer integer) {
            return integer+1;
        }
}).forEach(System.out::println);
```

e. 通过Stream中的generate方法创建
与iterate中创建无限元素的Stream类似,不过它的每个元素与前一元素无关,且生成的是一个无序的队列。也就是说每一个元素都可以随机生成。因此一般用来创建常量的Stream以及随机的Stream等。
示例如下:

```
/**
 * 随机生成10个Double元素的Stream并将其打印
 */
Stream.generate(new Supplier() {
    @Override
    public Double get() {
        return Math.random();
    }
}).limit(10).forEach(System.out::println);

//上述写法可以简化成以下写法:
Stream.generate(() -> Math.random()).limit(10).forEach(System.out::println);
```

## Stream对象的使用

Stream对象提供多个非常有用的方法,这些方法可以分成两类:
中间操作:将原始的Stream转换成另外一个Stream;如filter返回的是过滤后的Stream。
终端操作:产生的是一个结果或者其它的复合操作;如count或者forEach操作。

下面就几个比较常用的方法举例说明其用法:

### filter

用于对Stream中的元素进行过滤,返回一个过滤后的Stream
其方法定义如下



```java
Stream filter(Predicate<? super T> predicate);
```

使用示例:

```
Stream s = Stream.of("test", "t1", "t2", "teeeee", "aaaa");
//查找所有包含t的元素并进行打印
s.filter(n -> n.contains("t")).forEach(System.out::println);
```

###  map

元素一对一转换。
它接收一个Funcation参数,用其对Stream中的所有元素进行处理,返回的Stream对象中的元素为Function对原元素处理后的结果
其方法定义如下:

```
 Stream map(Function<? super T, ? extends R> mapper);
```

示例,假设我们要将一个String类型的Stream对象中的每个元素添加相同的后缀.txt,如a变成a.txt,其写法如下:

```
Stream s = Stream.of("test", "t1", "t2", "teeeee", "aaaa");
s.map(n -> n.concat(".txt")).forEach(System.out::println);
```

### flatMap

元素一对多转换:对原Stream中的所有元素使用传入的Function进行处理,每个元素经过处理后生成一个多个元素的Stream对象,然后将返回的所有Stream对象中的所有元素组合成一个统一的Stream并返回;
方法定义如下:

```
 Stream flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
```

示例,假设要对一个String类型的Stream进行处理,将每一个元素的拆分成单个字母,并打印:

```java
Stream s = Stream.of("test", "t1", "t2", "teeeee", "aaaa");
s.flatMap(n -> Stream.of(n.split(""))).forEach(System.out::println);
```

打包发布

hexo提供了 资源清理,生成静态资源,一键部署功能,建议生成资源前 先清理

清理资源

$ hexo clean
  • 执行该命令后,会删除根目录下生成的public目录

生成静态页

$ hexo generate   简写: hexo g
  • 静态页面生成后默认会在项目目录下的public目录中,因为是静态页面,我们可以手动拿去部署在nginx等静态资源服务器上。

一键发布配置

一键发布需要引入相对应的插件以及在_config.yml中配置一键发布的信息,一键发布执行前并不需熬手动执行生成静态页。

该文以github为例,利用一键发布功能,将静态页发布到github.io中

1. 创建github.io仓库

仓库名称必须以 .github.io 结尾,前缀必须是github的用户名,必须是一个公共仓库,如下,最终生成的访问链接为 https://huahuadelei.github.io/,github pages一个用户只能有一个以用户名为前缀的仓库

2. 安装插件github发布插件

在项目下执行如下命令安装插件

$ npm install hexo-deployer-git --save

3. 配置_config.yml部署相关信息

_config.yml最下方补充相关配置信息。

# Deployment
## Docs: https://hexo.io/docs/one-command-deployment
deploy:
  type: 'git'
  repo: 'git@github.com:huahuadelei/hexo-test.github.io.git'
  branch: 'main'

4. 配置本地sshkey到github中(授权)

一般使用ssh用户目录都会有一对ssh密钥对。如果没有使用 ssh-keygen 命令进行生成

找到用户目录中 ~.ssh/id_rsa.pub 文件,把内容复制到github中

  • 点击Add key即可添加完毕

5. 执行命令部署网站

在项目根目录下,打开命令行执行

$ hexo deploy

执行完毕后,如下命令行展示

此时查看github仓库也是已经有了数据

访问github pages网站查看部署内容,https:// + 刚刚创建的仓库名称,https://huahuadelei.github.io/

首页

内容页

  • 从截图中可以看出 之前配置的菜单已经在左上角显示出来了。
  • 项目中默认的helloword页面也是被展示出来了
  • 页面中的用户头像、用户名称以及每个页面上方的top图片也都是可以定制化,具体请参照butterfly主题官方教程

开发建议

使用Hexo开发博客及维护的一个过程基本上都需要在本地进行,开发流程大致分为: Markdown源内容编写 > Hexo生成静态网页 > 使用Hexo部署插件一键发布到远程。

Hexo最终生成/部署的是一个静态网页,也就是它不需要数据库环境来存储数据,数据都被编译到Html文件中。那么基于NodeJS的Hexo项目就是我们博客的源数据。如果项目丢失远程的网站将无法继续维护,所以建议将Hexo项目放在gitee或github上的私有仓库中(公共仓库也行,主要考虑有一键部署功能的配置信息会暴露),以后对项目的改动都建议提交到远程。

相关资源

  • ??Hexo官网 https://hexo.io/zh-cn/
  • ??Hexo插件 https://hexo.io/plugins/
  • ??Hexo主题 https://hexo.io/themes/
  • ??Butterfly官网 https://butterfly.js.org/
  • ??EasyHexo教程内容相对要全面些,有对不下于17个Hexo主题集成教程 点击查看

建议大家对Butterfly官方文章全部查看一边后再定制化配置自己的博客,官方教程更为详细,可更精细的定制自己的博客页面。

建议桉顺序查看这些文章教程

建议看不懂繁体字的转为简体后再阅读