종속성 없이 몇 분 만에 웹사이트에 대한 가장 빠른 검색을 생성하세요 ⚡🔎

17271 단어 javascriptreact
내가 작업하고 있는 프로젝트는 Gatsby JS로 작성되었지만 솔루션 자체는 바닐라 리액트이며 모든 곳에서 작동합니다.

Jump to main content

The project I'm working on is written in Gatsby JS, but the solution itself is vanilla react and will work everywhere.



오늘 저는 my blog 업데이트에 대부분의 시간을 할애했으며 검색, 태그, MDX 지원 및 사이드바를 포함한 몇 가지 디자인 변경과 같은 더 많은 기능을 추가할 생각을 했습니다.

검색 기능을 구현하는 방법을 결정하고 있었습니다. 검색 기능을 수행한 유일한 시간은
Typesense의 자체 호스팅 버전

하지만 음, 서버 측을 호스트하는 데 비용이 많이 들었습니다.
블로그처럼 단순한 것. 호스팅 솔루션도 가격면에서 그다지 좋지 않습니다.

따라서 한 가지 확실한 것은 이를 위해 API를 사용할 필요가 없다는 것입니다. 빠른 Google 검색 후 Gatsby의 웹사이트에서 adding search to Gatsby에 관한 이 문서를 발견했습니다.

해당 가이드의 Client Side 섹션에서 권장 사항은 다음과 같습니다.

타사 솔루션 없이도 Gatsby 사이트에서 모든 작업을 수행할 수 있습니다. 여기에는 약간의 코드 작성이 포함되지만 더 적은 서비스를 사용합니다. 인덱싱할 콘텐츠가 많으면 번들 크기도 크게 증가할 수 있습니다.

이를 수행하는 한 가지 방법은 js-search 라이브러리를 사용하는 것입니다.

Adding Search with JS Search

이를 지원하는 두 개의 Gatsby 플러그인이 있습니다.
gatsby-plugin-elasticlunr-searchgatsby-plugin-local-search
이제 이러한 검색 방법은 더 큰 번들 크기를 의미하는 모든 것을 인덱싱합니다. 그리고 그들은 또한 설정하기 번거롭습니다.

내가 함께 갔던 솔루션

Now for my use case, it was probably a good idea to just make something simple by myself, and I can build on it as I keep updating this blog.

The idea is really simple, I just need to make a search box, and on every keystroke, loop through the contents and filter them like that.

const BlogIndex = ({ data, location }) => {
  // These posts can be anything,
  // I've just used the posts from a gatsby query
    const posts = data.allMdx.edges;

  // We need to filter the posts by the search query.
  // by default, we have all posts
    const [filteredPosts, setFilteredPosts] = useState(posts);

  // This will be the search query
  const [search, setSearch] = useState('');

  return (
    <div>
      {/* Our search bar */}
      <input
        type="text"
        placeholder="Search"
        onChange={(e) => {
          e.preventDefault();
          setSearch(e.target.value)}
      }/>

      {/* Simply mapping through everything and rendering blogs */}
      {filteredPosts.map(({ node }) => {
        <BlogPost post={node} key={node.id} />
      }}
    </div>
  )
}

Now, whenever something is typed in the box, the search state will be updated. Now, let's write a useEffect hook to update the filteredPosts state whenever the search state changes.

const BlogIndex = ({ data, location }) => {

    const posts = data.allMdx.edges;
    const [filteredPosts, setFilteredPosts] = useState(posts);
  const [search, setSearch] = useState('');

  //highlight-start
  useEffect(() => {
    if (search) {
      // post filtering here
    }
  }
  // only update the filteredPosts state when the search state changes or the posts state changes
  , [search, posts]);
  ///highlight-end

  return (
    ... // rest of the code
  )

And now let's write some very simple code to filter the posts.

...

  if (search) {
    const filteredPosts = posts.filter(post => {
        //highlight-start
        const title = post.title.toLowerCase();
        const description = post.description.toLowerCase();
        return title.match(search.toLowerCase()) || description.match(search.toLowerCase());
        //highlight-end
    }
  }
  setFilteredPosts(filteredPosts);
...

Since my blog has tags and stuff like that, I added functionality to search and filter by tags, too

    if (search.startsWith("#")) {
      return tags.includes(search.replace("#", ""));
    }
...
And that's it! But wait, there's more. This works, but you can't really share a search query to someone else, I can share google links - google.com/search?q=github
공유해야 할 때 더 쉽고 편리하기 때문에 그게 좀 중요하다고 생각합니다.

그럼 실시간으로 검색어를 포함하도록 URL을 업데이트하겠습니다. 나는 전에 이것을 해본 적이 없었기 때문에 그것을 배우는 것이 좋았습니다. IFTTT search engine에서 영감을 얻었습니다.

기본적으로 브라우저 기록에 추가하거나 페이지를 다시 로드하지 않고 새 URL을 푸시할 수 있는 window.history.pushState() 방법에 대해 알게 되었습니다. 여기에서 동일한 문서를 읽으십시오 -
History API | MDN

useEffect(() => {
        if (search) {
      //highlight-start
            if (window.history.pushState) {
        window.history.pushState(null, null, `/?q=${search}`);
      }
      //highlight-end
      const filteredPosts = posts.filter(post => {
        const title = post.title.toLowerCase();
        const description = post.description.toLowerCase();
        return title.match(search.toLowerCase()) || description.match(search.toLowerCase());
    }
  }
  setFilteredPosts(filteredPosts);
  }, [search]);


이제 window location 개체를 사용하여 원래 요청을 구문 분석하고 useState에 대해 만든 search 후크의 기본값으로 설정해야 합니다.

                      // 👇🏻 converts the URL from HTML encoded to a string (%20 to space)
    const initialState = decodeURI(location.href? // Use window location
                      .split('/')               // Split the URL into an array
                      .pop()                    // Get the last element only
                      .split("=")               // at this point, it's q=search, so we only need the "Search" parth
                      .pop() );                 

                                        // 👇🏻 We're using the initialState to set the search query.
  const [search, setSearch] = useState(initialState); // Now, only the blogs that match the query will be displayed on first load 


그게 다야!



전체 구현은 source code of this blog on Github에서 찾을 수 있습니다.

당신은 할 수 있습니다

여기에서 이 블로그의 저장소를 자유롭게 방문하세요.

좋은 웹페이지 즐겨찾기