Cyan Blog Cyan Blog
首页
  • Java (opens new window)
  • JUC (opens new window)
  • JVM (opens new window)
  • Redis

    • Redis安装 (opens new window)
    • Redis基础 (opens new window)
    • Redis实战 (opens new window)
    • Redis集群安装 (opens new window)
    • Redis分布式缓存 (opens new window)
    • Redis多级缓存 (opens new window)
    • Redis原理 (opens new window)
  • 管理工具

    • Maven (opens new window)
    • Git (opens new window)
  • SSM

    • Spring (opens new window)
    • SpringBoot (opens new window)
    • Mybatis (opens new window)
    • MybatisPlus (opens new window)
  • 微服务

    • Docker (opens new window)
    • RabbitMQ (opens new window)
    • SpringCloud (opens new window)
    • Dubbo (opens new window)
    • MongoDB (opens new window)
    • Zookeeper (opens new window)
  • Java面试题 (opens new window)
  • JUC面试题 (opens new window)
  • JVM面试题 (opens new window)
  • Linux面试题 (opens new window)
  • SQL面试题 (opens new window)
  • Maven面试题 (opens new window)
  • Redis面试题 (opens new window)
  • SSM面试题 (opens new window)
  • SpringCloud面试题 (opens new window)
  • Linux (opens new window)
  • C++ (opens new window)
  • 数据库

    • MySQL (opens new window)
    • NoSQL (opens new window)
  • 软件测试

    • 软件测试 (opens new window)
  • 加密解密 (opens new window)
  • bilibili字幕提取 (opens new window)
  • 道理 (opens new window)
  • 关于博主

    • Github (opens new window)
    • CSDN (opens new window)
  • 关于本站

    • 如何搭建博客网站 (opens new window)
首页
  • Java (opens new window)
  • JUC (opens new window)
  • JVM (opens new window)
  • Redis

    • Redis安装 (opens new window)
    • Redis基础 (opens new window)
    • Redis实战 (opens new window)
    • Redis集群安装 (opens new window)
    • Redis分布式缓存 (opens new window)
    • Redis多级缓存 (opens new window)
    • Redis原理 (opens new window)
  • 管理工具

    • Maven (opens new window)
    • Git (opens new window)
  • SSM

    • Spring (opens new window)
    • SpringBoot (opens new window)
    • Mybatis (opens new window)
    • MybatisPlus (opens new window)
  • 微服务

    • Docker (opens new window)
    • RabbitMQ (opens new window)
    • SpringCloud (opens new window)
    • Dubbo (opens new window)
    • MongoDB (opens new window)
    • Zookeeper (opens new window)
  • Java面试题 (opens new window)
  • JUC面试题 (opens new window)
  • JVM面试题 (opens new window)
  • Linux面试题 (opens new window)
  • SQL面试题 (opens new window)
  • Maven面试题 (opens new window)
  • Redis面试题 (opens new window)
  • SSM面试题 (opens new window)
  • SpringCloud面试题 (opens new window)
  • Linux (opens new window)
  • C++ (opens new window)
  • 数据库

    • MySQL (opens new window)
    • NoSQL (opens new window)
  • 软件测试

    • 软件测试 (opens new window)
  • 加密解密 (opens new window)
  • bilibili字幕提取 (opens new window)
  • 道理 (opens new window)
  • 关于博主

    • Github (opens new window)
    • CSDN (opens new window)
  • 关于本站

    • 如何搭建博客网站 (opens new window)
  • 零成本搭建个人博客网站
  • 从零开始搭建博客
  • 更多配置
  • 域名
  • 图床
  • 同步GitHub和Gitee
  • 自动部署
  • 代码私有
  • 评论区
  • 站点信息
    • 版本一
    • 版本二
    • 1
  • MoreMore
  • 障碍与反思
  • Blog
2025-04-20
0
0
目录

站点信息

两个版本对比:

  • 两个版本主要是busuanzi插件是不一样的,两个busuanzi插件的数据是不一样的,版本二的busuanzi多了个文章访问人数数据统计,版本一是没有的,不过版本二使用的busuanzi数据可能会被重置😫。

  • 版本一部署后同一个页面刷新后会数据不显示,需要像版本二一样打补丁,但是版本二使用的代码是不一样的,可以学习版本二自己打补丁。

  • 版本二如果你做新增访问量功能会有点小bug,页面切换频繁时会数据显示异常,不做也没事。

# 版本一

参考 (opens new window)

跟着CV就行

  • 除了里面的选择readFile.js 或者 readFile.ts 文件

    • 如果报错Error: Dynamic require of "fs" is not supported
    • 要选择readFile.ts
  • 如果你的数据显示异常,要部署到线上才会显示正常

  • 如果你做完评论区没了,可能是你的插件被覆盖了,需要把两个插件写在一起["Twikoo", "PageInfo"](别问我是怎么知道的,这是来自CV工程师的经验)

      [
        {
          name: 'custom-plugins',
          globalUIComponents: ["Twikoo", "PageInfo"] // 2.x 版本 globalUIComponents 改名为 clientAppRootComponentFiles
        }
      ],
    

# 新增访问量(选做)

  • 每次看站点信息时,可以看到访问量增多,但是不知道到底增加了多少,所以我就修改了WebInfo和PageInfo中的代码,增加了显示新增访问量的功能,显示的方式为:在访问量后面显示,与你上次访问时网站的访问量相比增加了多少,

  • 主要是新增了showCounterWithIncrement方法,并在访问量元素加载后,将访问量(+新增访问量)覆盖上去,过一段时间后再显示回访问量

    • 由于代码执行得比网站快,(代码获取访问量后,访问量对应的元素才加载出来),导致我们第一次直接获取访问量可能获取不到(你也可以将第一次尝试显示新增访问量的代码去掉
    setTimeout(() => {
        let indexUv = document.querySelector(".web-site-pv");
        let indexPv = document.querySelector(".web-site-uv");
        if (
          indexPv && 
          indexUv &&
          indexPv.innerText !== "" &&
          indexUv.innerText !== ""
        ) {
          showCounterWithIncrement(indexPv, indexUv);
          return;
        }
    	//...
    }//记得后面的}也要去掉
    

    ,只是会等待3秒后才加载新增的访问量,PageInfo.vue的代码同理),所以我用setTimeout等待了1秒才获取访问量。并把iterationTime从原来的3秒改为2秒,保证加起来与原来等待的时间不变

# WebInfo.vue

 /**
 * 首页的统计量
 */
getIndexViewCouter(iterationTime = 1500) {
  fetch();
  var i = 0;
  var defaultCouter = "9999";
  // 提取显示访问量和新增量的公共方法
  const showCounterWithIncrement = (pvElement, uvElement) => {
    // 读取 localStorage 中的上次访问量
    const lastPv = localStorage.getItem("lastSitePv") || 0;
    const lastUv = localStorage.getItem("lastSiteUv") || 0;

    // 当前访问量
    const currentPv = parseInt(pvElement.innerText) || 0;
    const currentUv = parseInt(uvElement.innerText) || 0;

    // 计算新增访问量
    const newPv = Math.max(currentPv - parseInt(lastPv), 0);
    const newUv = Math.max(currentUv - parseInt(lastUv), 0);

    if(newPv != 0){
      // 显示新增的访问量
      pvElement.innerText = `${currentPv}(+${newPv})`;
      // 保存当前访问量到 localStorage
      localStorage.setItem("lastSitePv", currentPv);
    }
    if(newUv != 0){
      // 显示新增的访问量
      uvElement.innerText = `${currentUv}(+${newUv})`;
      // 保存当前访问量到 localStorage
      localStorage.setItem("lastSiteUv", currentUv);
    }

    // 设置定时器隐藏新增访问量
    setTimeout(() => {
      pvElement.innerText = currentPv; // 仅显示总访问量
      uvElement.innerText = currentUv; // 仅显示总访问量
    }, 5000); // 5秒后隐藏新增访问量
  };
  setTimeout(() => {
    let indexUv = document.querySelector(".web-site-pv");
    let indexPv = document.querySelector(".web-site-uv");
    if (
      indexPv && 
      indexUv &&
      indexPv.innerText !== "" &&
      indexUv.innerText !== ""
    ) {
      showCounterWithIncrement(indexPv, indexUv);
      return;
    }
    // 如果只需要第一次获取数据(可能获取失败),可注释掉 setTimeout 内容,此内容是第一次获取失败后,重新获取访问量
    // 可能会导致访问量再次 + 1 原因:取决于 setTimeout 的时间(需求调节),setTimeout 太快导致第一个获取的数据没返回,就第二次获取,导致结果返回 + 2 的数据
    setTimeout(() => {
      let indexUv = document.querySelector(".web-site-pv");
      let indexPv = document.querySelector(".web-site-uv");
      if (
        indexPv && 
        indexUv &&
        indexPv.innerText == "" &&
        indexUv.innerText == ""
      ) {
        let interval = setInterval(() => {
          // 再次判断原因:防止进入 setInterval 的瞬间,访问量获取成功
          if (
            indexPv && 
            indexUv &&
            indexPv.innerText == "" &&
            indexUv.innerText == ""
          ) {
            i += iterationTime;
            if (i > iterationTime * 5) {
              indexPv.innerText = defaultCouter;
              indexUv.innerText = defaultCouter;
              clearInterval(interval); // 5 次后无法获取,则取消获取
            }
            if (indexPv.innerText == "" && indexUv.innerText == "") {
              // 手动获取访问量
              fetch();
            } else {
              clearInterval(interval);
              showCounterWithIncrement(indexPv, indexUv);
            }
          } else {
            clearInterval(interval);
            showCounterWithIncrement(indexPv, indexUv);
          }
        }, iterationTime);
        // 绑定 beforeDestroy 生命钩子,清除定时器
        this.$once("hook:beforeDestroy", () => {
          clearInterval(interval);
          interval = null;
        });
      }else if(indexPv && indexUv){
        showCounterWithIncrement(indexPv, indexUv);
      }
    }, iterationTime);
  },1500)
},

# PageInfo.vue

/**
 * 文章页的访问量
 */
getPageViewCouter(iterationTime = 1500) {
  fetch();
  let i = 0;
  var defaultCouter = "9999";
  // 提取显示访问量和新增量的公共方法
  const showPageViewWithIncrement = (element) => {
    // 获取当前文章的唯一标识符
    const articleKey = this.$frontmatter.permalink || window.location.pathname;
    // 读取上次访问量
    const lastPageView = parseInt(localStorage.getItem(`lastPagePv_${articleKey}`)) || 0;
    // 当前访问量
    const currentPageView = parseInt(element.innerText) || 0;

    // 计算新增访问量(确保不为负数)
    const newPageView = Math.max(currentPageView - lastPageView, 0);

    if(newPageView != 0){
      // 显示新增访问量
      element.innerText = `${currentPageView}(+${newPageView})`;
      // 保存当前访问量
      localStorage.setItem(`lastPagePv_${articleKey}`, currentPageView);
      // 设置定时器隐藏新增访问量
      setTimeout(() => {
        element.innerText = currentPageView; // 仅显示总访问量
      }, 5000); // 5秒后隐藏新增访问量
    }

  };
  setTimeout(() => {
    let pageView = document.querySelector(".view-data");
    if (pageView && pageView.innerText !== "") {
      showPageViewWithIncrement(pageView);
      return;
    }

    // 如果只需要第一次获取数据(可能获取失败),可注释掉 setTimeout 内容,此内容是第一次获取失败后,重新获取访问量
    // 可能会导致访问量再次 + 1 原因:取决于 setTimeout 的时间(需求调节),setTimeout 太快导致第一个获取的数据没返回,就第二次获取,导致结果返回 + 2 的数据
    setTimeout(() => {
      let pageView = document.querySelector(".view-data");
      if (pageView && pageView.innerText == "") {
        let interval = setInterval(() => {
          // 再次判断原因:防止进入 setInterval 的瞬间,访问量获取成功
          if (pageView && pageView.innerText == "") {
            i += iterationTime;
            if (i > iterationTime * 5) {
              pageView.innerText = defaultCouter;
              clearInterval(interval); // 5 次后无法获取,则取消获取
            }
            if (pageView.innerText == "") {
              // 手动获取访问量
              fetch();
            } else {
              clearInterval(interval);
              showPageViewWithIncrement(pageView);
            }
          } else {
            clearInterval(interval);
            showPageViewWithIncrement(pageView);
          }
        }, iterationTime);
        // 绑定 beforeDestroy 生命钩子,清除定时器
        this.$once("hook:beforeDestroy", () => {
          clearInterval(interval);
          interval = null;
        });
      }else if(pageView){
        showPageViewWithIncrement(pageView);
      }
    }, iterationTime);
  },1500)

},

# 版本二

参考 (opens new window)

跟着CV就行

  • 如果报错Error: Dynamic require of "fs" is not supported
  • 要到版本一中复制里面的readFile.ts,不用readFile.js

WebInfo.vue

<template>
    <!-- Young Kbt -->
    <div class="web-info card-box">
        <div class="webinfo-title">
            <i class="iconfont icon-award" style="font-size: 0.875rem; font-weight: 900; width: 1.25em"></i>
            <span>站点信息</span>
        </div>
        <div class="webinfo-item">
            <div class="webinfo-item-title">文章数目:</div>
            <div class="webinfo-content">{{ mdFileCount }} 篇</div>
        </div>

        <div class="webinfo-item">
            <div class="webinfo-item-title">已运行时间:</div>
            <div class="webinfo-content">
                {{ createToNowDay != 0 ? createToNowDay + " 天" : "不到一天" }}
            </div>
        </div>

        <div class="webinfo-item">
            <div class="webinfo-item-title">本站总字数:</div>
            <div class="webinfo-content">{{ totalWords }} 字</div>
        </div>

        <div class="webinfo-item">
            <div class="webinfo-item-title">最后活动时间:</div>
            <div class="webinfo-content">
                {{ lastActiveDate == "刚刚" ? "刚刚" : lastActiveDate + "前" }}
            </div>
        </div>

        <div v-if="indexView" class="webinfo-item">
            <div class="webinfo-item-title">本站访问人次:</div>
            <div class="webinfo-content">
                <!-- <span id="busuanzi_value_site_pv" class="web-site-pv"><i title="正在获取..."
                        class="loading iconfont icon-loading"></i>
                </span> -->
                <span id="busuanzi_site_pv" class="web-site-pv"><i title="正在获取..."
                        class="loading iconfont icon-loading"></i>
                </span>
                次
            </div>
        </div>

        <div v-if="indexView" class="webinfo-item">
            <div class="webinfo-item-title">本站访客人数:</div>
            <div class="webinfo-content busuanzi">
                <!-- <span id="busuanzi_value_site_uv" class="web-site-uv"><i title="正在获取..."
                        class="loading iconfont icon-loading"></i>
                </span> -->
               <span id="busuanzi_site_uv" class="web-site-uv"><i title="正在获取..."
                            class="loading iconfont icon-loading"></i>
                 </span>
                人
            </div>
        </div>
    </div>
</template>

<script>
import { dayDiff, timeDiff, lastUpdatePosts } from "../webSiteInfo/utils";
import fetch from "../webSiteInfo/busuanzi"; // 统计量
export default {
    data() {
        return {
            // Young Kbt
            mdFileCount: 0, // markdown 文档总数
            createToNowDay: 0, // 博客创建时间距今多少天
            lastActiveDate: "", // 最后活动时间
            totalWords: 0, // 本站总字数
            indexView: true, // 开启访问量和排名统计
        };
    },
    computed: {
        $lastUpdatePosts() {
            return lastUpdatePosts(this.$filterPosts);
        },
    },
    mounted() {
        // Young Kbt
        if (Object.keys(this.$themeConfig.blogInfo).length > 0) {
            const {
                blogCreate,
                mdFileCountType,
                totalWords,
                moutedEvent,
                eachFileWords,
                indexIteration,
                indexView,
            } = this.$themeConfig.blogInfo;
            this.createToNowDay = dayDiff(blogCreate);
            if (mdFileCountType != "archives") {
                this.mdFileCount = mdFileCountType.length;
            } else {
                this.mdFileCount = this.$filterPosts.length;
            }
            if (totalWords == "archives" && eachFileWords) {
                let archivesWords = 0;
                eachFileWords.forEach((itemFile) => {
                    if (itemFile.wordsCount < 1000) {
                        archivesWords += itemFile.wordsCount;
                    } else {
                        let wordsCount = itemFile.wordsCount.slice(
                            0,
                            itemFile.wordsCount.length - 1
                        );
                        archivesWords += wordsCount * 1000;
                    }
                });
                this.totalWords = Math.round(archivesWords / 100) / 10 + "k";
            } else if (totalWords == "archives") {
                this.totalWords = 0;
                // console.log(
                //     "如果 totalWords = 'archives',必须传入 eachFileWords,显然您并没有传入!"
                // );
            } else {
                this.totalWords = totalWords;
            }
            // 最后一次活动时间
            this.lastActiveDate = timeDiff(this.$lastUpdatePosts[0].lastUpdated);
            this.mountedWebInfo(moutedEvent);
            // 获取访问量和排名
            this.indexView = indexView == undefined ? true : indexView;
            if (this.indexView) {
                this.getIndexViewCouter(indexIteration);
            }
        }
    },
    methods: {
        /**
         * 挂载站点信息模块
         */
        mountedWebInfo(moutedEvent = ".tags-wrapper") {
            let interval = setInterval(() => {
                const tagsWrapper = document.querySelector(moutedEvent);
                const webInfo = document.querySelector(".web-info");
                if (tagsWrapper && webInfo) {
                    if (!this.isSiblilngNode(tagsWrapper, webInfo)) {
                        tagsWrapper.parentNode.insertBefore(
                            webInfo,
                            tagsWrapper.nextSibling
                        );
                        clearInterval(interval);
                    }
                }
            }, 200);
        },
        /**
         * 挂载在兄弟元素后面,说明当前组件是 siblingNode 变量
         */
        isSiblilngNode(element, siblingNode) {
            if (element.siblingNode == siblingNode) {
                return true;
            } else {
                return false;
            }
        },
         /**
         * 首页的统计量
         */
        getIndexViewCouter(iterationTime = 3000) {
            fetch();
            var i = 0;
            var defaultCouter = "9999";
            // 如果只需要第一次获取数据(可能获取失败),可注释掉 setTimeout 内容,此内容是第一次获取失败后,重新获取访问量
            // 可能会导致访问量再次 + 1 原因:取决于 setTimeout 的时间(需求调节),setTimeout 太快导致第一个获取的数据没返回,就第二次获取,导致结果返回 + 2 的数据
            setTimeout(() => {
                let indexUv = document.querySelector(".web-site-pv");
                let indexPv = document.querySelector(".web-site-uv");
                if (
                    indexPv &&
                    indexUv &&
                    indexPv.innerText == "" &&
                    indexUv.innerText == ""
                ) {
                    let interval = setInterval(() => {
                        // 再次判断原因:防止进入 setInterval 的瞬间,访问量获取成功
                        if (
                            indexPv &&
                            indexUv &&
                            indexPv.innerText == "" &&
                            indexUv.innerText == ""
                        ) {
                            i += iterationTime;
                            if (i > iterationTime * 5) {
                                indexPv.innerText = defaultCouter;
                                indexUv.innerText = defaultCouter;
                                clearInterval(interval); // 5 次后无法获取,则取消获取
                            }
                            if (indexPv.innerText == "" && indexUv.innerText == "") {
                                // 手动获取访问量
                                fetch();
                            } else {
                                clearInterval(interval);
                            }
                        } else {
                            clearInterval(interval);
                        }
                    }, iterationTime);
                    // 绑定 beforeDestroy 生命钩子,清除定时器
                    this.$once("hook:beforeDestroy", () => {
                        clearInterval(interval);
                        interval = null;
                    });
                }
            }, iterationTime);
        },
        beforeMount() {
            let webInfo = document.querySelector(".web-info");
            webInfo && webInfo.parentNode.removeChild(webInfo);
        },
    },
};
</script>

<style scoped>
.web-info {
    font-size: 0.875rem;
    padding: 0.95rem;
}

.webinfo-title {
    text-align: center;
    color: #888;
    font-weight: bold;
    padding: 0 0 10px 0;
}

.webinfo-item {
    padding: 8px 0 0;
    margin: 0;
}

.webinfo-item-title {
    display: inline-block;
}

.webinfo-content {
    display: inline-block;
    float: right;
}

@keyframes turn {
    0% {
        transform: rotate(0deg);
    }

    100% {
        transform: rotate(360deg);
    }
}

.loading {
    display: inline-block;
    animation: turn 1s linear infinite;
    -webkit-animation: turn 1s linear infinite;
}
</style>

PageInfo.vue

<template>
    <div class="page-info">

        <!-- 当前文章页字数 -->
        <div class="book-words iconfont icon-book" style="float: left; margin-left: 20px; font-size: 0.8rem;">
            <a href="javascript:;" style="margin-left: 3px; color: #888">{{ wordsCount }}</a>
        </div>

        <!-- 预计阅读时间 -->
        <div class="reading-time iconfont icon-shijian" style="float: left; margin-left: 20px; font-size: 0.8rem;">
            <a href="javascript:;" style="margin-left: 3px; color: #888">{{ readTimeCount }}</a>
        </div>

         <!-- 文章页访问量 -->
            <div class="page-view iconfont icon-view" style="float: left; margin-left: 20px; font-size: 0.8rem;">
                <a href="javascript:;" id="busuanzi_page_pv" class="view-data">
                    <i title="正在获取..." class="loading iconfont icon-loading"></i>
                </a>
            </div>

            <!-- 本文总访客量 -->
            <div class="page_total_view iconfont icon-tongji" style="float: left; margin-left: 20px; font-size: 0.8rem;">
                <a href="javascript:;" id="busuanzi_page_uv" class="view-data">
                    <i title="正在获取..." class="loading iconfont icon-loading"></i>
                </a>
            </div>

    </div>
</template>

<script>
import fetch from "../webSiteInfo/busuanzi";
export default {

     data() {
        return {
            wordsCount: 0,
            readTimeCount: 0,
            mountedIntervalTime: 1000,
      //      showPageInfo: true,
            moutedParentEvent: ".articleInfo-wrap > .articleInfo > .info"
        };
    },

    mounted: function () {
        this.$nextTick(function () {
            if (this.$route.path != "/") {
                this.initPageInfo();
                this.isMounted(document.querySelector(".page-info"));
            } 
        })
    },

    watch: {
        $route(to, from) {
            // 如果页面是非首页,# 号也会触发路由变化,这里要排除掉
            if (to.path != "/" && to.path != from.path && this.$themeConfig.blogInfo) {
                this.initPageInfo();
                this.isMounted(document.querySelector(".page-info"));
            } 
        },
    },
    methods: {
        /**
         * 初始化页面信息
         */
        initPageInfo() {
            if (this.$frontmatter.article === undefined || this.$frontmatter.article) {
                // 排除掉 article 为 false 的文章
                const { eachFileWords, pageView, pageIteration, readingTime } =
                    this.$themeConfig.blogInfo;
                // 下面两个 if 可以调换位置,从而让文章的浏览量和字数交换位置
                if (eachFileWords) {
                    try {
                        eachFileWords.forEach((itemFile) => {
                            if (itemFile.permalink === this.$frontmatter.permalink) {
                                // this.addPageWordsCount 和 if 可以调换位置,从而让文章的字数和预阅读时间交换位置
                                this.wordsCount = itemFile.wordsCount;
                                if (readingTime || readingTime === undefined) {
                                    this.readTimeCount = itemFile.readingTime;
                                }
                            }
                        });
                    } catch (error) { 
                    console.error("获取浏览量失败:", error);
                    }
                }
                if (pageView || pageView === undefined) {
                    this.addPageView();
                    this.addtotalPageView()
                    this.getPageViewCouter(pageIteration);
                }
                 let page = document.querySelector(".page-info");
                if (page) {
                    this.mountedView(page);
                }
                // else { 
                //     console.error("初始化失败:", "站点信息不存在");
                // }
                return;
            }
        },
        /**
         * 文章页的访问量
         */
        getPageViewCouter(iterationTime = 3000) {
            fetch();
            let i = 0;
            var defaultCouter = "9999";
            // 如果只需要第一次获取数据(可能获取失败),可注释掉 setTimeout 内容,此内容是第一次获取失败后,重新获取访问量
            // 可能会导致访问量再次 + 1 原因:取决于 setTimeout 的时间(需求调节),setTimeout 太快导致第一个获取的数据没返回,就第二次获取,导致结果返回 + 2 的数据
            setTimeout(() => {
                let pageView = document.querySelector(".view-data");
                if (pageView && pageView.innerText == "") {
                    let interval = setInterval(() => {
                        // 再次判断原因:防止进入 setInterval 的瞬间,访问量获取成功
                        if (pageView && pageView.innerText == "") {
                            i += iterationTime;
                            if (i > iterationTime * 5) {
                                pageView.innerText = defaultCouter;
                                clearInterval(interval); // 5 次后无法获取,则取消获取
                            }
                            if (pageView.innerText == "") {
                                // 手动获取访问量
                                fetch();
                            } else {
                                clearInterval(interval);
                            }
                        } else {
                            clearInterval(interval);
                        }
                    }, iterationTime);
                    // 绑定 beforeDestroy 生命钩子,清除定时器
                    this.$once("hook:beforeDestroy", () => {
                        clearInterval(interval);
                        interval = null;
                    });
                }
            }, iterationTime);
        },
        /**
         * 浏览量
         */
        addPageView() {
            let pageView = document.querySelector(".page-view");
            if (pageView) {
                // 添加 loading 效果
                let style = document.createElement("style");
                style.innerHTML = `@keyframes turn {
                0% {
                transform: rotate(0deg);
                }
                100% {
                transform: rotate(360deg);
                }
            }
             .loading {
                display: inline-block;
                animation: turn 1s linear infinite;
                -webkit-animation: turn 1s linear infinite;
            }
          `;
                        document.head.appendChild(style);
                    }
        },

         /**
        * 本文总访客量
        */
        addtotalPageView() {
            let pageView = document.querySelector(".page_total_view");
            if (pageView) {
             // 添加 loading 效果
                let style = document.createElement("style");
                style.innerHTML = `@keyframes turn {
                0% {
                transform: rotate(0deg);
                }
                100% {
                transform: rotate(360deg);
                }
            }
            .loading {
                display: inline-block;
                animation: turn 1s linear infinite;
                -webkit-animation: turn 1s linear infinite;
            }`;
                document.head.appendChild(style);
            }
        },

        /**
         * 挂载目标到页面上
         */
        mountedView(template) {
            let i = 0;
            let parentElement = document.querySelector(this.moutedParentEvent);
            if (parentElement) {
                if (!this.isMountedView(template, parentElement)) {
                    parentElement.appendChild(template);
                }
            } else {
               let interval = setInterval(() => {
              let parentElement = document.querySelector(this.moutedParentEvent);
                if (parentElement) {
                    if (!this.isMountedView(template, parentElement)) {
                        parentElement.appendChild(template);
                        clearInterval(interval);
                    }
                } else if (i > 1 * 10) {
                    // 10 秒后清除
                    clearInterval(interval);
                }
            }, this.mountedIntervalTime);
            // 绑定 beforeDestroy 生命钩子,清除定时器
            this.$once("hook:beforeDestroy", () => {
                clearInterval(interval);
                interval = null;
            });
            }
        },

        //* 用于判断是否已经挂载到页面上 */
        isMounted(template) {
            let i = 0;
            let interval = setInterval(() => {
              let parentElement = document.querySelector(this.moutedParentEvent);
                if (parentElement) {
                    if (!this.isMountedView(template, parentElement) && template) {
                        parentElement.appendChild(template);
                        clearInterval(interval);
                    }
                } else if (i > 1 * 10) {
                    // 10 秒后清除
                    clearInterval(interval);
                }
            }, this.mountedIntervalTime);
            // 绑定 beforeDestroy 生命钩子,清除定时器
            this.$once("hook:beforeDestroy", () => {
                clearInterval(interval);
                interval = null;
            });
        },
       
        /**
         * 目标是否已经挂载在页面上
         */
        isMountedView(element, parentElement) {
            if (element) {
                if (element.parentNode == parentElement) {
                    return true;
                } else {
                    return false;
                }
            } else { 
                return false;
            }
            
        },
       
    },
  
};
</script>

<style>
 .view-data {
    color: #999;
    margin-left: 3px;
 }
 .page-info {
    display: inline-block;
 }

 .page-hide{
    display: none; 
    /* position: fixed;
    top: 96px;
    right: 450px;
    background-color: red; */
}
</style>

# 将pageInfo添加到源码中

vupress默认主题的基础上使用vdoing主题会有刷新页面文章页站点信息在页面上不显示的问题,可修改vdoing主题源码,然后用patch-package打补丁

https://liyao52033.github.io/pages/d18e21/#%E5%B0%86pageinfo%E6%B7%BB%E5%8A%A0%E5%88%B0%E6%BA%90%E7%A0%81%E4%B8%AD

在本地开发环境中:

# 1.安装 patch-package:

npm install patch-package --save-dev

# 2. 修改node_modules源码

  • 删除原来的.vuepress/components/PageInfo.vue文件,

  • 将原来.vuepress/webSiteInfo中的busuanzi.js添加到node_modules/vuepress-theme-vdoing/util中

  • 修改node_modules/vuepress-theme-vdoing/components中的ArticleInfo.vue

<template>
  <div class="articleInfo-wrap">
    <div class="articleInfo">
      <!-- 面包屑 -->
      <ul class="breadcrumbs" v-if="classify1 && classify1 !== '_posts'">
        <li>
          <router-link to="/" class="iconfont icon-home" title="首页" />
        </li>

        <li v-for="item in classifyList" :key="item">
          <!-- 跳目录页 -->
          <router-link v-if="cataloguePermalink" :to="getLink(item)">{{
            item
          }}</router-link>
          <!-- 跳分类页 -->
          <router-link
            v-else-if="$themeConfig.category !== false"
            :to="`/categories/?category=${encodeURIComponent(item)}`"
            title="分类"
            >{{ item }}</router-link
          >
          <!-- 没有跳转 -->
          <span v-else>{{ item }}</span>
        </li>
      </ul>

      <!-- 作者&日期 -->
      <div class="info">
        <div class="author iconfont icon-touxiang" title="作者" v-if="author">
          <a
            :href="author.href || author.link"
            v-if="
              author.href || (author.link && typeof author.link === 'string')
            "
            target="_blank"
            class="beLink"
            title="作者"
            >{{ author.name }}</a
          >
          <a v-else href="javascript:;">{{ author.name || author }}</a>
        </div>
        <div class="date iconfont icon-riqi" title="创建时间" v-if="date">
          <a href="javascript:;">{{ date }}</a>
        </div>
        <!-- 当前文章页字数 -->
        <div class="book-words iconfont icon-book" style="float: left; margin-left: 20px; font-size: 0.8rem;" title="文章字数">
            <a href="javascript:;" style="margin-left: 3px; color: #888">{{ wordsCount }}</a>
        </div>

        <!-- 预计阅读时间 -->
        <div class="reading-time iconfont icon-shijian" style="float: left; margin-left: 20px; font-size: 0.8rem;" title="预计阅读时间">
            <a href="javascript:;" style="margin-left: 3px; color: #888">{{ readTimeCount }}</a>
        </div>

        <!-- 文章页访问量 -->
        <div class="page-view iconfont icon-view" style="float: left; margin-left: 20px; font-size: 0.8rem;" title="文章访问量">
            <a href="javascript:;" id="busuanzi_page_pv" class="view-data">
                <i title="正在获取..." class="loading iconfont icon-loading"></i>
            </a>
        </div>

      <!-- 本文总访客量 -->
        <div class="page_total_view iconfont icon-tongji" style="float: left; margin-left: 20px; font-size: 0.8rem;" title="本文总访客量">
            <a href="javascript:;" id="busuanzi_page_uv" class="visitors-data">
                <i title="正在获取..." class="loading iconfont icon-loading"></i>
            </a>
        </div>
        <div
          class="date iconfont icon-wenjian"
          title="分类"
          v-if="
            $themeConfig.category !== false &&
            !(classify1 && classify1 !== '_posts') &&
            categories
          "
        >
          <router-link
            :to="`/categories/?category=${encodeURIComponent(item)}`"
            v-for="(item, index) in categories"
            :key="index"
            >{{ item + ' ' }}</router-link
          >
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import fetch from '../util/busuanzi.js'
export default {
  data() {
    return {
      date: '',
      classify1: '',
      classifyList: [],
      cataloguePermalink: '',
      author: null,
      categories: [],
      wordsCount: 0,
      readTimeCount: 0,
      mountedIntervalTime: 1000,
      moutedParentEvent: ".articleInfo-wrap > .articleInfo > .info"
    }
  },
  created() {
    this.getPageInfo()
  },
  mounted() {
   this.$nextTick(function () { 
      this.initPageInfo();
    })
  },
  watch: {
    '$route.path'() {
      this.classifyList = []
      this.getPageInfo()
      this.initPageInfo()
    }
  },
  methods: {
    getPageInfo() {
      const pageInfo = this.$page
      const { relativePath } = pageInfo
      const { sidebar } = this.$themeConfig

      // 分类采用解析文件夹地址名称的方式 (即使关闭分类功能也可以正确跳转目录页)
      const relativePathArr = relativePath.split('/')

      // const classifyArr = relativePathArr[0].split('.')

      relativePathArr.forEach((item, index) => {
        const nameArr = item.split('.')

        if (index !== relativePathArr.length - 1) {
          if (nameArr === 1) {
            this.classifyList.push(nameArr[0])
          } else {
            const firstDotIndex = item.indexOf('.');
            this.classifyList.push(item.substring(firstDotIndex + 1) || '')
          }
        }
      })

      this.classify1 = this.classifyList[0]

      const cataloguePermalink = sidebar && sidebar.catalogue ? sidebar.catalogue[this.classify1] : ''// 目录页永久链接
      const author = this.$frontmatter.author || this.$themeConfig.author // 作者
      let date = (pageInfo.frontmatter.date || '').split(' ')[0] // 文章创建时间

      // 获取页面frontmatter的分类(碎片化文章使用)
      const { categories } = this.$frontmatter

      this.date = date
      this.cataloguePermalink = cataloguePermalink
      this.author = author
      this.categories = categories
    },

    getLink(item) {
      const { cataloguePermalink } = this
      if (item === cataloguePermalink) {
        return cataloguePermalink
      }
      return `${cataloguePermalink}${cataloguePermalink.charAt(cataloguePermalink.length - 1) === '/'
        ? ''
        : '/'
        }#${item}`
    },

     /**
        * 初始化页面信息
        */
    initPageInfo() {
      if (this.$frontmatter.article === undefined || this.$frontmatter.article) {
        // 排除掉 article 为 false 的文章
        const { eachFileWords, pageView, pageIteration, readingTime } =
          this.$themeConfig.blogInfo;
        // 下面两个 if 可以调换位置,从而让文章的浏览量和字数交换位置
        if (eachFileWords) {
          try {
            eachFileWords.forEach((itemFile) => {
              if (itemFile.permalink === this.$frontmatter.permalink) {
                // this.addPageWordsCount 和 if 可以调换位置,从而让文章的字数和预阅读时间交换位置
                this.wordsCount = itemFile.wordsCount;
                if (readingTime || readingTime === undefined) {
                  this.readTimeCount = itemFile.readingTime;
                }
              }
            });
          } catch (error) {
            //     console.error("获取浏览量失败:", error);
          }
        }
        if (pageView || pageView === undefined) {
          this.addPageView();
          this.addtotalPageView()
          this.getPageViewCouter(pageIteration);
        }
        return;
      }
    },
     /**
     * 文章页的访问量
     */
    getPageViewCouter(iterationTime = 3000) {
      fetch();
      let i = 0;
      var defaultCouter = "9999";
      // 如果只需要第一次获取数据(可能获取失败),可注释掉 setTimeout 内容,此内容是第一次获取失败后,重新获取访问量
      // 可能会导致访问量再次 + 1 原因:取决于 setTimeout 的时间(需求调节),setTimeout 太快导致第一个获取的数据没返回,就第二次获取,导致结果返回 + 2 的数据
      setTimeout(() => {
        let pageView = document.querySelector(".view-data");
        if (pageView && pageView.innerText == "") {
          let interval = setInterval(() => {
            // 再次判断原因:防止进入 setInterval 的瞬间,访问量获取成功
            if (pageView && pageView.innerText == "") {
              i += iterationTime;
              if (i > iterationTime * 5) {
                pageView.innerText = defaultCouter;
                clearInterval(interval); // 5 次后无法获取,则取消获取
              }
              if (pageView.innerText == "") {
                // 手动获取访问量
                fetch();
              } else {
                clearInterval(interval);
              }
            } else {
              clearInterval(interval);
            }
          }, iterationTime);
          // 绑定 beforeDestroy 生命钩子,清除定时器
          this.$once("hook:beforeDestroy", () => {
            clearInterval(interval);
            interval = null;
          });
        }
      }, iterationTime);
    },
    /**
     * 浏览量
     */
    addPageView() {
      let pageView = document.querySelector(".page-view");
      if (pageView) {
        // 添加 loading 效果
        let style = document.createElement("style");
        style.innerHTML = `@keyframes turn {
                0% {
                transform: rotate(0deg);
                }
                100% {
                transform: rotate(360deg);
                }
            }
             .loading {
                display: inline-block;
                animation: turn 1s linear infinite;
                -webkit-animation: turn 1s linear infinite;
            }
          `;
        document.head.appendChild(style);
      }
    },

    /**
   * 本文总访客量
   */
    addtotalPageView() {
      let pageView = document.querySelector(".page_total_view");
      if (pageView) {
        // 添加 loading 效果
        let style = document.createElement("style");
        style.innerHTML = `@keyframes turn {
                0% {
                transform: rotate(0deg);
                }
                100% {
                transform: rotate(360deg);
                }
            }
            .loading {
                display: inline-block;
                animation: turn 1s linear infinite;
                -webkit-animation: turn 1s linear infinite;
            }`;
        document.head.appendChild(style);
      }
    },

  }
}
</script>

<style lang='stylus' scoped>
@require '../styles/wrapper.styl'

.theme-style-line
  .articleInfo-wrap
    .articleInfo
      padding-top 0.5rem
.articleInfo-wrap
  @extend $wrapper
  position relative
  z-index 1
  color #888
  .articleInfo
    overflow hidden
    font-size 0.92rem
    .breadcrumbs
      margin 0
      padding 0
      overflow hidden
      display inline-block
      line-height 2rem
      @media (max-width 960px)
        width 100%
      li
        list-style-type none
        float left
        padding-right 5px
        &:after
          content '/'
          margin-left 5px
          color #999
        &:last-child
          &:after
            content ''
        a
          color #888
          &:before
            font-size 0.92rem
          &:hover
            color $accentColor
        .icon-home
          text-decoration none
    .info
      float right
      line-height 32px
      @media (max-width 960px)
        float left
      div
        float left
        margin-left 20px
        font-size 0.8rem
        @media (max-width 960px)
          margin 0 20px 0 0
        &:before
          margin-right 3px
        a
          color #888
          &:hover
            text-decoration none
        a.beLink
          &:hover
            color $accentColor
            text-decoration underline

 .view-data {
    color: #999;
    margin-left: 3px;
 }

</style>

# 3.生成修改文件的补丁文件:

npx patch-package <package-name>
#如
npx patch-package vuepress-theme-vdoing

# 4.在 package.json 中添加 postinstall 脚本:

  • "scripts": {
      "postinstall": "patch-package"
    }
    

# 5.更新 GitHub Actions 工作流

# ...
# 安装依赖(仅在缓存未命中时执行)
- name: Install dependencies
if: steps.cache-node-modules.outputs.cache-hit != 'true'
run: npm install

# 应用补丁文件
- name: Apply patches with patch-package
run: npx patch-package

# 构建 VuePress 项目
# ...

# 6.保留vuepress-theme-vdoing提交到git

node_modules/
!node_modules/vuepress-theme-vdoing/
docs/.vuepress/dist/
.temp/

# 新增访问量(选做)

做了后页面切换频繁时会数据显示异常,谨慎选择

  • 每次看站点信息时,可以看到访问量增多,但是不知道到底增加了多少,所以我就修改了WebInfo和PageInfo中的代码,增加了显示新增访问量的功能,

    • 显示方式:在访问量后面显示,与你上次访问时网站的访问量相比增加了多少(访问量(+新增访问量)),
  • 主要是在原来统计的方法中新增了showCounterWithIncrement方法,并在访问量元素加载后,调用方法将访问量(+新增访问量)覆盖上去,过一段时间后再显示回访问量

    • 由于代码执行得比网站快,(代码获取访问量后,访问量对应的元素才加载出来),导致我们第一次直接获取访问量可能获取不到(你也可以将第一次尝试显示新增访问量的代码去掉

      setTimeout(() => {
          let indexPv = document.querySelector(".web-site-pv");
          let indexUv = document.querySelector(".web-site-uv");
          if (
            indexPv && 
            indexUv &&
            indexPv.innerText !== "" &&
            indexUv.innerText !== ""
          ) {
            showCounterWithIncrement(indexPv, indexUv);
            return;
          }
      	//...
      }//记得后面的}也要去掉
      

      ,只是会等待3秒后才加载新增的访问量,PageInfo.vue的代码同理),所以我用setTimeout等待了1秒(你也可以再调整小一点)才获取访问量。并把iterationTime从原来的3秒改为2秒,保证加起来与原来等待的时间不变

# WebInfo.vue

修改WebInfo.vue的getIndexViewCouter

 /**
         * 首页的统计量
         */
        getIndexViewCouter(iterationTime = 2000) {
            fetch();
            var i = 0;
            var defaultCouter = "9999";
            // 提取显示访问量和新增量的公共方法
            const showCounterWithIncrement = (pvElement, uvElement) => {
                // 读取 localStorage 中的上次访问量
                const lastPv = localStorage.getItem("lastSitePv") || 0;
                const lastUv = localStorage.getItem("lastSiteUv") || 0;

                // 当前访问量
                const currentPv = parseInt(pvElement.innerText) || 0;
                const currentUv = parseInt(uvElement.innerText) || 0;

                // 计算新增访问量
                const newPv = Math.max(currentPv - parseInt(lastPv), 0);
                const newUv = Math.max(currentUv - parseInt(lastUv), 0);
                // console.log("testtest",lastPv,currentPv,
                //     newPv, newUv
                // );

                if(newPv != 0){
                    // 显示新增的访问量
                    pvElement.innerText = `${currentPv}(+${newPv})`;
                    // 保存当前访问量到 localStorage
                    localStorage.setItem("lastSitePv", currentPv);
                }
                if(newUv != 0){
                    // 显示新增的访问量
                    uvElement.innerText = `${currentUv}(+${newUv})`;
                    // 保存当前访问量到 localStorage
                    localStorage.setItem("lastSiteUv", currentUv);
                }

                // 设置定时器隐藏新增访问量
                this.timer = setTimeout(() => {
                    pvElement.innerText = currentPv; // 仅显示总访问量
                    uvElement.innerText = currentUv; // 仅显示总访问量
                }, 5000); // 5秒后隐藏新增访问量
        		this.$once('hook:beforeDestroy', () => clearTimeout(timer)); // 确保销毁时清理
            };
            setTimeout(() => {
                let indexPv = document.querySelector(".web-site-pv");
                let indexUv = document.querySelector(".web-site-uv");
                if (
                indexPv && 
                indexUv &&
                indexPv.innerText !== "" &&
                indexUv.innerText !== ""
                ) {
                    showCounterWithIncrement(indexPv, indexUv);
                    return;
                }
                // 如果只需要第一次获取数据(可能获取失败),可注释掉 setTimeout 内容,此内容是第一次获取失败后,重新获取访问量
                // 可能会导致访问量再次 + 1 原因:取决于 setTimeout 的时间(需求调节),setTimeout 太快导致第一个获取的数据没返回,就第二次获取,导致结果返回 + 2 的数据
                setTimeout(() => {
                    let indexPv = document.querySelector(".web-site-pv");
                    let indexUv = document.querySelector(".web-site-uv");
                    if (
                        indexPv && 
                        indexUv &&
                        indexPv.innerText == "" &&
                        indexUv.innerText == ""
                    ) {
                        let interval = setInterval(() => {
                            // 再次判断原因:防止进入 setInterval 的瞬间,访问量获取成功
                            if (
                                indexPv && 
                                indexUv &&
                                indexPv.innerText == "" &&
                                indexUv.innerText == ""
                            ) {
                                i += iterationTime;
                                if (i > iterationTime * 5) {
                                    indexPv.innerText = defaultCouter;
                                    indexUv.innerText = defaultCouter;
                                    clearInterval(interval); // 5 次后无法获取,则取消获取
                                }
                                if (indexPv.innerText == "" && indexUv.innerText == "") {
                                // 手动获取访问量
                                    fetch();
                                } else {
                                    clearInterval(interval);
                                    showCounterWithIncrement(indexPv, indexUv);
                                }
                            } else {
                                clearInterval(interval);
                                showCounterWithIncrement(indexPv, indexUv);
                            }
                        }, iterationTime);
                        // 绑定 beforeDestroy 生命钩子,清除定时器
                        this.$once("hook:beforeDestroy", () => {
                            clearInterval(interval);
                            interval = null;
                        });
                    }else if(indexPv && indexUv){
                        showCounterWithIncrement(indexPv, indexUv);
                    }
                }, iterationTime);
            },1000)
        },
# ArticleInfo.vue

修改ArticleInfo.vue中的getPageViewCouter(TODO,有点小bug,如果你频繁切换页面会导致不同页面的访问量互相影响,因为用到setTimeOut延时任务,切换页面时应该取消掉该任务,用beforeDestroy取消不掉,如果你能解决的话)

/**
     * 文章页的访问量
     */
    getPageViewCouter(iterationTime = 2000) {
      fetch();
      let i = 0;
      var defaultCouter = "9999";
      // 提取显示访问量和新增量的公共方法
      const showPageViewWithIncrement = (element, visitorsElement) => {
        // 获取当前文章的唯一标识符
        const articleKey = this.$frontmatter.permalink || window.location.pathname;
        // 读取上次访问量
        const lastPageView = parseInt(localStorage.getItem(`lastPagePv_${articleKey}`)) || 0;
        const lastPageVisitors = parseInt(localStorage.getItem(`lastPageUv_${articleKey}`)) || 0;
        // 当前访问量
        const currentPageView = parseInt(element.innerText) || 0;
        const currentVisitors = parseInt(visitorsElement.innerText) || 0;

        // 计算新增访问量(确保不为负数)
        const newPageView = Math.max(currentPageView - lastPageView, 0);
        const newPageVisitors = Math.max(currentVisitors - lastPageVisitors, 0);

        if(newPageView != 0){
          // 显示新增访问量
          element.innerText = `${currentPageView}(+${newPageView})`;
          // 保存当前访问量
          localStorage.setItem(`lastPagePv_${articleKey}`, currentPageView);
        }
        if(newPageVisitors != 0){
          // 显示新增访问量
          visitorsElement.innerText = `${currentVisitors}(+${newPageVisitors})`;
          // 保存当前访问量
          localStorage.setItem(`lastPageUv_${articleKey}`, currentVisitors);
          // 设置定时器隐藏新增访问量
        }
        this.timer = setTimeout(() => {
          element.innerText = currentPageView; // 仅显示总访问量
          visitorsElement.innerText = currentVisitors; // 仅显示总访问量
        }, 5000); // 5秒后隐藏新增访问量
        this.$once('hook:beforeDestroy', () => clearTimeout(timer)); // 确保销毁时清理
      };
      setTimeout(() => {
        let pageView = document.querySelector(".view-data");
          let visitors = document.querySelector(".visitors-data");
        if (pageView && pageView.innerText !== "" && visitors && visitors.innerText !== "") {
          showPageViewWithIncrement(pageView, visitors);
          return;
        }

        // 如果只需要第一次获取数据(可能获取失败),可注释掉 setTimeout 内容,此内容是第一次获取失败后,重新获取访问量
        // 可能会导致访问量再次 + 1 原因:取决于 setTimeout 的时间(需求调节),setTimeout 太快导致第一个获取的数据没返回,就第二次获取,导致结果返回 + 2 的数据
        setTimeout(() => {
          let pageView = document.querySelector(".view-data");
          let visitors = document.querySelector(".visitors-data");
          if (pageView && pageView.innerText == "" && visitors && visitors.innerText == "") {
            let interval = setInterval(() => {
              // 再次判断原因:防止进入 setInterval 的瞬间,访问量获取成功
              if (pageView && pageView.innerText == "" && visitors && visitors.innerText == "") {
                i += iterationTime;
                if (i > iterationTime * 5) {
                  pageView.innerText = defaultCouter;
                  visitors.innerText = defaultCouter; 
                  clearInterval(interval); // 5 次后无法获取,则取消获取
                }
                if (pageView.innerText == "") {
                  // 手动获取访问量
                  fetch();
                } else {
                  clearInterval(interval);
                  showPageViewWithIncrement(pageView, visitors);
                }
              } else {
                clearInterval(interval);
                showPageViewWithIncrement(pageView, visitors);
              }
            }, iterationTime);
            // 绑定 beforeDestroy 生命钩子,清除定时器
            this.$once("hook:beforeDestroy", () => {
              clearInterval(interval);
              interval = null;
            });
          }else if(pageView){
            showPageViewWithIncrement(pageView, visitors);
          }
        }, iterationTime);
      },1500)

    },

!!注意,每次修改 vuepress-theme-vdoing 中的文件,需要重新生成补丁npx patch-package vuepress-theme-vdoing,不然部署到GitHu pages是不会更新的┭┮﹏┭┮

部署时可能出现patch-package: not found,需要在本地重新执行安装 patch-package操作

# 1

上次更新: 2025/5/2 14:40:28
评论区
MoreMore

← 评论区 MoreMore→

最近更新
01
项目优化
05-06
02
项目优化
05-06
03
延迟消息
05-05
更多文章>
Theme by Vdoing | Copyright © 2025-2025 Cyan Blog
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式