All Articles

Gatsby Gallery组件

1. 需求

Gallery算是一个blog中非常基础的需求,看了下gatsby的插件库,貌似是有一个gatsby-theme-gallery。但是这个插件引入了theme概念,对我来说无用,而且我需求的并不是要一个site级别的gallery,而是一个post级别的gallery,所以不合用。还是自己做一个得了。

主要参考了:Creating a Custom Photo Gallery using Gatsby.js and CSS Grid。此外,我的gatsby使用的是alxshelepenok/gatsby-starter-lumen这个starter,技术上倒是无所谓用的是哪个starter,但文件夹结构是因应着这个starter的结构,所以姑且这里提下。

另外还有一个插件:browniebroke/gatsby-image-gallery,不过看了下用起来也蛮麻烦的,再说了。

关于gatsby的一些技术点,及如何制作自制插件之类的,会在另一篇博文里展开,这里就不提了。

2. 文件结构

这里按当前这篇post进行举例,来说明下大致上的post相关文件及gallery相关图片文件的存放位置。

root /
     | content /
               | posts /
                       | 2021 /
                              | 09 /
                                   | gallery-example.md  # 这是博文本身
     | static /
              | media /
                      | posts /
                              | 2021 /
                                     | 09 /
                                          | gallery-example /
                                                            | gallery / *.jpeg  # 将会展示在gallery内的图片
                                                            | *.jpeg            # 其他会在post内使用的图片
  • 这篇blog的slug:gallery-example
  • post本身的md文件还是按slug存放在:/content/posts/YYYY/MM/${slug}.md
  • post对应的图片是按slug存放在:/static/media/posts/YYYY/MM/${slug}/*.jpeg
  • 这里新添加一层gallery文件夹,里面的图片会被gallery插件使用,展示在post的EOF之后:/static/media/posts/YYYY/MM/${slug}/gallery/*.jpeg

大致上就这样,主要就是在media的post专属文件夹里新添加了一个gallery文件夹,里面的图片都会作为post的专属gallery展示在EOF之后。

这里展示一张不会在gallery里展出的图片,因为它是存放在post的media文件夹内,但在gallery文件夹之外的图片:/static/media/posts/2021/09/gallery-example/non-gallery.jpeg

non gallery

3. 代码改动

代码改动主要是下面几个:

gatsby-config.js                                 # 添加gatsby-plugin-image插件,该图片展示插件在lumen这个starter中不存在
gatsby/create-page.js                            # 在post-template.js使用的context中添加gallery参数,该值为static的media文件夹下post对应的gallery文件夹
src/types/gallery.js                             # 描述从graphql内查询出来的allFile数据结构,这会作为gallery参数传给Post
src/templates/post-template.js                   # 在该template的page query中根据create-page.js给的gallery参数,查询对应路径的图片文件信息allFile;PostTemplate的传入参数data里会有一个新的节点allFile,把它作为gallery参数传给Post
src/components/Post/Gallery/Gallery.js           # 组件实现js
src/components/Post/Gallery/Gallery.module.scss  # 组件style
src/components/Post/Gallery/index.js             # 组件index,直接引入Gallery.js
src/components/Post/Post.js                      # 判断Post新的传入参数gallery,如果里面有图片,则在Content后面追加Gallery组件

3.1 create-page.js

  createPage({
    path: edge.node.fields.slug,
    component: path.resolve('./src/templates/post-template.js'),
    context: { slug: edge.node.fields.slug, gallery: `media${edge.node.fields.slug}/gallery` }
  });

3.2 post-template.js

  query PostBySlug($slug: String!, $gallery: String!) {
    markdownRemark(fields: { slug: { eq: $slug } }) {
      ### 
      others
      ###
    }
    allFile(filter: { relativeDirectory: { eq: $gallery } }) {
      totalCount
      nodes {
        childrenImageSharp {
          gatsbyImageData
        }
      }
    }
  }
  return (
    <Layout title={`${postTitle} - ${siteTitle}`} description={metaDescription} socialImage={socialImageUrl} >
      <Post post={data.markdownRemark} gallery={data.allFile} />
    </Layout>
  );

3.3 Gallery.js

// @flow strict
import React from 'react';
import { GatsbyImage } from 'gatsby-plugin-image';
import styles from './Gallery.module.scss';
import type { GalleryData } from '../../../types/gallery';

type Props = {
  gallery: GalleryData
};

const Gallery = ({ gallery }: Props) => {
  const images = [];
  // eslint-disable-next-line no-restricted-syntax
  for (const [index, node] of gallery.nodes.entries()) {
    const imgKey = `gallery-img-${index}`;
    const sharp = node.childrenImageSharp[0];
    if (sharp) {
      const image = sharp?.gatsbyImageData;
      if (image) {
        images.push(
          <GatsbyImage key={imgKey} alt={imgKey} image={image} />
        );
      }
    }
  }

  if (images.length > 0) {
    return (
      <div className={styles['gallery']}>
        {images}
      </div>
    );
  }
  return null;
};

export default Gallery;

3.4 Post.js

  <div className={styles['post__content']}>
    <Content body={html} title={title} />
    {galleryNode}
  </div>

EOF