























文章更新提示
对于 Vue 动态生成的部分属性做了兼容处理,解决了样式错乱问题
如标题所说,一个跳转提示页,可以防止用户直接访问站外链接,而是跳转到一个提示页,让用户先确认是否进入。许多知名站点,如 知乎、掘金 等,都提供了这样的提示页。
那为什么需要这个功能?我问了下 GPT,具体有以下几点:
至于我,其实是看到许多博主都搞了这样的跳转提示页,就觉得挺有意思的,于是想自己也搞一个。
说干就干,但是问题来了,其他博主要么使用的是 Typecho,要么是 Hexo,基本上都有对应的插件来实现这个功能,但是我使用的是 Vitepress,插件?根本没有,那就只能自己搞了。
由于 Vitepress 是基于 Vue 的,所以,最初打算在 onMounted 这个生命周期中,查找到全部的 <a> 标签,然后遍历这些标签,判断是否具有 target="_blank" 属性,如果有,则证明这个链接是站外链接,那么就进行替换。
但是这样有一个问题,在搜索引擎爬取网站时,并不能触发 Vue 的生命周期函数,所以,爬取到的页面仍旧为原链接,这样可不行,只能另寻他法。
最后,在翻看 vp 的文档时,找到了名为 transformHtml 的 构建钩子。
transformHtml是一个构建钩子,用于在保存到磁盘之前转换每个页面的内容。
那就好办了,这个钩子是在渲染页面时触发的,那就可以在 transformHtml 中替换标签内容了。
最初想的是用正则替换,于是请教了下 GPT ,结果给出了下面这一大坨:
js
const regex =
/<a(?=[^>]*?href=['"]([^'"]*)['"])(?=[^>]*?target=['"]_blank['"])(?:(?=[^>]*?class=['"]([^'"]*)['"]))?[^>]*?>(.*?)<\/a>/g;说实话,我一直没搞懂正则怎么写,甚至看不太懂这个正则 (●'◡'●),并且后面用正则的查找和替换也是个麻烦事,那就只能另寻他法。
由于不用正则,那就只能用我热榜项目用来解析 html 的 cheerio 库了,试了一下,还真可以,并且代码清晰了不少。
准备就绪,那让我们来实现这个方法:
js
/**
* 跳转中转页
* @param {string} html - 页面内容
* @param {boolean} isDom - 是否为 DOM 对象
*/
export const jumpRedirect = (html, isDom = false) => {
try {
// 是否启用
if (!themeConfig.jumpRedirect.enable) return html;
// 中转页地址
const redirectPage = "/redirect.html?url=";
// 排除的 className
const excludeClass = themeConfig.jumpRedirect.exclude;
if (isDom) {
if (typeof window === "undefined" || typeof document === "undefined") return false;
// 所有链接
const allLinks = [...document.getElementsByTagName("a")];
if (allLinks?.length === 0) return false;
allLinks.forEach((link) => {
// 检查链接是否包含 target="_blank" 属性
if (link.getAttribute("target") === "_blank") {
// 检查链接是否包含排除的类
if (excludeClass.some((className) => link.classList.contains(className))) {
return false;
}
const linkHref = link.getAttribute("href");
// 存在链接且非中转页
if (linkHref && !linkHref.includes(redirectPage)) {
// Base64
const encodedHref = btoa(linkHref);
const redirectLink = `${redirectPage}${encodedHref}`;
// 保存原始链接
link.setAttribute("original-href", linkHref);
// 覆盖 href
link.setAttribute("href", redirectLink);
}
}
});
} else {
const $ = load(html);
// 替换符合条件的标签
$("a[target='_blank']").each((_, el) => {
const $a = $(el);
const href = $a.attr("href");
const classesStr = $a.attr("class");
const innerText = $a.text();
// 检查是否包含排除的类
const classes = classesStr ? classesStr.trim().split(" ") : [];
if (excludeClass.some((className) => classes.includes(className))) {
return;
}
// 存在链接且非中转页
if (href && !href.includes(redirectPage)) {
// Base64 编码 href
const encodedHref = Buffer.from(href, "utf-8").toString("base64");
// 获取所有属性
const attributes = el.attribs;
// 重构属性字符串,保留原有属性
let attributesStr = "";
for (let attr in attributes) {
if (Object.prototype.hasOwnProperty.call(attributes, attr)) {
attributesStr += ` ${attr}="${attributes[attr]}"`;
}
}
// 构造新标签
const newLink = `<a href="${redirectPage}${encodedHref}" original-href="${href}" ${attributesStr}>${innerText}</a>`;
// 替换原有标签
$a.replaceWith(newLink);
}
});
return $.html();
}
} catch (error) {
console.error("处理链接时出错:", error);
}
};说下这个函数大致都做了什么:
isDom:由于 transformHtml 钩子是在 Node.js 环境下执行的,所以需要判断当前环境是否为 Node.js。从而采用不同方案( 详见下文 )excludeClass:将要排除的标签类名,比如友链的类名encodedHref:将真实地址改为 base64 编码格式说了这么多,唯独缺少了最重要的 —— 跳转页面,其实这个页面就是一个静态页面,你可以自行实现,或者直接使用第三方的页面,比如知乎。( 我觉得不好看 )
这里给出本站的跳转页面,你可以参考或者直接使用:
引用站外地址,请注意甄别链接安全性無名小栈 - 跳转提示页GitHub Gist: instantly share code, notes, and snippets.
由于评论系统都是在页面渲染后再生成的,并且对于评论区的外链进行中转也是必要的,所以需要对这个特殊的情况做特殊处理。
通常情况下,各个评论系统都有相应的 Event 事件,以本站使用的 Artalk 举例,你可以在文档中找到关于 Event 事件的说明。
Artalk 提供了一个名为 list-loaded 的事件,当评论列表加载完成后会触发该事件:
js
artalk.on("list-loaded", () => {
// 在此调用替换函数
jumpRedirect();
});如你所见,我上方提供的函数也对这种情况进行了兼容处理,只需在调用函数时将 isDom 参数传入 true 即可。
js
jumpRedirect(null, true);完美收工!( 可能还会有问题,等出现了在修吧 )
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。