背景介绍
使用nuxt
框架生成静态文件支持SEO优化,打包之后需要生成一个 sitemap.xml
文件方便提交搜索引擎进行收录。官网有提供一个插件sitemap 但是如果是动态路由需要手动一个个配置比较麻烦,无法自动检索生成。所以自己编写一个生成 sitemap 模块
准备工作
创建nuxt
项目,参考中文官网。安装JavaScript
模板ejs工具
$ npm install ejs |
相关网站
- 插件sitemap: Introduction - Nuxt Sitemap Module
- 中文官网:Nuxt.js - Vue.js 通用应用框架 | Nuxt.js 中文网
- 英文官网:Nuxt 2 - The Intuitive Vue Framework
- JS模板工具ejs:GitHub - mde/ejs: Embedded JavaScript templates -- http://ejs.co
- 扩展模块:Nuxt 2 - Modules directory
sitemap模块
项目根目录创建 modules
目录,以及对应文件,详细文件内容放在文末。
├─modules | |
│ └─robots.ejs // robots模板 | |
│ └─sitemap.js // 站点地图js | |
│ └─template.ejs //sitemap 模板 |
配置 nuxt.config.js
在 modules
数组增加以下内容 modules/sitemap
刚才自定义模块,excludes
需要排除的目录,hostname
站点域名
nuxt.config.js
export default { | |
...省略 | |
// Modules: https://go.nuxtjs.dev/config-modules | |
modules: [ | |
...省略, | |
['modules/sitemap', | |
{ | |
excludes: ['_nuxt', 'img'], | |
hostname: 'https://www.example.com' | |
} | |
], | |
], | |
} |
执行命令生成静态资源
$npm run generate |
打开项目根目录下dist
(默认输出路径),会多出两个文件
├─robots.txt | |
├─sitemap.xml |
结果展示
官方示例 modules
编写自己的模块
模块就是函数。它们可以打包为 npm 模块或直接包含在项目源代码中。
nuxt.config.js
export default { | |
exampleMsg: 'hello', | |
modules: [ | |
// Simple usage | |
'~/modules/example', | |
// Passing options directly | |
['~/modules/example', { token: '123' }] | |
] | |
} |
modules/example.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
moduleOptions
:modules
这是用户使用数组传递的对象 。我们可以用它来定制它的行为。
有时,如果我们可以在注册模块时使用顶级选项会更方便 nuxt.config.js
。这使我们能够组合多个选项源。
nuxt.config.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
export default function (moduleOptions) { | |
// `options` will contain option1, option2 and anotherOption | |
const options = Object.assign({}, this.options.axios, moduleOptions) | |
// ... | |
} |
modules文件
modules/robots.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
/** | |
* @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
<?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> |