Tạo Sơ đồ trang web động (Dynamic Sitemap) với Next.js

2021-07-03

Để cải thiện Tối ưu hóa Công cụ Tìm kiếm (SEO), bạn có thể cần thêm sơ đồ trang web hoặc tệp robots.txt vào trang Next.js của mình.

** Sitemap ** xác định mối quan hệ giữa các trang trong trang web của bạn. Công cụ tìm kiếm (Google,Bing,Yandex...) sử dụng tệp này để lập chỉ mục chính xác hơn trang web của bạn. Bạn cũng có thể cung cấp thêm thông tin chẳng hạn như thời gian cập nhật lần cuối <lastmod>, tần suất thay đổi của trang <changefreq> và hơn thế nữa.

Tệp ** robots.txt ** cho công cụ tìm kiếm biết những trang hoặc tệp nào mà trình thu thập thông tin (Bots) có thể hoặc không thể yêu cầu từ trang web của bạn.

Sơ đồ trang web tĩnh (Static Sitemap)

Nếu trang web của bạn không cập nhật thường xuyên, bạn có thể sử dụng sơ đồ trang web tĩnh. Đây là tệp .xml cơ bản xác định nội dung trang web của bạn. Ví dụ đơn giản:

<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
    <url>
        <loc>https://callmetony.com</loc>
    </url>
    <url>
        <loc>https://callmetony.com/blog</loc>
    </url>
</urlset>

Khi trang web của bạn mở rộng quy mô, bạn nên tạo sơ đồ trang web động.

Sơ đồ trang web động (Dynamic Sitemap)

Nếu trang web của bạn thường xuyên thay đổi, bạn nên tạo một sơ đồ trang web động. Trước tiên, hãy xem xét một ví dụ trong đó nội dung trang web của bạn dựa trên tệp (ví dụ: trong thư mục /pages).

Đầu tiên, hãy thêm globby để chúng ta có thể tìm nạp danh sách routes.

$ yarn add --dev globby

Tiếp theo, chúng ta có thể tạo một tập lệnh Node tại scripts/generate-sitemap.js. Tệp này sẽ tự động tạo sơ đồ trang dựa trên thư mục /pages của bạn.

const fs = require('fs');
 
const globby = require('globby');
const prettier = require('prettier');
 
(async () => {
  const prettierConfig = await prettier.resolveConfig('./.prettierrc.js');
 
  // Ignore Next.js specific files (e.g., _app.js) and API routes.
  const pages = await globby([
    'pages/**/*{.js,.mdx}',
    '!pages/_*.js',
    '!pages/api'
  ]);
  const sitemap = `
        <?xml version="1.0" encoding="UTF-8"?>
        <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
            ${pages
              .map((page) => {
                const path = page
                  .replace('pages', '')
                  .replace('.js', '')
                  .replace('.mdx', '');
                const route = path === '/index' ? '' : path;
 
                return `
                        <url>
                            <loc>${`https://webcuaban.com${route}`}</loc>
                        </url>
                    `;
              })
              .join('')}
        </urlset>
    `;
 
  // If you're not using Prettier, you can remove this.
  const formatted = prettier.format(sitemap, {
    ...prettierConfig,
    parser: 'html'
  });
 
  fs.writeFileSync('public/sitemap.xml', formatted);
})();

Cuối cùng, ghi đè next.config.js của bạn để chạy tập lệnh này trong quá trình build. Tệp của bạn được tạo tại public/sitemap.xml, sau đó được chuyển đến Root trang web của bạn (yêu cầu Next v9.0 +).

module.exports = {
  webpack: (config, { isServer }) => {
    if (isServer) {
      require('./scripts/generate-sitemap');
    }
 
    return config;
  }
};

Nội dung bổ sung

Nếu bạn có dữ liệu được lưu trữ bên ngoài (ví dụ: CMS), bạn sẽ cần thực hiện yêu cầu API trước khi bạn có thể tạo sơ đồ trang web của mình. Việc triển khai này sẽ khác nhau tùy thuộc vào nguồn dữ liệu của bạn, nhưng ý tưởng giống nhau. Để chứng minh, tôi đã tạo một ví dụ bằng cách sử dụng dữ liệu trình giữ chỗ (placeholder data).

Đầu tiên, tạo một tệp mới tại pages/sitemap.xml.js:

import React from 'react';
 
class Sitemap extends React.Component {
  static async getInitialProps({ res }) {
    const request = await fetch(EXTERNAL_DATA_URL);
    const posts = await request.json();
 
    res.setHeader('Content-Type', 'text/xml');
    res.write(createSitemap(posts));
    res.end();
  }
}
 
export default Sitemap;

Khi route /sitemap.xml được tải ban đầu, chúng ta sẽ tìm nạp các bài đăng từ nguồn dữ liệu bên ngoài và sau đó ghi một tệp XML dưới dạng phản hồi.

import React from 'react';
 
const EXTERNAL_DATA_URL = 'https://jsonplaceholder.typicode.com/posts';
 
const createSitemap = (posts) => `<?xml version="1.0" encoding="UTF-8"?>
    <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
        ${posts
          .map(({ id }) => {
            return `
                    <url>
                        <loc>${`${EXTERNAL_DATA_URL}/${id}`}</loc>
                    </url>
                `;
          })
          .join('')}
    </urlset>
    `;
 
class Sitemap extends React.Component {
  static async getInitialProps({ res }) {
    const request = await fetch(EXTERNAL_DATA_URL);
    const posts = await request.json();
 
    res.setHeader('Content-Type', 'text/xml');
    res.write(createSitemap(posts));
    res.end();
  }
}
 
export default Sitemap;

Đây là ví dụ về kết quả đầu ra.

<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
    <url>
        <loc>https://jsonplaceholder.typicode.com/posts/1</loc>
    </url>
    <url>
        <loc>https://jsonplaceholder.typicode.com/posts/2</loc>
    </url>
</urlset>

robots.txt

Cuối cùng, chúng ta có thể tạo một tệp tĩnh tại public/robots.txt để xác định tệp nào có thể được thu thập thông tin và vị trí của sơ đồ trang web.

User-agent: *
Sitemap: https://callmetony.com/sitemap.xml