logo

Samiti.dev

สร้าง sitemap ติดSEOรูปแบบใหม่กับ Next.js 13 ด้วย App Router

Sitemap เพื่อ SEO

ทางเลือกที่ดีที่สุดสำหรับการแจ้งเกิดเว็บไซต์ใหม่ เพื่อติดต่อกับ Google สำหรับเว็บที่ต้องการผลลัพธ์ของ SEO

คือการจัดทำ sitemap เปรียบเสมือนเครื่องชี้ทางให้ Google วิ่งตาม URL ภายในเว็บได้อย่างสะดวก

และยิ่งสะดวกเข้าใจง่ายแก่ Bot ที่วิ่งมาเมื่อไร ก็ทำให้เว็บไซต์ของเราได้รับการบันทึกเร็วเท่านั้น

ส่วนหน้าตาหลักของ Sitemap ก็คือไฟล์ที่ชื่อว่า sitemap.xml อาจถูกสร้างด้วย RSS หรือ Atom หรือไฟล์ข้อความธรรมดาก็ได้ทั้งนั้น

ภายในจะเป็นการให้ข้อมูลความสัมพันธ์ต่างๆ ของหน้าเว็บ วิดีโอ บทความ และไฟล์ข้อมูลอื่นๆ ที่เกี่ยวข้อง ซึ่งเมื่อใดก็ตาม

Bot ของทาง Google เจอไฟล์นี้ จะทำการวิ่งตรวจสอบตามที่ระบุได้เพื่อจัดทำดัชนีค้นหาต่อไป

ที่สำคัญคือ สิ่งที่เว็บมาสเตอร์ควรรู้ไว้ก็คือ เว็บที่ต้องการ Sitemap มากที่สุดคือ คือเว็บที่เพิ่งเกิดใหม่

เหตุผลเพราะ วัดจาก External Link หรือ ลิงก์เชื่อมโยงภายนอกจากแหล่งอื่น ยิ่งเรามีตรงนี้น้อย ยิ่งเป็นการยากที่ Googlebot จะค้นหาเราไม่เจอ

การทำงานมีเงื่อนไขสั้นๆ คือ กระโดดจาก A ไป B ดังนั้นหากเว็บเราเป็นที่พูดถึงหน่อย หรือไม่มีการวางลิงก์เลย ก็ยากที่จะถูกค้นพบ

วิธีเพิ่ม Sitemap

  1. พิมพ์ทุกอย่างด้วยตนเอง (Manual) โดยการสร้าง sitemap.xml ใน public folder และลงมือกรอกทุกอย่างตัวตนเอง
   <!-- public/sitemap.xml -->
   <xml version="1.0" encoding="UTF-8">
   <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
     <url>
       <loc>http://www.example.com/foo</loc>
       <lastmod>2021-06-01</lastmod>
     </url>
   </urlset>
   </xml>
  1. สร้างผ่านฟังชั่น getServerSideProps สำหรับ Next.js สำหรับวิธีนี้จะขอสาธิตการทำด้วย Next.js เวอร์ชั่นใหม่ App Router เพราะไฟล์ทุกอย่างที่สร้างมาแล้ว จะถูกเรนเดอร์บน Server ทั้งหมด ยกเว้นจะเพิ่มที่หัวข้อไฟล์ว่า 'use client' ทำให้การทำผ่าน getServerSideProps ไม่จำเป็นต้องใช้อีกต่อไป

ดังนั้นสำหรับปี 2563 ใน Next.js รุ่นปัจจุบันคือ 13 รองรับโครงสร้างไฟล์แบบ App Router ที่ช่วยลดระยะเวลาพัฒนาและเชื่อมต่อกับ Server ปลายทาง

สำหรับการเพิ่ม sitemap นั้นตัวอย่างด้านล่าง

import { MetadataRoute } from 'next'
 
export default function sitemap(): MetadataRoute.Sitemap {
  return [
    {
      url: 'https://example.com',
      lastModified: new Date(),
      changeFrequency: 'yearly',
      priority: 1,
    },
    {
      url: 'https://example.com/about',
      lastModified: new Date(),
      changeFrequency: 'monthly',
      priority: 0.8,
    },
    {
      url: 'https://example.com/blog',
      lastModified: new Date(),
      changeFrequency: 'weekly',
      priority: 0.5,
    },
  ]
}

ซึ่งสิ่งที่เพิ่มเติมเข้ามานั้นคือ MetadataRoute สำหรับรูปแบบการทำงานกับ TypeScript แต่หากเป็น JS ธรรมดาก็จะไม่ต้องนำเข้าดังกล่าว และไม่มี data types

หากใครต้องการทราง type ที่ return หรืออยากจัดวางเช่นไร สามารถดูได้ที่ code block ด้านล่างนี้

type Sitemap = Array<{
  url: string
  lastModified?: string | Date
  changeFrequency?:
    | 'always'
    | 'hourly'
    | 'daily'
    | 'weekly'
    | 'monthly'
    | 'yearly'
    | 'never'
  priority?: number
}>

ซึ่งเป็นเรื่องง่ายมาก เพียงแค่เราสร้าง object เก็บไว้ใน array แต่ระบุแต่ละ properties ที่ใช้งานประกอบไปด้วย url, lastModified, changeFrequency, priority

สำหรับเนื้อหา url /blog หากเราอัพเดตบ่อยก็ต้องเป็น weekly คือสัปดาห์หนึ่ง Update ที่ ซึ่งขึ้นอยู่กับการใช้งาน ถ้าหากไม่เลยก็ต้องการ never เป็นต้น

ดึงรายชื่อบทความทั้งหมดใน sitemap

เนื่องจากการดึง blog ในรูปแบบด้านบนที่กล่าวมายังไม่สมบูรณ์ เพราะบทความมีปลีกย่อยอีกหลายบทความ ที่ดึงจาก API ดังนั้น ตรงนี้จะเป็นการแสดงตัวอย่าง

โดยหลักการคือ

  1. เลือก Lib จากแหล่ง API ของเรา อาจจะเป็น sanity หรืออื่นๆ ที่เราทำขึ้นมาเอง

  2. ตั้งตัวแปรแบบ const แล้ว map ทีละบทความ

  3. นำส่งออกเป็น Array

หลักการคราวๆ ก็จะคล้ายด้านบน เพียงแต่ เพิ่มตัวแปรจากบทความ แกลลอรี่ หรือ routes อื่นๆ ที่เราเคยสร้างไว้

หลักการ loop บทความทั้งหมดใน sitemap

หากเราใช้ contentlayer หรือไฟล์ตระกูล .md เราสามารถดึงเข้ามาใช้งาน แล้วทำการ map ใช้เข้ากับ obj ของ sitemap ที่ต้อง return ค่า เช่น url lastModified priority หรือ changeFrequency เป็นต้น

  import { allPosts } from "@/.contentlayer/generated";
 
  const blog =  posts.map( (post) => ({
    url: `${baseUrl + post.url}`,
    lastModified: new Date(),
    priority: 1,
    changeFrequency: "monthly" as "monthly",
  }));

และเราอาจจะต้องการเพิ่มเติมการจัดเรียงก็สามารถเพิ่มหลักการได้ตามตัวอย่างด้านล่างนี้ โดยใช้ date-fns เพื่อนำวันที่ล่าสุดการขึ้นก่อน

  import { allPosts } from "@/.contentlayer/generated";
  import { compareDesc } from "date-fns";
 
  const posts = allPosts.sort((a, b) => 
      compareDesc(new Date(a.date), new Date(b.date))
  );
 
  const blog =  posts.map( (post) => ({
    url: `${baseUrl + post.url}`,
    lastModified: new Date(),
    priority: 1,
    changeFrequency: "monthly" as "monthly",
  }));
 

การนำ sitemap มาประยุกต์ใช้กับ sanity

import { MetadataRoute } from 'next'
import { getGallery, getProjects } from "@/sanity/sanity-utils";
 
const URL = "http://example.com";
 
export default async function sitemap(): MetadataRoute.Sitemap {
 
 const _galleries = await getGallery();
  const galleries = _galleries?.map(( gallery ) => ({
    url: `${URL}/gallery/${gallery.slug}`,
    lastModified: gallery.Date,
    changeFrequency: 'monthly',
    priority: 0.8,
  }));
 
  const _projects = await getProjects();
  const projects = projects?.map(( project ) => ({
    url: `${URL}/projects/${project.slug}`,
    lastModified: project.Date,
    changeFrequency: 'monthly',
    priority: 0.8,
  }));
 
  const routes = ["", "/showcase", "/about", "/contact"].map((route) => ({
    url: `${URL}${route}`,
    lastModified: new Date().toISOString(),
    changeFrequency: 'weekly',
    priority: 1,
  }));
 
  return [...routes, ...galleries, ...projects];
 
}
 

จากตัวอย่างด้านบนคือ การเขียน sitemap.xml แบบ Dynamic เรียกทุกหน้าและข้อมูลปลีกย่อยอื่นๆ มารวมที่แห่งเดียวกัน

ผ่านการ loop ทีละตัวนั้นเอง ทำให้ได้ข้อมูลที่ครบทั้งหมด โดยไม่เสียแรงการวางแบบมือ

ดังนั้นการมี sitemap ช่วยให้เราได้เปรียบ เมื่อต้องทำงานกับ SEO เพื่อให้ได้ผลอันดับติดหน้าแรก Google เพราะเป็นสิ่งจำเป็น

และยิ่งเป็น Next.js เวอร์ชั่นปัจจุบันก็ยิ่งสะดวก และรวดเร็วมากขึ้น เท่านี้ก็นำไปแข่งกับ WordPress ได้แล้วแบบสบายๆ