本篇重點 如何修改 Icarus 主題、新增自訂插件 Icarus 主題串接 Google Analytics 顯示網站 PV 和 UV 使用 forgetfulengineer/google-analytics-data-api-netlify API 顯示網站數據 使用 countUp.js 建立數字的動畫效果 Icarus 主題原生僅支援不蒜子 提供的 PV 計數功能,無法串接 Google Analytics  的瀏覽數據。為了顯示來自 GA 的數據,我建立了 forgetfulengineer/google-analytics-data-api-netlify
1. 架設網站 PV/UV 查詢 API fork forgetfulengineer/google-analytics-data-api-netlify【API】使用 Google Analytics Data API 架設網站流量查詢 API 。
2. 添加自定義插件 Icarus 主題大部分的通用布局文件已被移至 repository hexo-component-inferno官方文件 。
建立 ga_count 的 schema 新增 icarus 設定檔參數的概要
檔案路徑 1 2 3 4 5 6 7 8 themes/ ├── icarus/ │   └── include/ │       └── schema/ │           └── plugin/ │               └── ga_count.json       ← 新增參數概要 │           └── common/ │               └── plugins.json        ← 增加引用路徑 
新增參數概要
/include/schema/plugin/ga_count.json 查看 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 {     "$schema" :  "http://json-schema.org/draft-07/schema#" ,      "$id" :  "/plugin/ga_count.json" ,      "description" :  "Ga count plugin" ,      "type" :  "object" ,      "properties" :  {          "ga_count_api" :  {              "type" :  "string" ,              "description" :  "Google Analytics Data API" ,              "nullable" :  true ,              "examples" :  [ "https://ga-api-demo.netlify.app/.netlify/functions/pageview" ]          }      } ,      "required" :  [ "ga_count_api" ]  } 
增加引用路徑
/include/schema/common/plugins.json 查看 1 2 3 4 5 {   ...   "ga_count" :  "plugin/ga_count.json"    ... } 
主題設定啟用 ga_count 填入 API 路徑 https://{your-site}.netlify.app/.netlify/functions/pageview
_config.icarus.yml 1 2 3 plugins:     ga_count:          ga_count_api:  https://ga-api-demo.netlify.app/.netlify/functions/pageview  
建立 ga_count 元件 新增 /layout/plugin/ga_count.jsx 作為 Hexo 客製化插件,用於前端呼叫 GA API,並顯示瀏覽數。
jsx 查看 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 const  { Component , Fragment  } = require ('inferno' );const  { cacheComponent } = require ('hexo-component-inferno/lib/util/cache' );class  GACount  extends  Component  {    render (         const  { head, apiUrl, countUpJs } = this .props ;         if  (head) return  null ;         let  gaCountJs = `(function() {                              var url = "${apiUrl} ";                             fetch(url, { method: 'get' })                                 .then(response => response.json())                                 .then(json => {                                     var sitePvElement = document.getElementById('ga_value_site_pv');                                     var siteUvElement = document.getElementById('ga_value_site_uv');                                     var pvElement = document.getElementById('ga_value_page_pv');                                     if (sitePvElement) {                                         const sitePv = new countUp.CountUp('ga_value_site_pv', json.pv, { enableScrollSpy: true, scrollSpyOnce: true });                                     }                                     if (siteUvElement) {                                         const siteUv = new countUp.CountUp('ga_value_site_uv', json.uv, { enableScrollSpy: true, scrollSpyOnce: true });                                     }                                     if (pvElement) {                                         const pagePv = new countUp.CountUp('ga_value_page_pv', json.pageViews, { enableScrollSpy: true, scrollSpyOnce: true });                                     }                                 });                         })();` ;        return  <Fragment >                      <script  src ={countUpJs} > </script >                      <script  dangerouslySetInnerHTML ={{  __html:  gaCountJs  }}> </script >                  </Fragment >  ;    } } GACount .Cacheable  = cacheComponent (GACount , 'plugin.ga_count' , props  =>    const  { helper, head, page, plugin } = props;     const  { url_for, cdn } = helper;     const  apiBase = plugin.ga_count_api ;     const  path = url_for (page.path );     const  apiUrl = (page.layout  == 'post' ) ? `${apiBase} ?path=${encodeURIComponent (path)} `  : apiBase;     const  countUpJs = cdn ('countup.js' , '2.9.0' , 'dist/countUp.umd.js' );     return  { head, apiUrl, countUpJs }; }); module .exports  = GACount ;
元件快取機制 cacheComponent:
API 路徑動態處理:?path= 查詢字串,讓後端能計算單篇 PV。
jsx 查看 1 const  apiUrl = (page.layout  == 'post' ) ? `${apiBase} ?path=${encodeURIComponent (path)} `  : apiBase;
使用 countUp.js 呈現數字動畫:cdn() 方法載入 CountUp 套件 ,再透過 enableScrollSpy 與 scrollSpyOnce 讓動畫僅在畫面滾動到數字時觸發一次。
jsx 查看 1 new  countUp.CountUp ('ga_value_page_pv' , json.pageViews , { enableScrollSpy : true , scrollSpyOnce : true  })
plugin 載入處理:layout/common/head.jsxlayout/common/scripts.jsxga_count.jsx 只需載入一次,且必須等頁面元素渲染完成後再執行,避免抓不到 PV 元素,因此加入判斷,當處於 <head> 區塊時跳過渲染:
jsx 查看 資料顯示區塊:getElementById 取得 3 個容器(site PV、site UV、單篇 PV),再用 CountUp 顯示動畫數字。
3. 修改模板:顯示 PV / UV 數字 修改 article.jsx(文章 PV) 在文章內容(layout/common/article.jsx)中新增顯示該篇文章瀏覽數(PV)的區塊。
jsx 查看 1 2 3 4 5 6 7 8 {} {!index && plugins && plugins.ga_count .ga_count_api  ? (     <span  class ="level-item"  id ="ga_container_page_pv" >        <i  class ="far fa-eye" > </i >        <span  id ="ga_value_page_pv" > -</span >      </span >    ) : null ; } 
在頁尾(layout/common/footer.jsx)中新增顯示全站瀏覽及訪客數(PV/UV)的區塊。
取得 ga_count 設定並修改瀏覽數據顯示內容
jsx 查看 1 2 3 4 - showVisitorCounter: plugins && plugins.busuanzi === true, - visitorCounterTitle: _p('plugin.visitor_count', '<span id="busuanzi_value_site_uv">0</span>') + showVisitorCounter: plugins && plugins.ga_count.ga_count_api, + visitorCounterTitle: '<i class="far fa-eye"></i>總瀏覽數:<span id="ga_value_site_pv">-</span>  <i class="fa-solid fa-person-walking"></i>總訪客數:<span id="ga_value_site_uv">-</span>' 
將原本 Busuanzi 使用的容器 ID 改為 GA 專用的容器
jsx 查看 1 2 - {showVisitorCounter ? <span id="busuanzi_container_site_uv" + {showVisitorCounter ? <span id="ga_container_site_pvuv" 
結論 透過修改 Icarus 主題與串接 forgetfulengineer/google-analytics-data-api-netlify
如果在串接過程中遇到問題,或有任何建議,歡迎留言讓我知道~接下來計畫新增「熱門文章排行榜」,敬請期待!
延伸閱讀