Astro搭建博客实现图片懒加载和分页

679 阅读4分钟

前言:

最近想着是能自己写一个博客的,不仅可以练练手,也可以提高一下自己的前端技术,参照了现在主流的框架,像Nextjs、Astro这两个是非常适合去开发这种静态博客的,因为这两种都是内置了对md文档的支持,我们只要专注页面就行了,后面还是选择了Astro框架来搭建博客,感觉Astro 还是很适合静态文档类的网站的,整个项目搭建也是很简单的,参照 Astro官网 就行,官网还有一个直接现成博客模板可以下载,直接在这个现成模板上开发也是可以的,搭建过程不讲述,整体框架可以参照下图,基本差不多都是这种结构,这里主要记录一下:在Astro中实现图片懒加载和分页的功能

image-20240430163854114.png

图片懒加载实现

实现方法:

  • 默认情况下,图片不会载入,而是将图片实际的 src 路径存储在 data-src 属性中。
  • 当用户滚动页面,并且图片接近视窗时,JavaScript 会动态地将 data-src 的值赋给 src 属性,触发图片的加载。
  • 加载后的图片可以从懒加载图片的数组中删除,这样就不会再次被检查或加载。
  • 当所有懒加载的图片都被加载后,事件监听器可以被移除,以减少不必要的性能开销。

效果图:

Screenity video - May 2, 2024 (1).gif

具体代码:

 // 图片懒加载功能
  document.addEventListener("DOMContentLoaded", function () {
    let lazyImages = [].slice.call(document.querySelectorAll("img.lazyload"));
    let active = false;

    const lazyLoad = function () {
      if (active === false) {
        active = true;

        setTimeout(function () {
          lazyImages.forEach(function (lazyImage) {
            if (
              lazyImage.getBoundingClientRect().top <= window.innerHeight &&
              lazyImage.getBoundingClientRect().bottom >= 0 &&
              getComputedStyle(lazyImage).display !== "none"
            ) {
              lazyImage.src = lazyImage.dataset.src;
              lazyImage.classList.remove("lazyload");

              lazyImages = lazyImages.filter(function (image) {
                return image !== lazyImage;
              });

              if (lazyImages.length === 0) {
                document.removeEventListener("scroll", lazyLoad);
                window.removeEventListener("resize", lazyLoad);
                window.removeEventListener("orientationchange", lazyLoad);
              }
            }
          });

          active = false;
        }, 200);
      }
    };
    lazyLoad(); // 这里注意下,加载完成时要调用一次
    document.addEventListener("scroll", lazyLoad);
    window.addEventListener("resize", lazyLoad);
    window.addEventListener("orientationchange", lazyLoad);
  });

image-20240502105405562.png

分页实现

1、获取分页数据:

使用Astro中getStaticPaths方法获取分页的数据,Astro 会生成常见的分页属性,包括上一页/下一页链接、总页数等。我们可以用 paginate() 函数根据数组值生成这些页面:[...page].astro

---
import { getCollection, type CollectionEntry } from "astro:content";
import Layout from "../layouts/layout.astro";
import _ from "lodash";
import HomePage from "../pages/home/index.astro";

import type { FrontMatter } from "@/types/posts";

/**
 * 此函数基于分页生成所有博客文章的静态路径。
 * 它首先从 "blog" 集合获取所有博客文章,
 * 然后使用lodash的orderBy函数按日期降序排序文章列表。
 * 最后,它通过paginate函数将这个列表分页,
 * 每页显示10篇文章。
 *
 * @param {Object} params - 包含函数所需参数的对象。
 * @param {Function} params.paginate - 用于分页文章列表的函数。
 * @returns 分页的结果,它包含静态路径的列表等信息。
 */
export async function getStaticPaths({ paginate }) {
  // 从 "blog" 集合中获取所有博客文章
  const allBlogPosts = await getCollection("blog");

  // 将所有文章按文章的date属性进行降序排序
  const articleList = _.orderBy(
    allBlogPosts,
    // 使用文章的'date'属性作为排序依据
    [(article) => new Date(article.data.date)],
    // 指定为降序排序
    ["desc"]
  );

  // 使用传入的paginate函数对文章列表进行分页,设定每页10篇文章
  return paginate(articleList, {
    pageSize: 10, // 指定每页的文章数量为10
  });
}
export interface Props {
  page: {
    data: CollectionEntry<"posts">[];
    start: number;
    end: number;
    size: number;
    total: number;
    currentPage: number;
    lastPage: number;
    url: { current: string; next?: string; prev?: string };
  };
}
// 所有分页数据都在 "page" 参数中传递
// 当你使用 paginate() 函数时,每个页面将通过 page 传递数据。page 有很多有用的属性,下面列出最为重要的:
// page.data - 数组,包含你传递给 paginate() 函数的页面数据片段
// page.url.next - 下一个页面的链接
// page.url.prev - 上一个页面的链接
const { page } = Astro.props;

// console.log(page,'page...')

const frontmatter: FrontMatter = {
  title: "Nick Blog 首页",
  description: "使用了Astro4搭建的一个个人博客",
  author: "NickYang",
  date: new Date(),
  keywords: "Astro4.0 , 个人博客,快速的前端开发框架",
  cover: "/images/bg/bg1.jpg",
};
---

<Layout header="home" frontmatter={frontmatter}>
  <HomePage pagination={page} />
</Layout>

2、数据渲染:

我们将数据通过props传递到home组件中,然后就可以进行渲染了~

Astro代码:

---
import { Image, Picture } from "astro:assets";
import dayjs from "dayjs";
import _ from "lodash";
import "./index.css";

const { pagination } = Astro.props;

const currentPage = pagination?.currentPage || 0;
const totalPages = pagination?.lastPage || 0;
const url = pagination?.url;

const pageArray = Array.from({ length: totalPages }, (_, i) => i + 1);
const numRegex = /\d+/;
---

渲染数据:

image-20240506100742854.png

实现分页:

 <!-- 分页 -->
  <nav id="pagination">
    <div class="pagination">
      <!-- 上一页 -->
      {
        url?.prev && (
          <a class="extend next" rel="next" href={url?.current?.prev}>
            <i class="fas fa-chevron-left fa-fw" />
          </a>
        )
      }
      {
        totalPages > 1 &&
          pageArray.map((page) => {
            let targetUrl = url.current;
            let currentUrl = url.current;
            if (currentUrl == "/") {
              currentUrl = "";
            }
            if (numRegex.test(currentUrl)) {
              if (page == 1) {
                targetUrl = currentUrl.replace(numRegex, "");
              } else {
                targetUrl = currentUrl.replace(numRegex, page.toString());
              }
            } else {
              targetUrl = `${currentUrl}/${page}`;
            }
            return (
              <a
                class={`page-number ${currentPage == page && "current"}`}
                href={targetUrl}
                data-astro-prefetch
              >
                {page}
              </a>
            );
          })
      }
      <!-- 下一页 -->
      {
        url?.next && (
          <a class="extend next" rel="next" href={url.next} data-astro-prefetch>
            <i class="fas fa-chevron-right fa-fw" />
          </a>
        )
      }
    </div>
  </nav>

效果预览:

今天的截屏.gif

image-20240506101704983.png

总结:

整个框架是基于最新的Astro4.0,现在目前只完成了首页页面和文章内容页面的显示,还有很多的功能都没有去实现,像个人页面、归档等页面,后期再慢慢完善哈~