DNS预解析脚本

图例:
An image

# DNS 预解析

  • 通过在 html 文件的head标签里面添加 <link rel="dns-prefetch" href="//api.example.com"> 来实现 DNS 预解析

  • 可以减少 DNS 的查询时间:用户首次访问网页时,浏览器需要解析所有外部资源,所以可以提前进行 DNS 预解析,从而减少后续资源加载时的延迟

  • 加速首屏加载:可以有效减少页面加载的时间,尤其是首屏有资源需要加载的情况

# 实现思路

  • 扫描目录,提取 URL 域名:遍历打包产物路径下的所有的 .js.css.html 文件,使用正则表达式提取其中的 URL 域名

    • 可以设置一些需要排除的域名,因为扫描的结果会把reactlodash这些包的官网地址也扫描出来
  • 添加 link 标签到 HTML 文件中:扫描出的域名,映射成<link rel="dns-prefetch">标签,然后添加到打包产物目录下的所有 HTML 文件的 head 部分

# 实现代码

点击 展开/收起 jsx 代码
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';

// 获取当前模块的文件路径
const __filename = fileURLToPath(import.meta.url);

// 获取当前模块的目录路径
const __dirname = path.dirname(__filename);

// 扫描目录下的所有文件 得到其中的 url
function scanDistUrls(distPath) {
  const urlSet = new Set();
  const urlRegex = /https?:\/\/([a-zA-Z0-9.-]+)/g;

  // 定义要排除的域名或路径关键字
  const excludeList = [
    'w3.org', // 排除 w3.org
    'github.com', // 排除 GitHub
    'quilljs.com', // 排除 Quill
    'npms.io', // 排除 NPM
    'lodash.com', // 排除 lodash
    'html2canvas.hertzen.com', // 排除 html2canvas
    'openjsf.org', // 排除 openjsf
    'reactjs.org', // 排除 React
    'github.io', // 排除 GitHub
    'underscorejs.org', // 排除 Underscore
    'hertzen.com', // 排除 Hertzen
  ];

  // 递归扫描目录下的所有文件
  function scanDistFiles(distPath) {
    const files = fs.readdirSync(distPath);
    for (let file of files) {
      const filePath = path.join(distPath, file);
      const stats = fs.statSync(filePath);

      if (stats.isDirectory()) {
        scanDistFiles(filePath);
      } else if (
        file.endsWith('.js') ||
        file.endsWith('.html') ||
        file.endsWith('.css')
      ) {
        const content = fs.readFileSync(filePath, 'utf8');
        const urls = content.match(urlRegex);
        if (urls) {
          for (let url of urls) {
            if (!excludeList.some((keyword) => url.includes(keyword))) {
              urlSet.add(url);
            }
          }
        }
      }
    }
  }

  scanDistFiles(distPath);

  return Array.from(urlSet);
}

// 将得到的urls 映射成  <link rel="dns-prefetch">  添加到html中head标签里面
function addDnsPrefetchToHtml(distPath, urls) {
  const files = fs.readdirSync(distPath);
  for (let file of files) {
    const filePath = path.join(distPath, file);
    const stats = fs.statSync(filePath);

    if (stats.isDirectory()) {
      addDnsPrefetchToHtml(filePath, urls);
    } else if (file.endsWith('.html')) {
      let content = fs.readFileSync(filePath, 'utf8');
      const dnsPrefetchTags = urls
        .map((url) => `<link rel="dns-prefetch" href="${url}">`)
        .join('\n');
      // 插入到head标签里面
      content = content.replace(/<\/head>/i, `${dnsPrefetchTags}\n</head>`);
      fs.writeFileSync(filePath, content);
    }
  }
}

function main() {
  console.log(
    '--------------------------------------------------------------------------\n',
  );
  const quantanalysisPath = path.join(__dirname, 'quantanalysis');

  console.log('正在添加 dns-prefetch 到 html 文件中...\n');
  console.log(`获取 ${quantanalysisPath} 路径下所有css、js、html内的url...\n`);

  // 扫描 quantanalysis 目录
  const urls = scanDistUrls(quantanalysisPath);

  if (urls.length > 0) {
    console.log(`urls获取成功,开始添加 dns-prefetch 到 html 文件中\n`);
    console.log(urls);
    addDnsPrefetchToHtml(quantanalysisPath, urls);
    console.log('dns-prefetch 添加成功');
  } else {
    console.log('没有发现需要添加 dns-prefetch 的文件\n');
  }
  console.log(
    '--------------------------------------------------------------------------\n',
  );
}

main();