使用 Frontity 实现相关文章功能

frontity related posts

本篇文章将继续介绍使用 Frontity 增加新的网站功能,这次要实现的功能是经常会用到的如何显示一篇文章的相关文章。

首先还是要增加一个 Handler,用来异步获取相关文章列表。

sample-frontity-project/packages/frontity-chakra-theme/src/components/handler/post-related-archive.js

const PostRelatedArchive = {
    pattern: "/related-posts/:typ/:pid(\\d+)",
    name: "related post archive",
    priority: 10,
    func: async ({ link, params, state, libraries, force }) => {
      const { api, populate, parse, getTotal, getTotalPages } = libraries.source;
      const { page, query, route } = parse(link);
      //从状态中获取当前post对象
      const post = state.source[params.typ][params.pid];
      var cats = [4, 36, 118];  //限定目录
      var tags= [];
      var endpoint = params.typ + "s";
      post.format=="gallery"?cats = post.categories.filter(function (item) {return item!=3;}):tags = post.tags; //过滤掉类型是画廊的文章
      const response = await api.get({
        endpoint: endpoint,
        params: {
          _embed: true,
          page,
          exclude: post.id,  //不包含当前的post
          categories: cats,
          tags: tags,
          orderby: 'rand',
          order: "desc",
           ...state.source.params
        },
      })
      const items = await populate({
        response, state, force,
      });
      if (page > 1 && items.length === 0) throw new ServerError(`post archive doesn't have page ${page}`, 404);
      const total = getTotal(response, items.length);
      const totalPages = getTotalPages(response, 0);
      const hasNewerPosts = page < totalPages;
      const hasOlderPosts = page > 1;

      const getPageLink = (page) =>
        libraries.source.stringify({
        route,
        query,
        page,
      });
      const currentPageData = state.source.data[link];
      const newPageData = {
        type: params.typ,
        items,
        total,
        totalPages,
        isArchive: true,
        isPostArchive: true,
        isPostTypeArchive: true,
        ...(hasOlderPosts && { previous: getPageLink(page - 1) }),
        ...(hasNewerPosts && { next: getPageLink(page + 1) }),
      };
      Object.assign(currentPageData, newPageData);
    }
}

export default PostRelatedArchive;

第二步显示相关文章列表,使用了ChaKra SimpleGrid 组件。

sample-frontity-project/packages/frontity-chakra-theme/src/components/post/post-related.js

import React, {useState, useEffect} from "react";
import { connect } from "frontity"
import { Box, SimpleGrid, SkeletonCircle, SkeletonText, Flex, Heading, Stack, Divider } from "@chakra-ui/react";
import Image from "@frontity/components/image";
import { formatPostData } from "../helpers";
import Link from "../link";
import {displayTextString} from "../text-string"

const PostRelated = ({state, actions, postType, postId, ...rest}) => {
    const [isReady, setIsReady] = useState(false);
    const link = `/related-posts/${postType}/${postId}`;  //构造Url
    const data = state.source.get(link);

    useEffect( () => {
            //如果数据没准备好,从WordPress中获取
            if(!data.isReady) actions.source.fetch(link);
            if(data.isReady) setIsReady(true);
        }
    , [data.isReady]);
    if(data.is404) return null;
    return (
        <>
            {isReady ?(
            <>
            <Divider borderBottom="1px solid" my="20px" />
            <Stack p="5">
                <Heading fontSize="x-large" as="h6" textTransform="uppercase">{displayTextString(0, "relatedPost")}</Heading>
                <SimpleGrid  columns={{ base: 1, md: 5 }} spacing={1} >
                    {data.items && data.items.map(({ type, id }) => {
                        const item = state.source[type][id];
                        const datafmt = formatPostData(state, item);
                        const { title, featured_media, link } = datafmt;
                        const { src, alt, srcSet } = featured_media;
                        return (
                        <Flex
                            direction="column"
                            position="relative"
                            bg="white"
                            as="article"
                            key={item.id}
                            {...rest}
                        >
                            {featured_media && featured_media.src && (
                              <Link link={link}>
                                <Box
                                    role="group"
                                    cursor="pointer"
                                    height="150px"
                                    width="100%"
                                    pos="relative"
                                >
                                    <Box
                                        as={Image}
                                        width="900"
                                        height="550"
                                        position="absolute"
                                        boxSize="100%"
                                        objectFit="cover"
                                        top="0"
                                        left="0"
                                        maxWidth="100%"
                                        src={src}
                                        alt={alt}
                                        srcSet={srcSet}
                                    />
                                </Box>
                              </Link>
                            )}
                      
                            <Flex p="10px" flexGrow="1" direction="column">
                              <Heading fontSize="small" as="h6" textTransform="uppercase">
                                <Link link={link} dangerouslySetInnerHTML={{ __html: title }}></Link>
                              </Heading>
                            </Flex>
                        </Flex>
                        );
                    })}
                </SimpleGrid>
                </Stack>
                </>
            ):(<Stack p="5">{/* 显示过渡动画 */}
                    <SkeletonCircle size="50" />
                    <SkeletonText mt="10" noOfLines={5} spacing="8" />
                </Stack>
            )}
        </>
    );
}

export default connect(PostRelated)

最后在显示文章界面的组件中加入相关文章组件。

sample-frontity-project/packages/frontity-chakra-theme/src/components/post/post.js

import { Box, Divider } from "@chakra-ui/react";
import { connect, styled } from "frontity";
import React, { useEffect } from "react";
import List from "../archive";
import useScrollProgress from "../hooks/useScrollProgress";
import { LightPatternBox } from "../styles/pattern-box";
import Section from "../styles/section";
import AuthorBio from "./author-bio";
import FeaturedMedia from "./featured-media";
import PostHeader from "./post-header";
import PostProgressBar from "./post-progressbar";
import { getPostData, formatPostData } from "../helpers";
import PostRelated from "./post-related";
import Comments from "../comments";

const Post = ({ state, actions, libraries }) => {
  const postData = getPostData(state);
  const post = formatPostData(state, postData);

  // Get the html2react component.
  const Html2React = libraries.html2react.Component;

  // Once the post has loaded in the DOM, prefetch both the
  // home posts and the list component so if the user visits
  // the home page, everything is ready and it loads instantly.
  useEffect(() => {
    actions.source.fetch(`@comments/${postData.id}`);
    //相关文章预取
    actions.source.fetch(`/related-posts/${postData.type}/${postData.id}`);
    actions.source.fetch("/");
    List.preload();
  }, []);

  const [ref, scroll] = useScrollProgress();

  // Load the post, but only if the data is ready.
  if (!postData.isReady) return null;

  return (
    <LightPatternBox showPattern={state.theme.showBackgroundPattern} ref={ref}>
      <Box pb={{ base: "2rem", lg: "50px" }}>
        <PostHeader
          mt={{ base: "20px", lg: "4rem" }}
          px={{ base: "32px", md: "0" }}
          categories={post.categories}
          tags={post.tags}
          heading={post.title}
          author={post.author}
          date={post.publishDate}
          isPage={postData.isPage}
        />
      </Box>

      {!postData.isPage && <PostProgressBar value={scroll} />}

      {/* Look at the settings to see if we should include the featured image */}
      <Section bg="white" pb="80px" size="lg">
        {post.featured_media != null && (
          <FeaturedMedia id={post.featured_media.id} />
        )}

        {/* Render the content using the Html2React component so the HTML is processed
       by the processors we included in the libraries.html2react.processors array. */}
        <Content
          as={Section}
          px={{ base: "32px", md: "0" }}
          size="md"
          pt="50px"
        >
          <Html2React html={post.content} />
        </Content>

        <Comments postId={postData.id}></Comments>
        {/* 调用相关文章显示组件 */}
        <PostRelated postType={postData.type} postId={postData.id} />
      </Section>
    </LightPatternBox>
  );
};

export default connect(Post);
frontity related posts
Frontity 显示相关文章

发表评论

邮箱地址不会被公开。 必填项已用*标注