阅读视图

发现新文章,点击刷新页面。
🔲 ☆

WordPress自动更新缓存功能的归档页面制作

之前使用的归档页面是秋叶博客分享的 → Wordpress带缓存和特效的归档页面制作,使用的是php的缓存机制,每次发布/删除文章后,归档页面缓存不会自动清除和重建,想整一个包含缓存机制和手动刷新功能的归档页面,针对原来的归档页面优化了一下。

实现方案:将核心功能放在functions.php中,页面模板只负责显示。这种架构使缓存管理和内容生成逻辑集中化,而页面模板只负责显示内容,系统会在文章更新时自动刷新缓存,同时为管理员提供手动刷新选项,确保归档页面始终保持最新状态。更符合WordPress最佳实践,具有更好的性能和可维护性。

查看效果:https://chegva.com/archive/

功能设计

  1. functions.php部分

  • 缓存管理机制

  • 文章更新时自动清除缓存

  • 归档内容生成函数

  • 手动清除缓存的工具

  • 页面模板部分

    • 只负责显示归档内容

    • 包含响应式布局和交互功能

    缓存清除场景说明

    场景旧状态新状态说明
      新文章发布非发布状态 (draftpending 等)publish归档需要添加新文章
    定时发布futurepublish 归档需要添加新文章
      取消发布publish非发布状态 (draftprivate 等)归档需要移除该文章
      移至回收站任何状态trash归档需要移除该文章
      已发布文章更新publishpublish❌ 不触发缓存清除
      草稿更新draftdraft❌ 不触发缓存清除

    工作流程图

    1758014815507834.png

    1758014858393353.png

    定时发布文章的工作流程

    1759207128471671.png

    实现代码

    第一步:在functions.php中添加以下代码

    /**
     * 添加归档页面
     **/
    
    // 注册归档缓存选项
    add_action('init', function() {
        add_option('qiuye_archives', '', '', 'no');
    });
    
    // 核心缓存清除函数
    function clear_archive_cache() {
        delete_option('qiuye_archives');
        set_transient('qiuye_cache_cleared', time(), 60); // 设置60秒瞬态标记,避免在清除操作后立即重建
    
        // 增强调试:记录清除操作
        if (defined('WP_DEBUG') && WP_DEBUG) {
            error_log('清除归档缓存触发 - 文章ID: ' . $post_id);
        }
    }
    
    // 文章状态变更处理 - 只在状态变更时清除缓存
    add_action('save_post', function($post_id, $post, $update) {
        // 跳过自动保存
        if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return;
    
        // 只处理文章类型
        if ($post->post_type !== 'post') return;
    
        // 检查权限
        if (!current_user_can('edit_post', $post_id)) return;
    
        // 获取旧状态
        $old_status = get_post_meta($post_id, '_old_status', true);
    
        // 保存当前状态作为下次的旧状态
        update_post_meta($post_id, '_old_status', $post->post_status);
    
        // 检查定时发布
        $is_scheduled = ($post->post_status === 'future');
    
        // 如果是定时发布的文章,设置定时任务
        if ($is_scheduled) {
            $scheduled_time = strtotime($post->post_date);
            $current_time = current_time('timestamp');
    
            // 只有在未来时间才设置定时任务
            if ($scheduled_time > $current_time) {
                wp_schedule_single_event($scheduled_time, 'publish_future_post', array($post_id));
                error_log("定时发布文章设置: $post_id, 发布时间: " . date('Y-m-d H:i:s', $scheduled_time));
            }
        }
    
        // 状态变更检测
        $status_changed = ($old_status && $old_status !== $post->post_status);
    
        // 需要清除缓存的场景
        $clear_cache = false;
    
        if ($status_changed) {
            // 场景1: 从未发布变为发布状态
            if ($old_status !== 'publish' && $post->post_status === 'publish') {
                $clear_cache = true;
            }
            // 场景2: 从发布状态变为非发布状态
            elseif ($old_status === 'publish' && $post->post_status !== 'publish') {
                $clear_cache = true;
            }
            // 场景3: 从任何状态变为删除状态
            elseif ($post->post_status === 'trash') {
                $clear_cache = true;
            }
        }
    
        // 场景4: 新文章直接发布
        if (!$old_status && $post->post_status === 'publish') {
            $clear_cache = true;
        }
    
        if ($clear_cache) {
            clear_archive_cache();
            error_log("文章状态变更触发缓存清除: " . ($old_status ?: 'new') . " => {$post->post_status} (ID: $post_id)");
        }
    }, 10, 3);
    
    // 定时发布文章处理
    add_action('publish_future_post', function($post_id) {
        $post = get_post($post_id);
        if ($post && $post->post_type === 'post') {
            clear_archive_cache();
            error_log("定时发布文章触发缓存清除: $post_id");
    
            // 发送通知(可选)
            if (defined('WP_DEBUG') && WP_DEBUG) {
                $admin_email = get_option('admin_email');
                wp_mail($admin_email, '定时文章已发布', "文章 ID: $post_id 已按计划发布,归档缓存已更新。");
            }
        }
    });
    
    // 文章移至回收站处理
    add_action('trashed_post', function($post_id) {
        $post = get_post($post_id);
        if ($post && $post->post_type === 'post') {
            clear_archive_cache();
            error_log("文章移至回收站触发缓存清除: $post_id");
        }
    });
    
    // 文章从回收站恢复处理
    add_action('untrashed_post', function($post_id) {
        $post = get_post($post_id);
        if ($post && $post->post_type === 'post') {
            // 如果恢复后是发布状态,清除缓存
            if ($post->post_status === 'publish') {
                clear_archive_cache();
                error_log("文章恢复发布触发缓存清除: $post_id");
            }
        }
    });
    
    // 文章永久删除处理
    //add_action('deleted_post', function($post_id) {
    //    // 删除状态跟踪的元数据
    //    delete_post_meta($post_id, '_old_status');
    //
    //    $post = get_post($post_id);
    //    if ($post && $post->post_type === 'post') {
    //        clear_archive_cache();
    //        error_log("文章永久删除触发缓存清除: $post_id");
    //    }
    //});
    
    // 在文章编辑页添加状态跟踪
    add_action('post_submitbox_start', function() {
        global $post;
        if ($post && $post->post_type === 'post') {
            // 保存当前状态作为旧状态
            if (!get_post_meta($post->ID, '_old_status', true)) {
                update_post_meta($post->ID, '_old_status', $post->post_status);
            }
            echo '<input type="hidden" name="old_status" value="'.esc_attr($post->post_status).'">';
        }
    });
    
    // 在文章保存前捕获旧状态
    add_action('pre_post_update', function($post_id) {
        $old_status = get_post_status($post_id);
        update_post_meta($post_id, '_old_status', $old_status);
    });
    
    // 定时清理过期的状态跟踪元数据
    add_action('wp_scheduled_delete', function() {
        global $wpdb;
    
        // 删除30天前的状态跟踪元数据
        $thirty_days_ago = date('Y-m-d H:i:s', strtotime('-30 days'));
    
        $wpdb->query($wpdb->prepare("
            DELETE FROM $wpdb->postmeta
            WHERE meta_key = '_old_status'
            AND post_id IN (
                SELECT ID FROM $wpdb->posts
                WHERE post_modified < %s
            )
        ", $thirty_days_ago));
    
        error_log("清理过期的状态跟踪元数据完成");
    });
    
    // 归档内容生成函数
    function generate_archive_html() {
    	// 生成内容
    	$output = '<div class="archives"><div style="text-align:right;"><img src="https://cdn.chegva.com/static/sorting-answers.png" style="width: 16px;height: 14px;vertical-align:baseline;" ><a id="al_expand_collapse" href="#"> 全部展开/收缩</a> <span style="font-size:11px">(年份/月份也可点击哦!)</span></div>';
    
    	// 使用更可靠的查询方法
    	$args = array(
    		'post_type' => 'post',
    		'post_status' => 'publish',
    		'posts_per_page' => -1,
    		'ignore_sticky_posts' => 1,
    		'no_found_rows' => true,  // 提高性能
    		'update_post_term_cache' => false,  // 提高性能
    		'suppress_filters' => true  // 确保无插件干扰
    	);
    
    	$the_query = new WP_Query($args);
    
    	$year = 0;
    	$mon = 0;
    	while ( $the_query->have_posts() ) : $the_query->the_post();
    	$year_tmp = get_the_time('Y');
    	$mon_tmp = get_the_time('m');
    
    	if ($mon != $mon_tmp && $mon > 0) $output .= '</ul></li>';
    	if ($year != $year_tmp && $year > 0) $output .= '</ul>';
    
    	if ($year != $year_tmp) {
    		$year = $year_tmp;
    		$output .= '<h3 class="al_year">'. $year .'年<span style="padding-left:35px;">( '. get_num_posts_by_year($year) .' 篇文章 )</span></h3><ul class="al_mon_list">';
    	}
    
    	if ($mon != $mon_tmp) {
    		$mon = $mon_tmp;
    		$output .= '<li><span class="al_mon">'. $mon .' 月</span><ul class="al_post_list">';
    	}
    
    	$output .= '<li class="atitle fix"><span class="ttime">'. get_the_time('d日:  ') .'</span><a class="tttile" href="'. get_permalink() .'">'. get_the_title() .'</a><span class="ttview">·'. the_views('0',' ') .'</span></li>';
    	endwhile;
    
    	wp_reset_postdata();
    	$output .= '</ul></li></ul></div>';
    
            // 添加缓存信息 - 使用本地时间
    	$output .= "<!-- Cache generated: ".get_local_time('Y-m-d H:i:s')." -->";
    
        return $output;
    }
    
    // 扩展的获取本地时间函数(带时间戳参数)
    function get_local_time($format = 'Y-m-d H:i:s', $timestamp = null) {
        // 获取WordPress设置的时区
        $timezone_string = get_option('timezone_string');
    
        if ($timezone_string) {
            $timezone = new DateTimeZone($timezone_string);
        } else {
            $offset = get_option('gmt_offset');
            $timezone = timezone_open(sprintf('%+d', $offset));
        }
    
        // 使用当前时间或指定时间戳
        $timestamp = $timestamp ?: time();
        $datetime = new DateTime('@' . $timestamp);
        $datetime->setTimezone($timezone);
    
        return $datetime->format($format);
    }
    
    // 添加手动清除缓存的工具
    add_action('admin_bar_menu', function($admin_bar) {
        if (current_user_can('manage_options')) {
            $admin_bar->add_menu([
                'id'    => 'clear-archive-cache',
                'title' => '清除归档缓存',
                'href'  => wp_nonce_url(add_query_arg('clear_archive_cache', '1'), 'clear_archive_cache')
            ]);
        }
    }, 100);
    
    // 处理手动清除请求
    add_action('init', function($post_id) {
        if (isset($_GET['clear_archive_cache']) && check_admin_referer('clear_archive_cache')) {
    //        delete_option('qiuye_archives');
            clear_archive_cache();
            wp_redirect(remove_query_arg(['clear_archive_cache', '_wpnonce']));
            exit;
        }
    });

    第二步:创建归档页面模板 (page-archives.php)

    <div id="content">
    <?php
    // 归档显示函数
    function qiuye_display_archives() {
    
            // 检查瞬态是否存在
            $cache_cleared_time = get_transient('qiuye_cache_cleared');
    
            if ($cache_cleared_time) {
                    // 如果缓存被清除过,重新生成
                    $output = generate_archive_html();
                    update_option('qiuye_archives', $output);
    
                    // 添加缓存清除时间信息
                    $output .= "<!-- Cache cleared: ".get_local_time('Y-m-d H:i:s', $cache_cleared_time)." -->";
            } else {
                    // 尝试获取现有缓存
                    $output = get_option('qiuye_archives');
    
                    if (empty($output)) {
                            $output = generate_archive_html();
                            update_option('qiuye_archives', $output);
                    }
            }
    
            echo $output;
    }
    ?>
    
    <?php qiuye_display_archives(); ?>

    使用指南

    1. 安装步骤

    • 将functions.php代码添加到主题的functions.php文件

    • 创建page-archives.php模板文件

    • 在WordPress后台创建新页面并选择"归档页面"模板

  • 缓存管理

    • 自动:发布新文章时自动刷新

    • 手动:管理员点击"清除归档缓存"链接

  • 自定义选项

    • 修改缓存名称:'qiuye_archives'

    • 调整样式:编辑CSS代码段

    • 修改查询参数:调整WP_Query数组

    调试步骤:

    1. 将完整代码复制到主题的 functions.php

    2. 发布一篇测试文章

    3. 查看归档页面源代码中的缓存时间标记

    4. 检查debug.log中的清除记录

    5. 使用管理栏按钮测试手动清除功能

    1、检查缓存状态
    在归档页面查看源代码,搜索 <!-- Cache generated: 查看缓存时间

    image.png

    2、检查错误日志
    在 wp-config.php 中添加:

    define('WP_DEBUG', true);
    define('WP_DEBUG_LOG', true);

    发布文章后检查 /wp-content/debug.log

    $ grep "清除归档缓存" debug.log
    [25-Jun-2025 18:36:05 UTC] 清除归档缓存触发 - 文章ID: 6434
    [25-Jun-2025 18:37:27 UTC] 清除归档缓存触发 - 文章ID: 6435
    [25-Jun-2025 18:37:44 UTC] 清除归档缓存触发 - 文章ID: 6436
    [25-Jun-2025 18:37:45 UTC] 清除归档缓存触发 - 文章ID: 6437
    [25-Jun-2025 18:37:54 UTC] 清除归档缓存触发 - 文章ID: 6437
    [25-Jun-2025 18:38:01 UTC] 清除归档缓存触发 - 文章ID: 6437
    [25-Jun-2025 18:38:01 UTC] 清除归档缓存触发 - 文章ID: 6437
    ......
    [25-Jun-2025 19:07:18 UTC] 清除归档缓存触发 - 文章ID: 6442
    [25-Jun-2025 19:07:19 UTC] 清除归档缓存触发 - 文章ID: 6442
    [25-Jun-2025 19:07:27 UTC] 清除归档缓存触发 - 文章ID: 6443
    [25-Jun-2025 19:07:28 UTC] 清除归档缓存触发 - 文章ID: 6444
    [25-Jun-2025 19:08:34 UTC] 清除归档缓存触发 - 文章ID: 6445
    [25-Jun-2025 19:08:51 UTC] 清除归档缓存触发 - 文章ID: 6442

    3、手动清除测试
    使用添加的管理栏按钮手动清除缓存

    image.png

    4、缓存插件排查
    如果使用了缓存插件(W3TC、WP Super Cache等),尝试:

      • 排除归档页面缓存

      • 禁用插件测试

      5、对象缓存检查
      如果使用Redis/Memcached,确保 delete_option 能正确清除持久化存储


      🔲 ☆

      Chrome 浏览器禁用三方 Cookies,今天已经开始全球 1% 的灰度,这个问题也折腾了我好几天,踩到一些坑

      Chrome 浏览器禁用三方 Cookies,今天已经开始全球 1% 的灰度,这个问题也折腾了我好几天,踩到一些坑。

      1)禁用三方 Cookies 是 Google 搞的 Privacy-Sandbox 项目的子项,最主要的目的是为了保护用户的在线隐私,但从厂商角度来看,这也是 Google 对数字广告业的一次主动出击,未来广告主推送广告的精准度将更大程度依赖 Google 系统或平台提供的能力;对应的,苹果在 iOS 14 也推出了 App Tracking Transparency(ATT)技术,目的也是为了提高用户隐私透明度,看资讯说这项技术让 Facebook 损失了 100 亿美元的广告收入,而苹果自身却没有受到此规则的限制。

      2)禁用三方 Cookies,类似的还有禁用三方 Storage,这意味着未来跨站点的用户隐私信息收集将被强约束,例如 A 网站嵌入了 B 网站的内容,用户在 B 网站中储存的所有信息,包括 Cookies、LocalStorage、Cache 等等一大堆的东西,在 A 网站下默认都不允许使用(因为这些储存是根据域名分区的),这也会带来一些负面问题,假设你的网站(如文档类、流程图工具、代码演示工具等)大量被人内嵌,同时要求登录才能被使用,那意味着用户需要在每个内嵌你网站的地方重新登陆一次才能正常使用。

      3)为了实现跨域数据交互,最通用的做法是在 …

      The post Chrome 浏览器禁用三方 Cookies,今天已经开始全球 1% 的灰度,这个问题也折腾了我好几天,踩到一些坑 first appeared on Lenix Blog.

      🔲 ⭐

      用法介绍JavaScript中获取年份的几种方法

      在JavaScript中,获取当前年份是比较常见的需求之一。在本文中,我们将详细探讨JavaScript中获取年份的几种方法。

      一、获取当前年份

      我们可以使用JavaScript中的Date对象来获取当前的年份:

      var date = new Date();
      var year = date.getFullYear();
      console.log(year);
      

      这段代码将打印出当前年份。

      我们也可以使用字符串的方式获取当前年份:

      var year = new Date().toDateString().split(' ')[3];
      console.log(year);
      

      这段代码将打印出当前年份。

      二、获取特定年份

      如果我们想获取特定年份的信息,我们可以创建一个指定日期的Date对象,然后获取该对象的年份:

      var date = new Date('2021-01-01');
      var year = date.getFullYear();
      console.log(year);
      

      这段代码将打印出2021。

      三、获取用户输入的年份

      如果我们需要获取用户输入的年份,我们可以使用JavaScript的prompt方法:

      var year 

      The post 用法介绍JavaScript中获取年份的几种方法 first appeared on Lenix Blog.

      🔲 ⭐

      html to image 把html转换为图片

      html to image 把html转换为图片
      <div class="login-box" id="loginbox" style="width: 500px;">
      要显示的内容
      </div>
      <script src="https://html2canvas.hertzen.com/dist/html2canvas.js"></script>
      <script>
      $(document).ready(function () {
      setTimeout(function(){
      downloadImage();
      },1000)
      });
      functiondownloadImage(){
      html2canvas(document.querySelector("#loginbox")).then(canvas=> {
      a = document.createElement('a');
      document.body.appendChild(a);
      a.download = "test.png";
      a.href = canvas.toDataURL();
      a.click();
      });
      }
      </script>

      The post html to image 把html转换为图片 first appeared on Lenix Blog.

      🔲 ☆

      使用 Clear-Site-Data 强制清除客户端的浏览器缓存(cookie,存储,缓存)

      Clear-Site-Data

      Clear-Site-Data 响应头,表示清除当前请求网站有关的浏览器数据(cookie,存储,缓存)。它让 Web 开发人员对浏览器本地存储的数据有更多控制能力。

      Header type Response header
      Forbidden header name no

      语法

      Clear-Site-Data 可以接受一个或多个参数,如果想要清除所有类型的数据,可以使用通配符 ("*")

      // 单个参数
      Clear-Site-Data: "cache"
      
      // 多个参数 (用逗号分隔)
      Clear-Site-Data: "cache", "cookies"
      
      // 通配
      Clear-Site-Data: "*"
      

      指令

      "cache"
      表示服务端希望删除本 URL 原始响应的本地缓存数据(即:浏览器缓存,请参阅 HTTP 缓存

      The post 使用 Clear-Site-Data 强制清除客户端的浏览器缓存(cookie,存储,缓存) first appeared on Lenix Blog.

      ❌