Skip to content

[Astro] 如何自動為外部連結加上 rel='nofollow' 和 target='_blank' 屬性

Published: at 08:58 AM (3 min read)

Astro 中寫 MDX 產生內容時非常方便且快速,但時常內容可能包含外部連結,這時候我們可能會希望自動為這些外部連結加上 rel='nofollow'target='_blank' 屬性,讓用戶在點擊外部連結時不會離開我們的網站和提升網站 SEO。

如何實作

由於在 Astro 中 HTML 最終會經過 rehype plugins 處理,因此其實我們可以在這裡掛上一個 plugin 來幫我們自動為外部連結加上 rel='nofollow'target='_blank' 屬性。

實作有以下兩種方向:

  1. 直接使用開源寫好的 💁‍♂️ rehype-external-links
# 安裝 rehype-external-links
npm install rehype-external-links
// astro.config.ts
markdown: {
  // 加上以下這段設定
  rehypePlugins: [
    [
      rehypeExternalLinks,
      {
        target: "_blank",
      },
    ],
  ]
},
  1. rehype plugin 文件 自己開發一個 Plugin ✏️
// src/externalLink.ts
import type { RehypePlugin } from "@astrojs/markdown-remark";
import { visit } from "unist-util-visit";
import type { Element } from "hast";

interface Options {
  domain: string; // The domain to check against for determining if a link is external
}

// Define the Rehype plugin for processing external links
const externalLink: RehypePlugin = (options?: Options) => {
  const siteDomain = options?.domain ?? "";

  return tree => {
    // Visit each node in the syntax tree
    visit(tree, node => {
      // If the node is not an element, skip processing
      if (node.type != "element") {
        return;
      }

      const element = node as Element;

      // Check if the element is an anchor tag; if not, skip processing
      if (!isAnchor(element)) {
        return;
      }

      // Get the URL from the anchor element
      const url = getUrl(element);

      // Check if the URL is external; if so, set the target attribute to "_blank"
      if (isExternal(url, siteDomain)) {
        element.properties!["target"] = "_blank";
      }
    });
  };
};

// Helper function to determine if an element is an anchor tag
const isAnchor = (element: Element) =>
  element.tagName == "a" && element.properties && "href" in element.properties;

// Helper function to extract the URL from an anchor element
const getUrl = (element: Element) => {
  if (!element.properties) {
    return "";
  }

  const url = element.properties["href"];

  if (!url) {
    return "";
  }

  return url.toString();
};

// Helper function to determine if a URL is external
const isExternal = (url: string, domain: string) => {
  return url.startsWith("http") && !url.includes(domain);
};

export default externalLink;
// import externalLink plugin
import externalLink from "./src/externalLink";
...
// astro.config.ts
markdown: {
  // 加上你自己寫的 plugin 設定
  rehypePlugins: [
    [
      rehypeExternalLinks,
      {
        target: "_blank",
      },
    ],
  ],
}

1 應該會是最快也最簡單的方式,但如果你想要更多彈性與調整
2 則會是一個練習自己寫 rehype plugin 的不錯機會!

這樣調整完後,就不用在 MDX 文件中特別為每個外部連結手動寫 HTML 加上 rel='nofollow'target='_blank' 屬性了 🎉

參考資料


Previous Post
[leetcode] 4. Median of Two Sorted Arrays
Next Post
[leetcode] 3. Longest Substring Without Repeating Characters