Nuxt.js 生成sitemap站点地图文件

Nuxt.js 生成sitemap站点地图文件

背景介绍

​ 使用nuxt框架生成静态文件支持SEO优化,打包之后需要生成一个 sitemap.xml 文件方便提交搜索引擎进行收录。官网有提供一个插件sitemap 但是如果是动态路由需要手动一个个配置比较麻烦,无法自动检索生成。所以自己编写一个生成 sitemap 模块

准备工作

创建nuxt项目,参考中文官网。安装JavaScript模板ejs工具

shell 复制代码
$ npm install ejs

相关网站

sitemap模块

项目根目录创建 modules 目录,以及对应文件,详细文件内容放在文末。

java 复制代码
├─modules
│  └─robots.ejs // robots模板
│  └─sitemap.js // 站点地图js
│  └─template.ejs //sitemap 模板

配置 nuxt.config.js

modules 数组增加以下内容 modules/sitemap 刚才自定义模块,excludes 需要排除的目录,hostname 站点域名

nuxt.config.js

js 复制代码
export default {
  ...省略
  // Modules: https://go.nuxtjs.dev/config-modules
  modules: [
    ...省略,
    ['modules/sitemap',
      {
        excludes: ['_nuxt', 'img'],
        hostname: 'https://www.example.com'
      }
    ],
  ],
}

执行命令生成静态资源

shell 复制代码
$npm run generate

打开项目根目录下dist(默认输出路径),会多出两个文件

java 复制代码
├─robots.txt
├─sitemap.xml

结果展示

官方示例 modules

编写自己的模块

模块就是函数。它们可以打包为 npm 模块或直接包含在项目源代码中。

nuxt.config.js

js 复制代码
export default {
  exampleMsg: 'hello',
  modules: [
    // Simple usage
    '~/modules/example',
    // Passing options directly
    ['~/modules/example', { token: '123' }]
  ]
}

modules/example.js

js 复制代码
export default function ExampleModule(moduleOptions) {
  console.log(moduleOptions.token) // '123'
  console.log(this.options.exampleMsg) // 'hello'

  this.nuxt.hook('ready', async nuxt => {
    console.log('Nuxt is ready')
  })
}

// REQUIRED if publishing the module as npm package
module.exports.meta = require('./package.json')

1) ModuleOptions

moduleOptionsmodules 这是用户使用数组传递的对象 。我们可以用它来定制它的行为。

顶级选项

有时,如果我们可以在注册模块时使用顶级选项会更方便 nuxt.config.js。这使我们能够组合多个选项源。

nuxt.config.js

js 复制代码
export default {
  modules: [['@nuxtjs/axios', { anotherOption: true }]],

  // axios module is aware of this by using `this.options.axios`
  axios: {
    option1,
    option2
  }
}

2) this.options

this.options:您可以使用此参考直接访问 Nuxt 选项。nuxt.config.js 这是分配有所有默认选项的用户内容 。它可用于模块之间的共享选项。

模块.js

js 复制代码
export default function (moduleOptions) {
  // `options` will contain option1, option2 and anotherOption
  const options = Object.assign({}, this.options.axios, moduleOptions)

  // ...
}

modules文件

modules/robots.ejs

ejs 复制代码
# robots.txt
User-agent: Baiduspider
Disallow:
User-agent: Sosospider
Disallow:
User-agent: sogou spider
Disallow:
User-agent: YodaoBot
Disallow:
User-agent: Googlebot
Disallow:
User-agent: Bingbot
Disallow:
User-agent: Slurp
Disallow:
User-agent: Teoma
Disallow:
User-agent: ia_archiver
Disallow:
User-agent: twiceler
Disallow:
User-agent: MSNBot
Disallow:
User-agent: Scrubby
Disallow:
User-agent: Robozilla
Disallow:
User-agent: Gigabot
Disallow:
User-agent: googlebot-image
Disallow:
User-agent: googlebot-mobile
Disallow:
User-agent: yahoo-mmcrawler
Disallow:
User-agent: yahoo-blogs/v3.9
Disallow:
User-agent: psbot
Disallow:
Disallow: /bin/
Disallow: /js/
Disallow: /img/
Sitemap: <%= hostname %>/sitemap.xml

modules/sitemap.js

js 复制代码
/**
 * @description 生成 sitemap robots 模块
 * @author 方圆百里
 * @time 2023年10月12日
 */

const path = require('path');
const fs = require('fs');
const ejs = require('ejs');
/**
 * @description 获取当前目录下载的所有路径 -同步
 * @author 方圆百里
 *
 * @param {String} dir 文件路径
 * @returns {Array} 返回路径数组
 */
const loadFiles = (dir) => {
  try {
    const data = fs.readdirSync(dir);
    return data;
  } catch (e) {
    console.error('获取目录路径异常', e)
    return undefined;
  }
}

/**
 * @description 获取文件信息
 * @author 方圆百里
 *
 * @param {String} dir 文件路径
 * @returns {Array} 返回路径数组
 */
const statFile = (full_path) => {
  try {
    const stat = fs.statSync(full_path);
    stat.path = full_path;
    return stat;
  } catch (e) {
    console.error('获取目录路径异常', e)
    return undefined;
  }
}

/**
 * @description 递归处理文件路径
 * @author 方圆百里
 *
 * @param {String} dir 文件路径
 * @param {String} list 文件信息数组
 * @returns {Array} 返回路径数组
 */
const handleFiles = (dir, list = [], excludes) => {
  // 1、加载当前目录下所有路径,包含文件夹和文件
  const data = loadFiles(dir);
  if (data) {
    data.forEach(item => {
      if (!excludes.includes(item)) {
        // 2、拼接绝对路径
        const absolutePath = path.join(dir, item)
        // 3、获取文件基本信息
        const stat = statFile(absolutePath);
        // 4、如果是文件,处理基本信息
        if (stat.isFile()) {
          list.push({
            size: stat.size,
            time: stat.ctime,
            ...path.parse(stat.path)
          })
        } else { // 5、目录递归进行处理
          handleFiles(stat.path, list, excludes);
        }
      }
    })
  }
  return list;
}

/**
 * @description 格式化日期
 * @author 方圆百里
 *
 * @param {Date} date 日期
 * @returns {String} 2023-10-12
 */
const formatYear = (date) => {
  // 获取年、月和日
  const year = date.getFullYear();
  const month = (date.getMonth() + 1).toString().padStart(2, '0'); // 月份从0开始,需要加1,同时确保两位数格式
  const day = date.getDate().toString().padStart(2, '0'); // 确保两位数格式
  // 格式化日期
  return `${year}-${month}-${day}`;
}

/**
 * @description 生成站点地图
 * @author 方圆百里
 *
 * @param {String} dist 打包后文件路径
 * @param {String} hostname 主机名称
 * @param {Array} excludes 排除路径
 *
 */
const generateSitemap = (dist, hostname, excludes) => {
  const data = handleFiles(dist, [], excludes)
  const set = new Set();
  for (var i = 0; i < data.length; i++) {
    const f = data[i];
    if (f.ext === '.html') {
      const relative = f.dir.replace(dist, "")
      if (relative) {
        const paths = relative.split(path.sep);
        let loc = hostname;
        for (var x = 1; x < paths.length; x++) {
          loc += "/" + paths[x];
        }
        set.add({
          loc: loc,
          time: formatYear(f.time)
        });
      }
    }
  }
  // 读取模板文件
  const template = fs.readFileSync('modules/template.ejs', 'utf-8');
  // 提供模板数据
  const datas = {
    urls: set
  };
  // 使用模板引擎渲染模板
  const renderedContent = ejs.render(template, datas);
  // 写入生成的文件
  fs.writeFileSync(path.join(dist, 'sitemap.xml'), renderedContent);
  console.log('sitemap.xml 生成成功!');

  const robotsRendered = ejs.render(fs.readFileSync('modules/robots.ejs', 'utf-8'), {
    hostname
  });
  // 写入生成的文件
  fs.writeFileSync(path.join(dist, 'robots.txt'), robotsRendered);
  console.log('robots.txt 生成成功!');
}
export default function ExampleModule(moduleOptions) {
  const dist = this.options.generate?.dir || 'dist'; // 打包输出路径
  const hostname = moduleOptions.hostname || 'https://www.example.com'; // 主机名称
  const excludes = moduleOptions.excludes || ['.nuxt']; // 排除路径
  console.log('打包输出路径:=====>', dist)
  console.log('主机名称:=====>', hostname)
  console.log('排除路径:=====>', excludes)

  this.nuxt.hook('generate:done', async generator => {
    // 这将在Nuxt生成页面之之后调用
    console.log('执行 generate 完成')
    generateSitemap(dist, hostname, excludes)

  })
}

// 将模块发布为npm包
module.exports.meta = require('../package.json')

modules/template.ejs

ejs 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:news="http://www.google.com/schemas/sitemap-news/0.9"
  xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:mobile="http://www.google.com/schemas/sitemap-mobile/1.0"
  xmlns:image="http://www.google.com/schemas/sitemap-image/1.1"
  xmlns:video="http://www.google.com/schemas/sitemap-video/1.1">
  <url>
    <% urls.forEach(function(item) { %>
       <loc><%= item.loc %></loc>
       <lastmod><%= item.time %></lastmod>
       <changefreq>monthly</changefreq>
       <priority>0.8</priority>
    <% }); %>
  </url>
</urlset>
相关推荐
REDcker1 小时前
浏览器端Web程序性能分析与优化实战 DevTools指标与工程清单
开发语言·前端·javascript·vue·ecmascript·php·js
nbwenren3 小时前
2026实测:Gemini 3 镜像站视觉能力实践——拍照原型图,一键生成 HTML+CSS 代码
前端·css·html
爱上好庆祝9 小时前
学习js的第五天
前端·css·学习·html·css3·js
前端老石人10 小时前
前端开发中的 URL 完全指南
开发语言·前端·javascript·css·html
jingqingdai312 小时前
别用正则格式化 HTML!我用 DOM 遍历实现零风险本地格式化,老项目重构效率直接拉满
前端·重构·html
a11177614 小时前
“像风之翼“无人机巡检平台仪表盘
前端·javascript·开源·html·无人机
a11177614 小时前
QQ 宠物(怀旧 开源)前端electron项目
前端·开源·html
ZC跨境爬虫14 小时前
跟着 MDN 学 HTML day_8:(高级文本语义标签+适配核心功底)
前端·css·笔记·ui·html
Dxy123931021614 小时前
HTML中的伪类详解:从基础到高级应用的全面指南
前端·html
Dxy123931021614 小时前
HTML中如何设置元素样式:从基础到进阶的完整指南
前端·html