網站計數器和下載次數自己做:不裝外掛、有快取也能算、自己人不算

網站計數器和下載次數自己做:不裝外掛、有快取也能算、自己人不算

最後更新:2026/10/15

二十年前的網站首頁都有一個「您是第 003847 位訪客」的計數器。現在的網站不需要那個,但有兩個類似的需求一直都在:文章想顯示「看過幾次」,讓新讀者知道這篇有人看;下載按鈕旁邊想顯示「已下載幾次」,讓講義、型錄、菜單的下載數看得到。Google Analytics 當然都有這些數字,但它只能自己在後台看,不能顯示在網站上給客人看。

自己做一個計數器有兩個常見的坑。一是快取:網站開了頁面快取之後,PHP 不會每次都跑,在 PHP 裡加一就算不到。二是灌水:同一個人重新整理十次算十次,自己編輯文章時也算。這篇的做法避開這兩個坑:由瀏覽器在頁面打開後悄悄送一個小請求回報,所以有快取也算得到;同一個人十分鐘內只算一次;登入的員工不算。

不想自己改?把這篇交給 AI

把這篇的網址(或全文)連同下面這段話貼給 AI:

我的網站是 WordPress。我想照下面這篇文章,在文章顯示瀏覽次數、在下載連結旁顯示下載次數,不裝外掛。
請先問我:我要算文章瀏覽還是連結點擊、要顯示在哪裡、網站有沒有快取外掛。
問清楚之後,再一步一步告訴我:要用哪一段程式碼、貼在哪裡、短代碼怎麼放、怎麼確認數字有在增加。
如果文章的做法不適合我的網站,請直接告訴我原因和替代做法。
文章:(貼上這篇的網址,或整篇內容)

改之前先備份,密碼不要貼給 AI。

完整程式碼

// 第 87 篇:網站計數器、下載次數自己做(不裝外掛、有快取也能算)

// 1. 接收計數的網址:/wp-json/mimi/v1/hit
add_action('rest_api_init', function () {
    register_rest_route('mimi/v1', '/hit', [
        'methods'             => 'POST',
        'permission_callback' => '__return_true',
        'args'                => ['post' => ['type' => 'integer'], 'key' => ['type' => 'string']],
        'callback'            => function ($req) {
            if (current_user_can('edit_posts')) {
                return ['skipped' => 'staff']; // 自己人不算
            }
            $ip   = $_SERVER['REMOTE_ADDR'] ?? '';
            $what = $req['key'] ? 'k:' . sanitize_key($req['key']) : 'p:' . (int) $req['post'];
            $lock = 'mimi_hit_' . md5($ip . $what);
            if (get_transient($lock)) {
                return ['skipped' => 'repeat']; // 同一個人 10 分鐘內只算一次
            }
            set_transient($lock, 1, 10 * MINUTE_IN_SECONDS);
            if ($req['key']) {
                $clicks = get_option('mimi_clicks', []);
                $clicks[sanitize_key($req['key'])] = ($clicks[sanitize_key($req['key'])] ?? 0) + 1;
                update_option('mimi_clicks', $clicks, false);
                return ['count' => $clicks[sanitize_key($req['key'])]];
            }
            $n = (int) get_post_meta((int) $req['post'], '_mimi_views', true) + 1;
            update_post_meta((int) $req['post'], '_mimi_views', $n);
            return ['count' => $n];
        },
    ]);
});

// 2. 前台的小程式:打開文章時記一次;按了有 data-count 的連結也記一次
add_action('wp_footer', function () {
    $post_id = is_singular() ? get_queried_object_id() : 0;
    ?>
    <script>
    (function () {
        var url = <?php echo wp_json_encode(rest_url('mimi/v1/hit')); ?>;
        function hit(data) {
            var body = new Blob([JSON.stringify(data)], {type: 'application/json'});
            if (!navigator.sendBeacon || !navigator.sendBeacon(url, body)) {
                fetch(url, {method: 'POST', body: body, keepalive: true});
            }
        }
        <?php if ($post_id) : ?>hit({post: <?php echo (int) $post_id; ?>});<?php endif; ?>
        document.addEventListener('click', function (e) {
            var a = e.target.closest('[data-count]');
            if (a) { hit({key: a.dataset.count}); }
        });
    })();
    </script>
    <?php
});

// 3. 顯示:[mimi_views] 這篇看過幾次;[mimi_clicks key="menu-pdf"] 某個連結點過幾次
add_shortcode('mimi_views', function () {
    return number_format_i18n((int) get_post_meta(get_the_ID(), '_mimi_views', true));
});
add_shortcode('mimi_clicks', function ($atts) {
    $a = shortcode_atts(['key' => ''], $atts);
    return number_format_i18n((int) (get_option('mimi_clicks', [])[sanitize_key($a['key'])] ?? 0));
});

// 4. 後台文章列表多一欄「瀏覽」
add_filter('manage_posts_columns', function ($cols) {
    $cols['mimi_views'] = '瀏覽';
    return $cols;
});
add_action('manage_posts_custom_column', function ($col, $id) {
    if ($col === 'mimi_views') {
        echo (int) get_post_meta($id, '_mimi_views', true);
    }
}, 10, 2);

程式碼放在 Code Snippets 這類外掛或子主題,放法和備份方式見〈WooCommerce 結帳頁改成台灣習慣〉。

四段各做了什麼

第 1 段開一個接收計數的網址 /wp-json/mimi/v1/hit,瀏覽器用 POST 送「文章編號」或「連結代號」過來,它就加一。三個規則寫在裡面:登入而且能編輯文章的人(管理員、編輯、作者)不算;同一個 IP 對同一篇文章或同一個連結,十分鐘內只算一次(用 WordPress 的暫存資料記住);文章的次數存在文章的附加資料 _mimi_views,連結的次數存在一個選項裡,用代號分開。

第 2 段是前台的小程式,放在每一頁的最下面。打開單篇文章或頁面時,它用瀏覽器的 sendBeacon 送一次「這篇被看了」,這個方法不會拖慢頁面,就算客人馬上關掉分頁也送得出去;舊瀏覽器沒有這個方法就改用一般的請求。另外它監聽整個頁面的點擊,任何加了 data-count="代號" 屬性的連結或按鈕被按下,就送一次那個代號。所以下載連結只要寫成 <a href="menu.pdf" data-count="menu-pdf">下載菜單</a>,點一次就算一次。

因為計數是瀏覽器送的,頁面快取不影響它:快取的頁面照樣含這段小程式,客人的瀏覽器照樣會送。顯示出來的數字則會跟著快取停一陣子,和快取時間一樣長,這在計數器上無所謂。

第 3 段是顯示用的短代碼:[mimi_views] 放在文章裡顯示這篇的次數,[mimi_clicks key="menu-pdf"] 顯示某個代號的點擊數,數字會加千分位。短代碼的寫法在〈WordPress 自己寫短代碼〉。

第 4 段在後台「文章」列表多一欄「瀏覽」,哪篇最多人看,一排就看完。

測試結果

我們在本機測試站建一篇文章,內文放「看過 [mimi_views] 次」和一個 data-count="menu-pdf" 的下載連結:

  • 訪客打開兩次:瀏覽數是 1,第二次在十分鐘內,沒有重複算。
  • 訪客按一下下載連結:點擊數變成 1,頁面顯示「看過 1 次。下載菜單 下載 1 次」。
  • 管理員打開同一篇:數字不變。
  • 後台文章列表多了「瀏覽」欄,那篇顯示 1。

這個數字和 GA 的差別

Google Analytics 算的是「工作階段」和「事件」,會過濾機器人、分裝置、分來源,數字給你自己分析用;這篇的計數器只是一個簡單的加法,給網站上的訪客看。兩個數字不會一樣,也不需要一樣。要追蹤按鈕、電話、表單這些事件給自己分析,看〈GA4 追蹤電話和按鈕點擊〉;要讓客人看到「這篇很多人看」「這份講義很多人下載」,用這篇。

數字很小的時候要不要顯示,自己決定。新網站的文章顯示「看過 3 次」沒有說服力,可以先只在後台看,累積到幾百再放到前台;或者只顯示下載數不顯示瀏覽數。

數字會說話,但要說對的話

「已下載 1,200 次」放在講義旁邊,是一種見證:很多人拿過,你也可以拿。這和客人評價、案例數量一樣,都是讓新客人安心的訊號,怎麼用可以看〈網站有人看卻沒人聯絡?〉。重點是數字要真的,自己做的計數器你知道它怎麼算,也就能放心用。

如果你的網站想把下載、見證、案例這些信任訊號一起規劃,可以先看〈IG 帳號不是你的資產〉想想網站要做到什麼。MIMI 專做客製化網頁設計,客製功能網站可以依你的需求做這類小功能。MIMI 提供新北網頁設計服務,新北的創作者可以用電話、Email 直接和設計師討論;費用可以先看首頁的製作費用參考,歡迎聯絡 MIMI。

WordPress 網站變快,自己能做的事:圖片 WebP、關掉用不到的程式、快取、Gzip、Redis
英文版網站怎麼做:中英文頁面互相對應、hreflang、語言切換,不裝翻譯外掛

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

我的車
願望清單
最近瀏覽過的
類別