【momo·实战指南】让next构造你的世界

少年,看到大佬们的个人博客,是否渴望拥有自己的博客?别担心,next会出手!

背景

  这段时间有去看大佬们的博客,看到的有大佬们对各种知识的理解,有大佬们积极乐观的人生态度和多彩的生活,博客不只是博客,还是大佬们的内心世界。作为一只momo小兵,俺也想构建俺滴一亩三分地------momo世界

  作为博客,我希望他能够尽量轻便,难道还得去特地进行前后端分离吗?不,不行,太累了~

  那有什么好的解决方案?这阵子不是流行一个next吗,走,咱瞧瞧!

  不看不知道,一看吓一跳!next在掘金的文章早在4年前就有,很多公司也在自己的项目中对next进行落地。

  围绕着快速开发的原则,momo世界使用next+tailwindcss+daisyui+gray-matter+next-mdx-remote+Docker进行构建部署。在开发过程nexttailwindcss的带来了很多方便,例如md获取、例如媒体查询适配。

要点解析

文章解析和渲染

大伙都喜欢使用markdown记录笔记,而从掘金搬运获取,获取的也是md格式的文章,因此,我的需求是解析md并添加相关参数。为了想要添加更多的参数,我使用了mdx作为文件名,实际上mdx非常强大。mdx官网是这么说的: 得益于next我们可以在项目中使用node相关模块的服务,例如在这我们就可以使用fs对文章进行获取:

js 复制代码
import path from "path";
import fs from "fs";
import matter from "gray-matter";
import { serialize } from "next-mdx-remote/serialize";
import rehypeHighlight from "rehype-highlight/lib";

// 定义文章信息
export interface FileSketch {
  id: string;
  title: string;
  date: string;
  filePath: string;
  description: string;
  tags: Array<string>;
  cover:string;
  [key: string]: any;
}

// 文件路径
export const postPath = path.join(process.cwd(), "/posts/computer");

// 获取全部文件信息
export const findMdxFiles = () => {
   // 获取文件信息的函数
  const findFileDetail = (
    dir = postPath,
    fileList = [] as Array<string>,
    sketchList = [] as Array<FileSketch>
  ) => {
    const files = fs.readdirSync(dir);

    files.forEach((file) => {
      const filePath = path.join(dir, file);
      const stat = fs.statSync(filePath);
      
      if (stat.isDirectory()) {
        // 递归处理子目录
        findFileDetail(filePath, fileList, sketchList);
      } else if (path.extname(file) === ".mdx") {
        // 找到 .mdx 文件,将其进行解析
        try {
          // 借助gray-matter解析mdx文件的二进制流
          const { data } = matter(fs.readFileSync(filePath, "utf-8"));
          sketchList.push({ ...data, filePath } as FileSketch);
        } catch (e) {
          // 埋点
        }
        fileList.push(filePath);
      }
    });

    return sketchList;
  };
  // 返回文件信息
  return findFileDetail();
};

// 寻找对应id的文章
export const getFileById = async (id: string) => {
  const [targetBlog] = findMdxFiles().filter((blog) => blog.id === id);
  const fileContent = fs.readFileSync(targetBlog.filePath, "utf-8");
  const { data: frontmatter, content } = matter(fileContent);
  // 读取文章
  const mdxSource = await serialize(content, {
    mdxOptions: {
      // 代码主题设置
      rehypePlugins: [rehypeHighlight],
    },
  });
  return {
    frontmatter,
    mdxSource,
    id,
  };
};

每次的生成,都会执行一次findMdxFiles方法,虽然查询的数量很少,虽然nodeio是友好的,但是不优化说不过去。从代码上,可以维护一个文章盒子,对外进行交流,具体实现这里不延申:

对于文章的渲染来说,next-mdx-remote可以对返回的mdx数据进行处理:

js 复制代码
import React from "react";
import { MDXRemoteSerializeResult, MDXRemote } from "next-mdx-remote";
import Image from "next/image";
type BlogContentType = {
  source: MDXRemoteSerializeResult;
};
// 我们可以根据自己爱好进行自定义样式,但是daisyui也给我们开启了排版服务,因此代码量显著减少。
const components = {
  img: (props: any) => (
    <Image
      alt={props.alt}
      width={150}
      height={50}
      layout="responsive"
      {...props}
    />
  ),
};

export default function BlogContent({ source }: BlogContentType) {
  return (
    <article className="w-full mx-auto my-5 prose lg:prose-xl">
       // source就是上面经过文章id获取文章后的信息,component是自定义组件
      {source && <MDXRemote {...source} components={components}></MDXRemote>}
    </article>
  );
}

更新、添加文章肿么办

next给我们提供了多种渲染方式,对于博客项目ssg也许是合适的。ssgbuild的过程中就对页面进行了内容生成。那么我们如果想要更新文章、添加文章,肿么办?答案很简单,看官网,设置参数,开启ISR

可以通过使用getStaticProps开启ssg,在动态路由文件中需要使用getStaticPaths获取需要生成的的路由。

getStaticPropsfallback属性有3种返回值类型:

  • false:仅仅会动态生成getStaticPaths给出的路径代表的页面,如果访问的路径不在给定范围内,就显示404界面
  • true:会动态生成getStaticPaths给出的路径代表的页面,如果访问其他路径,会拿到显示fallback版本,再进行页面生成
  • blocking:会动态生成getStaticPaths给出的路径代表的页面,如果访问其他路径,会有加载的loading,再进行页面生成

想要添加文章,只需要借助docker共享数据卷,共享到上文的文章获取的目录即可!

开启ISR,设置参数revalidate,值为时间,当到达时间间隔的时候,重新生成页面。

js 复制代码
import { getAllId, getFileById } from "@/utils/mdxUtil";
import { serializeUtil } from "@/utils";
import React from "react";
import SingleBlog from "@/component/Blogs/SingleBlog";
import { GetStaticPaths, GetStaticProps, InferGetStaticPropsType } from "next";
import { useRouter } from "next/router";

export default function SingBlog({
  source,
  formatter,
}: InferGetStaticPropsType<GetStaticProps>) {
  const router = useRouter()
  if(router.isFallback){
    return <div>
      加载中!
    </div>
  }
  return <SingleBlog source={source} formatter={formatter}></SingleBlog>;
}

export const getStaticPaths: GetStaticPaths = async () => {

  const paths = getAllId().map((slug) => {
    return {
      params: {
        slug: slug,
      },
    };
  });

  return {
    paths: paths,
    // 设置fallback的参数进行生成
    fallback: true,
  };
};

export const getStaticProps: GetStaticProps = async (context) => {
  const { params } = context || { slug: "" };
  const { frontmatter, mdxSource } = await getFileById(params?.slug as string);
  return {
    props: {
      source: serializeUtil(mdxSource),
      formatter: serializeUtil(frontmatter),
    },
    // 开启ISR,如果有更新20s后刷新将会得到改动的效果
    revalidate:20
  };
};

部署注意

部署可以使用官网给出的Dockerfile部署,需要注意的是,官网不会cv自定义的next.configimage内,而我们有自定义的next.config,因此,需要解开注释。

想要加速,这里安利阿里云的镜像服务,可以自行注册并在Docker daemon中配置,嘎嘎快!

js 复制代码
FROM node:alpine AS deps might be needed.
RUN apk add --no-cache libc6-compat
WORKDIR /app
COPY package.json yarn.lock ./
RUN yarn install --frozen-lockfile

FROM node:alpine AS builder
WORKDIR /app
COPY . .
COPY --from=deps /app/node_modules ./node_modules
RUN yarn build && yarn install --production --ignore-scripts --prefer-offline

FROM node:alpine AS runner
WORKDIR /app

ENV NODE_ENV production

RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001

# 下面第一行需要注意,官网这里是直接注释的,但是我们需要用到我们自定义的next.config,因此需要解开
COPY --from=builder /app/next.config.js ./
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next ./.next
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./package.json

USER nextjs

EXPOSE 3000

ENV PORT 3000

CMD ["node_modules/.bin/next", "start"]

效果图展示

/首页展示:

/about关于页面:

/blogs文章列表:

/blogs/[slug]具体文章:

最后

  感谢某大佬的ui布局参考!作为设计大佬,还使用next graphql并写出那么优雅的代码,在技术上实在是值得致敬!

  加油,动起来,create your world! Glad to say hello world

相关推荐
掘金者阿豪31 分钟前
把业务数据变成共享仪表盘:Metabase可视化与远程访问实践
前端·后端
kyriewen1 小时前
折腾了半年 AI 编程工作流,最后发现效率瓶颈是桌上那块屏幕
前端·javascript·ai编程
蜗牛前端1 小时前
codex 全流程开发上线的高颜值礼簿小程序
前端·微信小程序
大龄秃头程序员2 小时前
我在图文流 App 里落地双层缓存、弱网降级与 OOM 治理
前端
老王以为2 小时前
React Renderer 分离的多平台架构
前端·react native·react.js
hunterandroid2 小时前
Kotlin Coroutines 与 Flow:让异步任务更清晰
前端
Bigger3 小时前
从零搭建 AI 代码审查服务:一份前端也能看懂的 Python 学习笔记
前端·ci/cd·ai编程
lichenyang4533 小时前
JSAPI、NAPI、Biz、Imp:ASCF Demo 如何真正调用系统能力和 C++ 能力
前端
lichenyang4533 小时前
IPC、JSVM、UIThread、libuv:ASCF 架构图里最容易混的几个词
前端
用户059540174463 小时前
Redis记忆存储故障恢复测试踩坑实录:手动测试让我漏掉了2个一致性Bug
前端·css