Stories

Detail Return Return

低成本單頁應用 SEO(Simple-Spa-Seo) - Stories Detail

背景

  • 本項目,低成本預渲染:

    • 非侵入式,無需改動業務代碼
    • 屏蔽框架差異,無依賴,單文本實現,直接拷貝粘貼本腳本即可實現功能
    • 簡單,開源代碼,無編譯,腳本代碼量僅一百多行,二次集成門檻低,代碼清晰易懂
    • 注意本項目僅適合低成本的中小型項目,大型商業項目可參考以下成熟方案
  • SSG 預渲染:Prerender SPA Plugin,和本工具類似,但集成了 puppeteer,自動運行項目,截取內容,生產頁面元素
  • SSR 服務端渲染:Next.js React 應用程序的服務器渲染框架,Nuxt.js Vue.js 應用程序的服務器渲染框架

效果

  • 在指定目錄生成與 index.html 為模版的入口文件
  • 在每個入口文件中,實現各自定製的 seo 配置,實現 title、keywords、description 等信息的自定義

功能

  • 【pageLinkNum】自動生成頁面間的超鏈接跳轉,實現頁面間關聯效果,設置為 0 則關閉該功能
  • 【path】需要對外暴露的路徑,腳本將自動在相對路徑生成對應入口文件。注意,無需刻意攜帶文件後綴,通過後文 nginx 的一行配置,可將無後綴文件統一以 html 文件類型返回。讓瀏覽器正常解析。
  • 【title】指定頁面的 title,<title>m3u8 downloader</title>
  • 【keywords】指定頁面的 seo 關鍵字,<meta name="keywords" content="m3u8 下載工具,毛靜文的博客,Momo's Blog">
  • 【description】指定頁面的 seo 摘要,<meta name="description" content="無需後端支持,一個頁面即可完成 m3u8 視頻下載,遍歷下載 ts 碎片,完成 ts 碎片文件整合,生成完整視頻文件。">
  • 【img】設置頁面的圖片,實現搜索引擎收錄,展示效果時,展示縮略圖
  • 【link】設置頁面的超鏈接跳轉,使得搜索引擎的蜘蛛爬蟲可以按照超鏈接找到系統中其他頁面的鏈接,提供相關頁面的入口
  • 【content】設置頁面主體文案內容,使得搜索引擎可匹配關鍵字
  • 【html】其他自定義標籤,提供自定義插入特定 html 代碼功能
{
    path: '/m3u8-downloader/index.html', // 訪問鏈接
    title: 'm3u8 downloader',
    keywords: "m3u8 下載工具,毛靜文的博客,Momo's Blog",
    description: '無需後端支持,一個頁面即可完成 m3u8 視頻下載,遍歷下載 ts 碎片,完成 ts 碎片文件整合,生成完整視頻文件。', // 頁面描述
    img: [
      'https://upyun.luckly-mjw.cn/Assets/blog/m3u8-downloader-121-75.jpeg',
      'https://upyun.luckly-mjw.cn/Assets/blog/m3u8-downloader.jpeg'
    ],
    link: [{
      text: '點擊這裏跳轉',
      href: '/tool-show/nginx-online-config-debug/index.html',
    }],
    content: [{
      tag: 'h1',
      text: '特大視頻原格式下載,邊下載邊保存,徹底解決大文件下載內存不足問題',
    }, {
      tag: 'div',
      text: '推薦一個 m3u8 網頁版提取工具,無需下載軟件,打開網站即可下載,自動檢測,一鍵下載。',
    }],
    html: `
    頁面加載中,請耐心等待...
    <h1 style="white-space: pre;">
      推薦一個 m3u8 網頁版提取工具,無需下載軟件,打開網站即可下載,自動檢測,一鍵下載。
      工具鏈接:https://blog.luckly-mjw.cn/tool-show/m3u8-downloader/index.html
      工具教程:https://segmentfault.com/a/1190000021847172?_ea=32289224
      <img src="https://upyun.luckly-mjw.cn/Assets/blog/m3u8-downloader-121-75.jpeg" alt="m3u8 視頻下載工具" title="logo"/>
      <a target="_blank" href="https://blog.luckly-mjw.cn/tool-show/m3u8-downloader/index.html">點擊跳轉</a>
    </h1>
`,
  }

用法

  • 拷貝項目的 seo.js 文件,修改配置項
  • 執行腳本,node seo.js
  • 集成 package script

  • 添加 nginx 配置,default_type text/html; 使無後綴文件以 html 類型返回

源代碼

/* eslint-disable prettier/prettier */
const fs = require('fs');
const path = require('path');
const pageLinkNum = 3; // 自動生成頁面間的超鏈接跳轉的個數,設置為 0 則關閉該功能
const appendTagId = 'app'; // 頁面 seo 元素(超鏈接,圖片,文字等)插入的容器 id
const outputBasePath = path.join(__dirname, 'dist'); // 新建的 seo 入口文件,放在那個文件夾下
const templatePath = path.join(__dirname, 'dist', 'index.html'); // 以那個文件作為模版,一般是 spa 項目的 index.html 文件

// SEO 參考資料:https://github.com/madawei2699/awesome-seo/blob/main/README_CN.md

// 定義數組對象
const pageConfigs = [
  {
    // 參考資料:https://developers.google.com/search/docs/crawling-indexing/url-structure?hl=zh-cn
    path: '/m3u8-downloader/index.html', // 訪問鏈接

    // 頁面標題:搜索引擎通常顯示頁面標題的前 55 至 60 個字符,超出此範圍的文本可能會丟失
    // 參考資料:https://developers.google.com/search/docs/appearance/title-link?hl=zh-cn
    // 參考資料:https://developer.mozilla.org/en-US/docs/Web/HTML/Element/title#page_titles_and_seo
    title: 'm3u8 downloader',

    // 頁面關鍵字,google 已經棄用該字段
    // https://zhuanlan.zhihu.com/p/382454488
    // https://developers.google.com/search/docs/crawling-indexing/special-tags?hl=zh-cn
    keywords: "m3u8 下載工具,毛靜文的博客,Momo's Blog",

    // 頁面摘要
    // https://developers.google.com/search/docs/appearance/snippet?hl=zh-cn
    description: '無需後端支持,一個頁面即可完成 m3u8 視頻下載,遍歷下載 ts 碎片,完成 ts 碎片文件整合,生成完整視頻文件。', // 頁面描述

    // 自定插入的圖片
    img: [
      'https://upyun.luckly-mjw.cn/Assets/blog/m3u8-downloader-121-75.jpeg',
      'https://upyun.luckly-mjw.cn/Assets/blog/m3u8-downloader.jpeg'
    ],

    // 自定義超鏈接跳轉
    // 參考資料:https://developers.google.com/search/docs/appearance/sitelinks?hl=zh-cn
    link: [{
      text: '點擊這裏跳轉',
      href: '/tool-show/nginx-online-config-debug/index.html',
    }],

    // 自定義插入的標籤
    content: [{
      tag: 'h1',
      text: '特大視頻原格式下載,邊下載邊保存,徹底解決大文件下載內存不足問題',
    }, {
      tag: 'div',
      text: '推薦一個 m3u8 網頁版提取工具,無需下載軟件,打開網站即可下載,自動檢測,一鍵下載。',
    }],

    // 自定義插入的 html
    // 參考資料:https://zhuanlan.zhihu.com/p/391844443
    html: `
    頁面加載中,請耐心等待...
    <h1 style="white-space: pre;">
      推薦一個 m3u8 網頁版提取工具,無需下載軟件,打開網站即可下載,自動檢測,一鍵下載。
      工具鏈接:https://blog.luckly-mjw.cn/tool-show/m3u8-downloader/index.html
      工具教程:https://segmentfault.com/a/1190000021847172?_ea=32289224
      <img src="https://upyun.luckly-mjw.cn/Assets/blog/m3u8-downloader-121-75.jpeg" alt="m3u8 視頻下載工具" title="logo"/>
      <a target="_blank" href="https://blog.luckly-mjw.cn/tool-show/m3u8-downloader/index.html">點擊跳轉</a>
    </h1>
`,
  },
];

// 生成頁面間隨機頁面跳轉
pageLinkNum && pageConfigs.forEach(config => {
  config.link = config.link || [];
  const pageLinks = [...pageConfigs];
  for (let index = 0; index < Math.min(pageConfigs.length, pageLinkNum); index++) {
    const pageConfig = pageLinks.splice(Math.floor(Math.random() * pageLinks.length), 1)[0];
    config.link.push({ // 自定義插入的標籤
      text: pageConfig.title || pageConfig.keywords || pageConfig.description,
      href: pageConfig.path,
    })
  }
})

const appendTagRegex = new RegExp(`(id="${appendTagId}"[^>]*>)`);
const templateStr = fs.readFileSync(templatePath, 'utf8');
pageConfigs.forEach(data => {
  // 讀取目標文件內容
  let fileContent = templateStr;

  // 替換 title 標籤
  if (data.title) {
    fileContent = fileContent.replace(/<title>.*<\/title>/, `<title>${data.title}</title>`);
  }

  // 替換 meta name="keywords" 標籤
  if (data.keywords) {
    fileContent = fileContent.replace(/name="keywords"[^>]+/, `name="keywords" content="${data.keywords}"`);
  }

  // 替換 meta name="description" 標籤
  if (data.description) {
    fileContent = fileContent.replace(/name="description"[^>]+/, `name="description" content="${data.description}"`);
  }

  // 插入自定義 html 標籤
  if (data.html) {
    fileContent = fileContent.replace(appendTagRegex, `$1\n${data.html}`);
  }

  // 插入 content 標籤
  if (data.content) {
    const contentTags = data.content.map(contentConf => typeof contentConf === 'object' ? `\n<${contentConf.tag}>${contentConf.text}</${contentConf.tag}>` : `\n<div>${content}</div>`).join('');
    fileContent = fileContent.replace(appendTagRegex, `$1${contentTags}`);
  }

  // 插入 a 標籤,設置超鏈接跳轉
  if (data.link) {
    const aTags = data.link.map(linkConf => `\n<a target="_blank" href="${linkConf.href}">${linkConf.text}</a>`).join('');
    fileContent = fileContent.replace(appendTagRegex, `$1${aTags}`);
  }

  // 插入 img 標籤
  if (data.img) {
    const imgTags = data.img.map(imgUrl => `\n<img src="${imgUrl}"/>`).join('');
    fileContent = fileContent.replace(appendTagRegex, `$1${imgTags}`);
  }

  // 將修改後的內容寫入目標文件
  const targetPath = path.join(outputBasePath, data.path);
  fs.mkdirSync(targetPath.substring(0, targetPath.lastIndexOf('/')), { recursive: true });
  fs.writeFileSync(targetPath, fileContent, 'utf8');

  console.log(`SEO 入口文件生成:${targetPath}`);
});

其他優秀工具推薦

  • tinypng-script 圖片壓縮工具

    • 單文件,單命令完成項目圖片視覺無損壓縮
    • 自動跳過已壓縮文件,腳本運行快

  • iconfont 在線預覽工具

    • 支持解析本地 ttf 文件,支持解析在線阿里圖標資源鏈接

  • Nginx 在線配置調試工具

    • Nginx 學習,Nginx 在線配置調試神器,解析各命令運行過程

  • git 特定文件夾下載工具

    • 複製目標頁面鏈接,直接下載 GitHub 「特定」文件夾或文件(支持特定分支或 tag)。解決 GitHub 只能下載整個項目的問題

user avatar tianmiaogongzuoshi_5ca47d59bef41 Avatar zaotalk Avatar freeman_tian Avatar kobe_fans_zxc Avatar razyliang Avatar febobo Avatar woniuseo Avatar yixiyidong Avatar libubai Avatar xw-01 Avatar huangmingji Avatar ldh-blog Avatar
Favorites 34 users favorite the story!
Favorites

Add a new Comments

Some HTML is okay.