Gatsby의 Sanity에서 앵커 링크

TL;DR version is make sure you implement onRouteUpdate and shouldUpdateScroll in gatsby-browser.js.



그렇다면 앵커 링크는 무엇입니까?



앵커 링크는 HTML에서 동일한 페이지 내에서 탐색하는 방법입니다. 그것들을 생각하는 가장 쉬운 방법은 목차 또는 페이지의 책갈피와 같습니다. # 형식의 헤더 태그가 있는 마크다운 페이지에서 자주 사용되는 앵커를 볼 수 있습니다. 이제 일반 헤더 태그가 링크를 가지려면 다음과 같이 프런트 엔드에서 링크 태그로 래핑해야 합니다. <a href="#anchor"><h2>Headline Link</h2></a> . 이 페이지의 코드를 검사하면 블로그가 마크다운으로 작성되고 HTML로 변환되는 것과 같은 예를 볼 수 있습니다.

Sanity.io



이것이 Sanity.io와 어떻게 작동합니까?



Sanity은 헤드리스 콘텐츠 기반 CMS입니다. rich text editor 을 작성하면 portable text 이 생성됩니다. 따라서 마크다운과 달리 헤더# 항목을 변환할 필요는 없지만 휴대용 텍스트를 Gatsby가 이해할 수 있는 것으로 직렬화해야 합니다. Sanity.io를 사용하여 사이트를 만드는 방법에 대해 너무 깊이 파고들지는 않겠습니다.

훌륭한 가이드 Sanity Gatsby 블로그 확장



Sanity.io의 Gatsby 는 빠르게 시작하고 실행하는 방법에 대한 훌륭한 출발점입니다. 이를 사용한 다음 원하는 대로 기능을 확장할 수 있습니다. 예제에는 아래와 비슷한 게시물 파일이 있습니다. 우리가 정말로 신경 쓰는 것은 gatsby-source-sanity 행입니다.

import React from "react";
import PortableText from "./portableText";
import Card from "../Card";

export default (props) => {
  const { _rawBody, authors, categories } = props;
  return(
  <article className="flex flex-col w-full max-w-xl md:max-w-1xl md:max-w-2xl lg:max-w-3xl xl:max-w-6xl m-2 md:max-m-8 md:max-m-8 lg:max-m-8 xl:m-8">
    <div className="w-full">
    <Card {...props} />
    </div>
    <section className="markdown bg-white w-full rounded mt-4 p-4">
    <div>{_rawBody && <PortableText blocks={_rawBody} />}</div>
    <div>
      <aside>
        {categories && (
          <div>
            <h3>Categories</h3>
            <ul>
              {categories.map(category => (
                <li key={category._id}>{category.title}</li>
              ))}
            </ul>
          </div>
        )}
      </aside>
    </div>
    </section>
  </article>
  )}



이것은 blog example 을 사용하는 간단한 반응 구성 요소입니다. 여기서 가장 중요한 부분은 직렬 변환기를 허용하고 Sanity.io의 graphql에서 수신할 블록 기반 PortableText에 많은 사용자 정의를 추가할 수 있다는 것입니다.

import React from "react";
import clientConfig from "../../../client-config";
import BasePortableText from "@sanity/block-content-to-react";
import serializers from "../graphql/serializers";

const PortableText = ({ blocks }) => {
  return(
  <BasePortableText
    blocks={blocks}
    serializers={{...serializers}}
    {...clientConfig.sanity} />
  )
};

export default PortableText;



@sanity/block-content-to-react Sanity.io 직렬 변환기



직렬 변환기의 가장 큰 장점은 Sanity.io에서 오는 다양한 유형을 처리하려는 사용자 지정 React 구성 요소를 제공할 수 있다는 것입니다.

import React from "react";
import Figure from "./Figure";
import Code from "./Code";
import Img from "./Img";
import Block from "./Block";

const serializers = {
  types: {
    authorReference: ({ node }) => <span>{node.author.name}</span>,
    mainImage: Figure,
    code: Code,
    img: Img,
    block: Block
  },
};

export default serializers;



재미있고 간단한 예는 <div>{_rawBody && <PortableText blocks={_rawBody} />}</div>입니다. 이 인라인을 많이 추가할 수 있지만 이미지 조작을 사용하여 이미지에 원하는 대로 효과를 적용할 계획입니다. 그래서 노드를 가져와 해당 대체 텍스트가 있는 간단한 img 태그를 출력하는 img라는 간단한 구성 요소를 추가했습니다.

import React from "react";

export default ({ node }) => {
  const { asset } = node;
  return <img src={asset.src} alt={asset.alt} />;
};



이제 portableText와 함께 나타나는 모든 Img 유형 항목에 대해서도 마찬가지입니다. 우리는 Sanity.io의 멋진block을 사용하고 있기 때문에 여기에서 실제로 많은 작업을 수행할 필요는 없지만 다시 한 번 게으른 개발자이므로 모든 제목에 앵커 태그가 연결되도록 만들고 싶지만 portableText는 아래와 같이 보입니다. :그래서 이를 실현하기 위해 우리는 위에 Sanity.io가 훌륭한 설정 방법cloudinary's을 가지고 있는 직렬 변환기@sanity/block-content-to-react를 추가했습니다. My Block은 매우 유사해 보이지만 이러한 각 제목(현재는 h2 및 h3) 내부에 Gatsby Link 태그를 설정하고 있습니다.

import React from "react";
import { IoMdLink } from "react-icons/io/";
import { Link } from "gatsby";
const slugs = require(github-slugger)()

export default (props) => {
    slugs.reset();
    const style = props.node.style || 'normal';
    // If Heading Level add Anchor Link
    if (typeof location !== undefined && /^h\d/.test(style)) {
      const level = style.replace(/[^\d]/g, '')
      const slug = slugs.slug(props.children[0], false)
      switch (level) {
        case 'h3':
          return <h3 id={slug} className="flex">{props.children}<Link to={${location.pathname}#${slug}}><div className="py-1 pl-1 rotateIn"><IoMdLink /></div></Link></h3>
        default:
            return <h2 id={slug} className="flex">{props.children}<Link to={${location.pathname}#${slug}}><div className="py-1 pl-1 rotateIn"><IoMdLink /></div></Link></h2>
      }
    }

    return style === 'blockquote'
    ? <blockquote>{props.children}</blockquote>
    : <p>{props.children}</p>
};




예시 Gatsby 앵커 링크 추가



이제 Knut Melvær는 프런트 엔드에 링크를 추가하는 방법을 자세히 설명하는 훌륭한 가이드 를 가지고 있지만, 저는 상당히 게으른 개발자이고 모든 앵커 링크를 수동으로 선택하고 추가하고 싶지 않습니다. 그래서 위의 방법을 사용했습니다. Internal and external links을 사용하는 마크다운 파일을 사용하여 동일한 접근 방식을 만들 수 있습니다. 내가 찾은 한 가지 누락된 부분은 Gatsby에서 페이지 내의 올바른 위치로 스크롤하는 것입니다. 이를 위해 Gatsby는 몇 가지 멋진 기능gatsby-remark-autolink-headers을 제공합니다. 페이지가 처음 로드될 때 스크롤이 발생하려면 block: Block를 사용해야 합니다. 이렇게 하면 위치를 사용하고 앵커 링크인 onRouteUpdate의 존재를 확인할 수 있습니다. 또한 내부 링크를 선택해도 경로 업데이트가 트리거되지 않으므로hash 구현했습니다. 따라서 이는 refresh.gatsby-browser.js 없이 필요했습니다.

/**
 * Implement Gatsby's Browser APIs in this file.
 *
 * See: https://www.gatsbyjs.org/docs/browser-apis/
 */

// You can delete this file if you're not using it

exports.onRouteUpdate = ({location}) => {
  anchorScroll(location);
  return true;
};
exports.shouldUpdateScroll = ({
  routerProps: { location },
  getSavedScrollPosition
}) => {
  anchorScroll(location);
  return true;
}

function anchorScroll(location){
  // Check for location so build does not fail
  if (location && location.hash) {
    setTimeout(() => {
      // document.querySelector(${location.hash}).scrollIntoView({ behavior: 'smooth', block: 'start' });
      const item = document.querySelector(${location.hash}).offsetTop;
      const mainNavHeight = document.querySelector(nav).offsetHeight;
      window.scrollTo({top: item - mainNavHeight, left: 0, behavior: 'smooth'});
    }, 0);
  }
}



브라우저 API 최종 결과



새로 고침 및 내부 링크 클릭 시 멋진 부드러운 스크롤 화면.

좋은 웹페이지 즐겨찾기