十八比特 十八比特
⚡ 18bit DNS (opens new window)
🏠 主页
  • 🌐 网站
  • 📦 资源
  • 📝 教程
  • 📐 制表符
  • 🗃️ 分类
  • 🏷️ 标签
  • 🗄️ 归档
📃 文章日志
ℹ️ 关于

十八比特

风带来故事的种子,时间使之发芽
⚡ 18bit DNS (opens new window)
🏠 主页
  • 🌐 网站
  • 📦 资源
  • 📝 教程
  • 📐 制表符
  • 🗃️ 分类
  • 🏷️ 标签
  • 🗄️ 归档
📃 文章日志
ℹ️ 关于
  • 文章使用CDN加载静态资源
  • 文章无法加载Webp格式图片解决方法
  • 给文章添加Waline评论系统和文章阅读量统计
    • 前言
    • 搭建 Waline
    • 集成到 Vdoing
    • 结束
  • 优化VuePress文章的SEO
  • 文章日志
  • 博客建设
秋澪冬安
2022-04-24
目录

给文章添加Waline评论系统和文章阅读量统计

# 前言

这篇文章对应的 Vuepress 版本为1.x。使用的主题是 Vdoing (opens new window)

接下来的步骤都是基于这个主题来的,如果使用的其它主题,本教程可能对你没有帮助

# 搭建 Waline

这部分直接参考官方文档 (opens new window)

接下来着重讲一下怎么集成到主题上

# 集成到 Vdoing

# 引入在线图标

在config.js文件内的head中添加:

["link", { rel: "stylesheet", href: "https://at.alicdn.com/t/font_3352320_u1o3vbik6r8.css" }]

# 创建 Waline 组件

在docs/.vuepress/components目录下创建组件Waline.vue

<template>
    <div id="waline"></div>
</template>
<script>
import Waline from "@waline/client";
const locale = { placeholder: "发条友善的评论吧" };
export default {
    name: "Waline",
    data() {
        return {
            walineTemplate: "", // 存储初始化的评论区模板
            firstLoad: true, // 是否第一次初始化评论区
            isLocal: false, // 是否是本地环境
        };
    },

    mounted() {
        // 判断是否是本地调试
        this.isLocal = window.location.hostname.includes("localhost");
        // 第一次打开网站且不是首页,初始化评论区
        // 不初始化评论区的页面:首页、404、分类、归档、标签页面;comment: false 的文章页
        if (
            (this.$frontmatter.comment == undefined ||
                this.$frontmatter.comment) &&
            this.$route.path != "/" &&
            this.$route.path != "/archives/" &&
            this.$route.path != "/tags/" &&
            this.$route.path != "/categories/" &&
            !this.isFourZeroFour(this.$route)
        ) {
            // 延迟初始化评论区,防止加载过快导致评论区初始化失败
            const t = setTimeout(() => {
                this.mountVisitorPage();
                clearTimeout(t);
            }, 500);
        }
        // 在首页、标签页、分类页时,初始化 waline 并文章加载阅读数
        if (
            this.$route.path == "/categories/" ||
            this.$route.path == "/tags/" ||
            this.$route.path == "/"
        ) {
            const t = setTimeout(() => {
                this.mountVisitorList();
                this.deleteComment();
                clearTimeout(t);
            }, 500);
        }
    },

    watch: {
        $route(to, from) {
            // 404、归档页面,不做回应
            // 首页、分类、标签页面,显示文章阅读量
            if (
                this.$route.path == "/categories/" ||
                this.$route.path == "/tags/" ||
                this.$route.path == "/"
            ) {
                const t = setTimeout(() => {
                    this.mountVisitorList();
                    clearTimeout(t);
                }, 500);
                return;
            } else if (
                to.path == from.path ||
                this.isFourZeroFour(to) ||
                this.$route.path == "/archives/"
            ) {
                return;
            }
            // 通过 frontmatter 的 comment 判断文章页是否需要评论区
            if (
                this.getCommentByFrontmatter(to) == undefined ||
                this.getCommentByFrontmatter(to)
            ) {
                const t = setTimeout(() => {
                    // 是否是第一次初始化过 waline
                    if (this.firstLoad) {
                        // 初始化 waline,并加载评论区
                        this.walineInit();
                        this.firstLoad = false;
                    } else {
                        // 仅加载评论区,刷新阅读数,不再初始化 waline
                        this.mountVisitorPage();
                        this.loadWaline();
                    }
                    clearTimeout(t);
                }, 500);
            } else {
                // 如果文章页不需要评论区,且初始化过 waline,则删除评论区
                if (!this.firstLoad) {
                    this.deleteComment();
                }
            }
        },
    },

    methods: {
        // 初始化 waline
        walineInit() {
            this.vWaline = Waline({
                el: "#waline",
                serverURL: "https://your-domain.vercel.app",
                visitor: this.isLocal ? false : true,
                dark: "body.theme-mode-dark",
                locale,
            });
            this.loadWaline();
        },
        // 更新信息
        updateInfo() {
            this.vWaline.update();
        },

        /**************** 评论区功能 ****************/
        // 加载 waline 评论区
        loadWaline() {
            let page = document.getElementsByClassName("page")[0];
            let comment = document.getElementById("waline");
            // 判断是否初始化过 waline
            if (this.firstLoad) {
                // 存储评论区模板
                this.walineTemplate = comment;
            } else {
                // 从存储的评论区模板中提取评论区
                comment = this.walineTemplate;
            }
            // 插入评论区
            page ? page.appendChild(comment) : "";
            // 更新评论区
            this.updateInfo();
        },
        // 判定当前页面是不是 404
        isFourZeroFour(route) {
            let flag = true;
            this.$site.pages.forEach((item) => {
                if (item.path == route.path) {
                    flag = false;
                }
            });
            return flag;
        },
        // 获取 frontmatter 的 comment
        getCommentByFrontmatter(route) {
            let comment = "";
            this.$site.pages.forEach((item) => {
                if (item.path == route.path) {
                    comment = item.frontmatter.comment;
                }
            });
            return comment;
        },
        // 删除评论区
        deleteComment() {
            let comment = document.getElementById("waline");
            comment ? comment.parentNode.removeChild(comment) : "";
        },

        /**************** 访问量功能 ****************/
        // 在文章列表加载阅读数
        mountVisitorList() {
            // 获取当前页面的所有文章列表
            const parentElement = document.querySelectorAll(
                ".post-list > div > .post >  .title-wrapper"
            );
            // 遍历文章列表
            parentElement.forEach((element) => {
                // 获取文章 a 标签中的 href 属性
                let a = element.querySelector("h2 > a");
                // 生成模板
                let template = document.createElement("span");
                template.title = "阅读数量";
                template.className = "iconfont icon-chakanshu";
                template.innerHTML = `<span class="waline-visitor-count" id="${a.getAttribute(
                    "href"
                )}"><span title="正在获取..." class="loading iconfont icon-lodaing"></span></span>`;
                // 挂载模板
                let target = element.querySelector(".article-info");
                if (!this.isMountedView(target)) {
                    target.appendChild(template);
                }
            });
            // 是否初始化过 waline
            if (this.firstLoad) {
                // 添加 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);
                this.walineInit();
                this.firstLoad = false;
            } else {
                this.updateInfo();
            }
        },
        // 在文章页加载阅读数
        mountVisitorPage() {
            // 获取元素
            const parentElement = document.querySelector(
                ".articleInfo-wrap > .articleInfo > .info"
            );
            if (parentElement) {
                // 生成模板
                let template = document.createElement("div");
                template.title = "阅读数量";
                template.className = "date iconfont icon-chakanshu";
                template.style.float = "left";
                template.style.marginLeft = "20px";
                template.style.fontSize = "0.8rem";
                template.innerHTML = `<span style="margin-left: 3px;" href="javascript:;" class="waline-visitor-count" id="${this.$route.path}"><span title="正在获取..." class="loading iconfont icon-lodaing"></span></span>`;
                // 挂载模板
                if (!this.isMountedView(parentElement)) {
                    parentElement.appendChild(template);
                } else {
                    let element =
                        parentElement.querySelector(".icon-chakanshu");
                    element.innerHTML = `<span href="javascript:;" class="waline-visitor-count" id="${this.$route.path}"><span title="正在获取..." class="loading iconfont icon-lodaing"></span></span>`;
                }
            }
            // 是否初始化过 waline
            if (this.firstLoad) {
                // 添加 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);
                this.walineInit();
                this.firstLoad = false;
            }
        },
        // 判定元素是否已经挂载
        isMountedView(parentElement) {
            var element = parentElement.querySelector(".icon-chakanshu");
            if (element) {
                return true;
            } else {
                return false;
            }
        },
    },
};
</script>

# 创建 WalineIndex 组件

之后再创建一个组件WalineIndex.vue,引入上面创建的Waline组件

提示

为什么还要再创建一个WalineIndex组件来引入Waline组件呢,因为直接使用Waline组件的话,在运行yarn dev命令时不会有什么问题,但是如果使用yarn build命令构建时会报错。

原因详见 browser-api-access-restrictions (opens new window)

<template>
    <component v-if="Waline" :is="Waline" />
</template>
<script>
export default {
    name: "WalineIndex",
    data() {
        return {
            Waline: null,
        };
    },
    mounted() {
        // 使用动态注册的方式,加载 Waline 核心组件
        // 详见 ==> https://v1.vuepress.vuejs.org/guide/using-vue.html#browser-api-access-restrictions
        import("./Waline").then((module) => {
            this.Waline = module.default;
        });
    },
};
</script>

# 配置 config.js

在docs/.vuepress/config.js的plugins模块内添加内容:

[
    {
        name: "custom-plugins",
        globalUIComponents: ["WalineIndex"]
    }
],

# 结束

完成上述步骤之后,评论区就成功集成到主题上了,在文章列表和文章页信息栏也会显示文章的阅读数量

参考文章:

https://notes.youngkbt.cn/about/website/comment/ (opens new window)

https://notes.youngkbt.cn/about/website/info/ (opens new window)

#文章#Waline
上次编辑: 2024/08/30, 16:21:31

← 文章无法加载Webp格式图片解决方法 优化VuePress文章的SEO→

最近更新
01
SSH跳板机访问内网服务
06-16
02
GPG 导出导入命令
06-16
03
从零开始的 All In One
06-14
更多文章>
Theme by Vdoing | Copyright © 2020-2025 十八比特 | 蜀ICP备2022002410号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式