📖 本教程更新於 2021 年 03 月 30 日,教程的內容針對最新穩定版而更新(如果你是舊版,教程會有些出入,請留意)
🦋 Butterfly 已經更新到 3.7.1
建議
- 不要把個人需要的文件/圖片放在主題
source
文件夾裏,因為在升級主題的過程中,可能會把文件覆蓋刪除了。
在Hexo根目錄的source
文件夾裏,創建一個文件夾來放置個人文件/圖片。文件夾不能命名為css
、js
和img
引用文件直接為/文件夾名稱/文件名
自定義代碼配色
點擊前往自定義側邊欄
點擊前往添加全局吸底Aplayer教程
點擊前往jQuery 加載
主題已於 3.4.0
去除 jQuery 的引用,但是部分功能仍需要加載 jQuery(Justified Gallery和 fancybox)。
如果你仍需要使用 jQuery,可以調用主題提供的 function,防止 jQuery 多次加載。
btf.isJqueryLoad(fn)
function 會判斷是否加載了 jQuery ,如果沒有,加載 jQuery 後運行 fn。如果有,直接運行 fn。
使用方法
1 2 3 4 5 6 7 8 9 10
| btf.isJqueryLoad(function() { 你的function }}
// 方法 2 function myFn () { 你的function } btf.isJqueryLoad(myFn)
|
Gulp壓縮
Gulp 是一款自動化構建的工具,擁有眾多的插件。而我們只需要使用到幾個插件來壓縮Html/css/js。
安裝Gulp
1
| npm install --global gulp-cli
|
插件安裝
壓縮HTML
可以使用gulp-htmlclean和gulp-html-minifier-terser來壓縮HTML
1 2
| npm install gulp-htmlclean --save-dev npm install --save gulp-html-minifier-terser
|
壓縮CSS
可以使用gulp-clean-css來壓縮CSS
1
| npm install gulp-clean-css --save-dev
|
壓縮JS
由於Butterfly主題中的JS使用到了部分ES6語法,因此不能只使用 gulp-uglify 來壓縮,還需要搭配其它的插件。兩種方法都可以有效的壓縮JS代碼,選一種適合自己的就行。
gulp-terser 是直接壓縮 js 代碼,不會進行轉換
gulp-babel是一個JavaScript轉換編譯器,可以把ES6轉換成ES5
gulp-terser
1
| npm install gulp-terser --save-dev
|
gulp-uglify + gulp-babel
1 2
| npm install --save-dev gulp-uglify npm install --save-dev gulp-babel @babel/core @babel/preset-env
|
壓縮圖片
可以使用gulp-imagemin來壓縮圖片
1
| npm install --save-dev gulp-imagemin
|
創建 gulpfile 文件
在Hexo的根目錄,創建一個gulpfile.js
文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
| const gulp = require('gulp') const cleanCSS = require('gulp-clean-css') const htmlmin = require('gulp-html-minifier-terser') const htmlclean = require('gulp-htmlclean') const imagemin = require('gulp-imagemin')
const uglify = require('gulp-uglify') const babel = require('gulp-babel')
gulp.task('compress', () => gulp.src(['./public/**/*.js', '!./public/**/*.min.js']) .pipe(babel({ presets: ['@babel/preset-env'] })) .pipe(uglify().on('error', function (e) { console.log(e) })) .pipe(gulp.dest('./public')) )
gulp.task('minify-css', () => { return gulp.src('./public/**/*.css') .pipe(cleanCSS()) .pipe(gulp.dest('./public')) })
gulp.task('minify-html', () => { return gulp.src('./public/**/*.html') .pipe(htmlclean()) .pipe(htmlmin({ removeComments: true, collapseWhitespace: true, collapseBooleanAttributes: true, removeEmptyAttributes: true, removeScriptTypeAttributes: true, removeStyleLinkTypeAttributes: true, minifyJS: true, minifyCSS: true, minifyURLs: true })) .pipe(gulp.dest('./public')) })
gulp.task('minify-images', async () => { gulp.src('./public/img/**/*.*') .pipe(imagemin({ optimizationLevel: 5, progressive: true, interlaced: false, multipass: false })) .pipe(gulp.dest('./public/img')) })
gulp.task('default', gulp.parallel( 'compress', 'minify-css', 'minify-html', 'minify-images' ))
|
運行
在hexo g
之後運行gulp
就行。
PWA
這是另一種實現PWA的方法,使用這個方法之前,請先卸載掉其它的PWA插件。
據維基百科介紹
漸進式網絡應用程式(英語:Progressive Web Apps,簡稱:PWA)是一種普通網頁或網站架構起來的網絡應用程式,但它可以以傳統應用程式或原生移動應用程式形式展示給用户。這種應用程式形態視圖將目前最為現代化的瀏覽器提供的功能與行動裝置的體驗優勢相結合。
當你的網站實現了PWA,那就代表了
- 用户可以添加你的博客到電腦╱手機的桌面,以原生應用般的方式瀏覽你的博客
- 用户可以更快速地瀏覽你的博客
- 用户可以離線瀏覽你的博客
Hexo已經有很多插件可以實現PWA,下面是另一種實現方法,需要有Gulp就行。這種方法也可實現彈窗提醒用户刷新網站(當網站有更新時)
此方法是使用 Service Worker。我們使用 Workbox 這個工具生成 sw.js
以快速實現 Service Worker ,並實現頁面的預緩存和頁面更新後的提醒功能。
開啟設置和配置manifest.json
在主題配置文件中
中開啟 pwa 選項
1 2 3 4 5 6 7 8
| pwa: enable: true manifest: /img/pwa/manifest.json theme_color: "#fff" apple_touch_icon: /img/pwa/apple-touch-icon.png favicon_32_32: /img/pwa/32.png favicon_16_16: /img/pwa/16.png mask_icon: /img/pwa/safari-pinned-tab.svg
|
在Hexo的source
目錄中創建manifest.json
文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| { "name": "string", "short_name": "Junzhou", "theme_color": "#49b1f5", "background_color": "#49b1f5", "display": "standalone", "scope": "/", "start_url": "/", "icons": [ { "src": "images/pwaicons/36.png", "sizes": "36x36", "type": "image/png" }, { "src": "images/pwaicons/48.png", "sizes": "48x48", "type": "image/png" }, { "src": "images/pwaicons/72.png", "sizes": "72x72", "type": "image/png" }, { "src": "images/pwaicons/96.png", "sizes": "96x96", "type": "image/png" }, { "src": "images/pwaicons/144.png", "sizes": "144x144", "type": "image/png" }, { "src": "images/pwaicons/192.png", "sizes": "192x192", "type": "image/png" }, { "src": "images/pwaicons/512.png", "sizes": "512x512", "type": "image/png" } ], "splash_pages": null }
|
你也可以通過 Web App Manifest快速創建manifest.json
。(Web App Manifest 要求至少包含一個 512*512 像素的圖標)
安裝插件
在命令行中輸入安裝插件
1
| npm install workbox-build gulp --save-dev
|
創建gulpfile.js
文件
在Hexo的根目錄,創建一個gulpfile.js
文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| const gulp = require("gulp"); const workbox = require("workbox-build");
gulp.task('generate-service-worker', () => { return workbox.injectManifest({ swSrc: './sw-template.js', swDest: './public/sw.js', globDirectory: './public', globPatterns: [ "**/*.{html,css,js,json,woff2}" ], modifyURLPrefix: { "": "./" } }); });
gulp.task("build", gulp.series("generate-service-worker"));
|
創建 sw-template.js
文件
在Hexo的根目錄,創建一個sw-template.js
文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
| const workboxVersion = '5.1.3';
importScripts(`https://storage.googleapis.com/workbox-cdn/releases/${workboxVersion}/workbox-sw.js`);
workbox.core.setCacheNameDetails({ prefix: "your name" });
workbox.core.skipWaiting();
workbox.core.clientsClaim();
workbox.precaching.precacheAndRoute(self.__WB_MANIFEST,{ directoryIndex: null });
workbox.precaching.cleanupOutdatedCaches();
workbox.routing.registerRoute( /\.(?:png|jpg|jpeg|gif|bmp|webp|svg|ico)$/, new workbox.strategies.CacheFirst({ cacheName: "images", plugins: [ new workbox.expiration.ExpirationPlugin({ maxEntries: 1000, maxAgeSeconds: 60 * 60 * 24 * 30 }), new workbox.cacheableResponse.CacheableResponsePlugin({ statuses: [0, 200] }) ] }) );
workbox.routing.registerRoute( /\.(?:eot|ttf|woff|woff2)$/, new workbox.strategies.CacheFirst({ cacheName: "fonts", plugins: [ new workbox.expiration.ExpirationPlugin({ maxEntries: 1000, maxAgeSeconds: 60 * 60 * 24 * 30 }), new workbox.cacheableResponse.CacheableResponsePlugin({ statuses: [0, 200] }) ] }) );
workbox.routing.registerRoute( /^https:\/\/fonts\.googleapis\.com/, new workbox.strategies.StaleWhileRevalidate({ cacheName: "google-fonts-stylesheets" }) ); workbox.routing.registerRoute( /^https:\/\/fonts\.gstatic\.com/, new workbox.strategies.CacheFirst({ cacheName: 'google-fonts-webfonts', plugins: [ new workbox.expiration.ExpirationPlugin({ maxEntries: 1000, maxAgeSeconds: 60 * 60 * 24 * 30 }), new workbox.cacheableResponse.CacheableResponsePlugin({ statuses: [0, 200] }) ] }) );
workbox.routing.registerRoute( /^https:\/\/cdn\.jsdelivr\.net/, new workbox.strategies.CacheFirst({ cacheName: "static-libs", plugins: [ new workbox.expiration.ExpirationPlugin({ maxEntries: 1000, maxAgeSeconds: 60 * 60 * 24 * 30 }), new workbox.cacheableResponse.CacheableResponsePlugin({ statuses: [0, 200] }) ] }) );
workbox.googleAnalytics.initialize();
|
把prefix
修改為你博客的名字(英文),如果你想用其它緩存策略,請自行查看相關文檔
添加js進主題
在主題配置文件中,添加需要的css和js
1 2 3 4 5
| inject: head: - '<style type="text/css">.app-refresh{position:fixed;top:-2.2rem;left:0;right:0;z-index:99999;padding:0 1rem;font-size:15px;height:2.2rem;transition:all .3s ease}.app-refresh-wrap{display:flex;color:#fff;height:100%;align-items:center;justify-content:center}.app-refresh-wrap a{color:#fff;text-decoration:underline;cursor:pointer}</style>' bottom: - '<div class="app-refresh" id="app-refresh"> <div class="app-refresh-wrap"> <label>✨ 網站已更新最新版本 👉</label> <a href="javascript:void(0)" onclick="location.reload()">點擊刷新</a> </div></div><script>function showNotification(){if(GLOBAL_CONFIG.Snackbar){var t="light"===document.documentElement.getAttribute("data-theme")?GLOBAL_CONFIG.Snackbar.bgLight:GLOBAL_CONFIG.Snackbar.bgDark,e=GLOBAL_CONFIG.Snackbar.position;Snackbar.show({text:"已更新最新版本",backgroundColor:t,duration:5e5,pos:e,actionText:"點擊刷新",actionTextColor:"#fff",onActionClick:function(t){location.reload()}})}else{var o=`top: 0; background: ${"light"===document.documentElement.getAttribute("data-theme")?"#49b1f5":"#1f1f1f"};`;document.getElementById("app-refresh").style.cssText=o}}"serviceWorker"in navigator&&(navigator.serviceWorker.controller&&navigator.serviceWorker.addEventListener("controllerchange",function(){showNotification()}),window.addEventListener("load",function(){navigator.serviceWorker.register("/sw.js")}));</script>'
|
上面的代碼是壓縮過的,具體相關代碼如下,可供理解。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| <div class="app-refresh" id="app-refresh"> <div class="app-refresh-wrap"> <label>✨ 網站已更新最新版本 👉</label> <a href="javascript:void(0)" onclick="location.reload()">點擊刷新</a> </div> </div> <script> if ('serviceWorker' in navigator) { if (navigator.serviceWorker.controller) { navigator.serviceWorker.addEventListener('controllerchange', function () { showNotification() }) }
window.addEventListener('load', function () { navigator.serviceWorker.register('/sw.js') }) }
function showNotification() { if (GLOBAL_CONFIG.Snackbar) { var snackbarBg = document.documentElement.getAttribute('data-theme') === 'light' ? GLOBAL_CONFIG.Snackbar.bgLight : GLOBAL_CONFIG.Snackbar.bgDark var snackbarPos = GLOBAL_CONFIG.Snackbar.position Snackbar.show({ text: '已更新最新版本', backgroundColor: snackbarBg, duration: 500000, pos: snackbarPos, actionText: '點擊刷新', actionTextColor: '#fff', onActionClick: function (e) { location.reload() }, }) } else { var showBg = document.documentElement.getAttribute('data-theme') === 'light' ? '#49b1f5' : '#1f1f1f' var cssText = `top: 0; background: ${showBg};` document.getElementById('app-refresh').style.cssText = cssText } } </script>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| <style type="text/css"> .app-refresh { position: fixed; top: -2.2rem; left: 0; right: 0; z-index: 99999; padding: 0 1rem; font-size: 15px; height: 2.2rem; transition: all 0.3s ease; } .app-refresh-wrap { display: flex; color: #fff; height: 100%; align-items: center; justify-content: center; }
.app-refresh-wrap span { color: #fff; text-decoration: underline; cursor: pointer; } </style>
|
運行
在你運行hexo g
後,記得要運行gulp
這樣才會生效
以下是結合了上面提到的Gulp壓縮和PWA的gulpfile.js
,可供參考。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
| const gulp = require('gulp') const cleanCSS = require('gulp-clean-css') const htmlmin = require('gulp-html-minifier-terser') const htmlclean = require('gulp-htmlclean') const imagemin = require('gulp-imagemin') const workbox = require("workbox-build");
const uglify = require('gulp-uglify'); const babel = require('gulp-babel');
gulp.task('generate-service-worker', () => { return workbox.injectManifest({ swSrc: './sw-template.js', swDest: './public/sw.js', globDirectory: './public', globPatterns: [ "**/*.{html,css,js,json,woff2}" ], modifyURLPrefix: { "": "./" } }); });
gulp.task('compress', () => gulp.src(['./public/**/*.js', '!./public/**/*.min.js']) .pipe(babel({ presets: ['@babel/preset-env'] })) .pipe(uglify().on('error', function(e){ console.log(e); })) .pipe(gulp.dest('./public')) );
gulp.task('minify-css', () => { return gulp.src('./public/**/*.css') .pipe(cleanCSS({ compatibility: 'ie11' })) .pipe(gulp.dest('./public')); });
gulp.task('minify-html', () => { return gulp.src('./public/**/*.html') .pipe(htmlclean()) .pipe(htmlmin({ removeComments: true, collapseWhitespace: true, collapseBooleanAttributes: true, removeEmptyAttributes: true, removeScriptTypeAttributes: true, removeStyleLinkTypeAttributes: true, minifyJS: true, minifyCSS: true, minifyURLs: true })) .pipe(gulp.dest('./public')) });
gulp.task('minify-images', async () => { gulp.src('./public/img/**/*.*') .pipe(imagemin({ optimizationLevel: 5, progressive: true, interlaced: false, multipass: false, })) .pipe(gulp.dest('./public/img')); });
gulp.task("default", gulp.series("generate-service-worker", gulp.parallel( 'compress','minify-html', 'minify-css', 'minify-images' )));
|
Icon
Butterfly主題內置了Font Awesome V5 圖標,目前已更新到 5.13.0,總共有1,588個免費圖標。由於是國外的圖標網站,對於國內的一些網站Icon並不支持。如有需要,你可以引入其它的圖標服務商。
iconfont
國內最出名的莫過於iconfont,功能很強大且圖標內容很豐富的矢量圖標庫。很多Font Awesome
不支持的圖標都可以在這裏找到。同時,iconfont支持選擇需要的圖標生成css鏈接,減少不必要的CSS加載。
註冊賬號
打開iconfont的網站,點擊導航欄的人像
圖標,會跳出註冊界面,按要求註冊賬號。
添加圖標入庫
選擇自己需要的圖標,把鼠標移到圖標上,會顯示三個按鈕(依次是添加入庫、收藏和下載),而我們需要的是把圖標添加入庫
添加入庫後,你可以看到網站右上角購物車
圖標顯示了1
字,代表圖標已經添加入庫,點擊購物車
圖標,會彈出側邊欄顯示詳情。
已選擇的圖標會顯示在上面,你可以重複上面的操作,把需要的圖標添加入庫,然後點擊添加到項目
。
接下來會要求選擇項目名稱,沒有的自己創建一個。
生成CSS鏈接
在添加到項目之後,會跳到項目的詳情界面。點擊Font class
,然後再點擊暫無代碼,點擊生成
文字。網站會自動生成CSS鏈接,我們只需要複製鏈接就行。
添加鏈接進主題配置文件
打開主題配置文件
,找到inject
配置,按要求把鏈接填入
在我們需要使用的地方填入icon,例如Menu
,圖片使用格式為iconfont icon名字
運行Butterfly之後,你就可以看到menu的圖標生效了
其他添加方法
除了通過引入CSS鏈接使用圖標,iconfont也支持通過其它方法使用圖標,具體可查看iconfont官方使用文檔
其它圖標提供商
除了iconfont,還有RemixIcon、Flaticon等等提供商,很多圖標可以選擇,具體使用方法請參考各自的文檔。
圖片壓縮
Butterfly主題需要使用到很多圖片。如果圖片太大,會嚴重拖慢網站的加載速度。
圖片壓縮能夠有效的緩解這個問題。
除了通過gulp-imagemin
來壓縮圖片,還可以通過在綫壓縮網站和軟件來進行壓縮。以下兩款是我自己正在使用的工具。網上有很多這樣的工具,挑選一款適合自己的就行。
tinypng
一個在綫壓縮的網站。壓縮後的圖片也保留了很高的質量,在知乎上很多人推薦,不過免費版有限制。
caesium
開源軟件,支持Windows和macOS。可以批量壓縮軟件,無限制。
imgbot
imgbot 是一款 Github 插件。
安裝後,你上傳圖片到 Github 去,imgbot 會自動壓縮圖片並推送 PR,我們只需要合併 PR 就行
你可以配置 imgbot 的偵測方法、壓縮方法(有損/無損),具體可以查看插件的文檔
插件推薦
參考
利用 Workbox 實現博客的 PWA
漸進式網絡應用程式)
✨ Butterfly 安裝文檔(七) 更新日誌轉載來自 https://butterfly.js.org/