六、Miscellaneous杂项

郁子大约 5 分钟约 1427 字笔记NextJSCodevolution

(一)App Layout

1.components/Header.js

function Header() {
  return <div className="layout-header">Header</div>;
}
export default Header;
function Footer() {
  return <div className="layout-footer">Footer</div>;
}
export default Footer;

3.styles/layout.css

.layout-header {
  background-color: #264653;
  color: #f4a261;
  font-size: 30px;
  text-align: center;
  padding: 30px;
}
.layout-footer {
  background-color: #264653;
  color: #e9c46a;
  font-size: 30px;
  text-align: center;
  padding: 20px;
}
.content {
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 100vh;
}

4.pages/_app.js

import Header from "../components/Header";
import Footer from "../components/Footer";
import "../styles/globals.css";
import "../styles/layout.css";

function MyApp({ Component, pageProps }) {
  if (Component.getLayout) {
    return Component.getLayout(<Component {...pageProps} />);
  }

  return (
    <>
      <Header />
      <Component {...pageProps} />
      <Footer />
    </>
  );
}

export default MyApp;

5.pages/about.js

import Footer from "../components/Footer";

function About() {
  return <h1 className="content">About</h1>;
}

export default About;

About.getLayout = function PageLayout(page) {
  return (
    <>
      {page}
      <Footer />
    </>
  );
};

(二)Head Component

1.pages/_app.js

import Head from "next/head";
import Header from "../components/Header";
import Footer from "../components/Footer";
import "../styles/globals.css";
import "../styles/layout.css";

function MyApp({ Component, pageProps }) {
  if (Component.getLayout) {
    return Component.getLayout(<Component {...pageProps} />);
  }

  return (
    <>
      <Head>
        <title>Codevolution</title>
        <meta name="description" content="Awesome YouTube channel" />
      </Head>
      <Header />
      <Component {...pageProps} />
      <Footer />
    </>
  );
}

export default MyApp;

2.pages/about.js

import Head from "next/head";
import Footer from "../components/Footer";

function About() {
  return (
    <>
      <Head>
        <title>About Codevolution</title>
        <meta name="description" content="Free tutorials on web development" />
      </Head>
      <h1 className="content">About</h1>
    </>
  );
}

export default About;

About.getLayout = function PageLayout(page) {
  return (
    <>
      {page}
      <Footer />
    </>
  );
};

3.pages/blog/[blogId].js

import Head from "next/head";

function Blog({ title, description }) {
  return (
    <>
      <Head>
        <title>{title}</title>
        <meta name="description" content={description} />
      </Head>
      <h1 className="content">Article</h1>
    </>
  );
}

export default Blog;

export async function getServerSideProps() {
  return {
    props: {
      title: "Article Title",
      description: "Article description",
    },
  };
}

(三)Image Component

  • 图片存储于 public 文件夹下
  • 图片自动转换为 webp 格式,以更小的体积渲染
  • 图片自动懒加载
  • 图片在加载时可以显示占位图片,此时图片路径不能是动态加载的,必须 import 图片
    • placeholder 设置为 blur ,开启显示占位图片
    • blurDataURL 设置自定义占位图片路径

1.pages/pets.js

import Image from "next/image";
import img from "../public/1.jpg";

function PetsPage() {
  return (
    <div>
      <Image src={img} placeholder="blur" alt="pet" width="280" height="420" />
      {["1", "2", "3", "4", "5"].map((path) => {
        return (
          <div key={path}>
            <Image src={`/${path}.jpg`} alt="pet" width="280" height="420" />
          </div>
        );
      })}
    </div>
  );
}

export default PetsPage;

(四)Absolute Imports & Module Paths

1.jsconfig.json

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/layout/*": ["components/layout/*"]
    }
  }
}

2.pages/_app.js

import Head from "next/head";
// import Header from "../components/Header";
// import Footer from "../components/Footer";
// import "../styles/globals.css";
// import "../styles/layout.css";
import Header from "@/layout/Header";
import Footer from "@/layout/Footer";
import "styles/globals.css";
import "styles/layout.css";

function MyApp({ Component, pageProps }) {
  if (Component.getLayout) {
    return Component.getLayout(<Component {...pageProps} />);
  }

  return (
    <>
      <Head>
        <title>Codevolution</title>
        <meta name="description" content="Awesome YouTube channel" />
      </Head>
      <Header />
      <Component {...pageProps} />
      <Footer />
    </>
  );
}

export default MyApp;

3.components/layout/Header.js

function Header() {
  return <div className="layout-header">Header</div>;
}
export default Header;
function Footer() {
  return <div className="layout-footer">Footer</div>;
}
export default Footer;

(五)Static HTML Export

1.Next build, start and export

  • next build
    • .next 文件夹中构建生产应用程序
  • next start
    • 启动一个支持混合页面的 Node.js 服务器,为静态渲染(SG)和服务端渲染(SSR)提供服务
  • next export
    • 将所有页面导出为静态的 HTML 文件,不需要 Node.js 服务器即可提供服务
    • 可以在任何静态托管服务或 CDN 上托管应用程序,而无需维护服务器
    • 不能使用 ISRSSR
    • 客户端数据获取渲染动态内容
    • 适合登录页面、博客和任何在构建时渲染内容的应用程序

2.package.json

{
  "script": {
    "export": "next build && next export"
  }
}
yarn export

(六)TypeScript Support

  • 目录下创建 tsconfig.json 空文件
  • 执行命令 yarn dev
  • 根据报错安装相应的 TypeScript
  • 再次执行命令 yarn dev ,会自动填充 tsconfig.json 文件

1.Data Fetching

import { GetStaticProps, GetStaticPaths, GetServerSideProps } from "next";

export const getStaticProps: GetStaticProps = async (context) => {
  // ...
};

export const getStaticPaths: GetStaticPaths = async () => {
  // ...
};

export const getServerSideProps: GetServerSideProps = async (context) => {
  // ...
};

2.API Routes

import type { NextApiRequest, NextApiResponse } from "next";

type Data = {
  name: string;
};

export default (req: NextApiRequest, res: NextApiResponse<Data>) => {
  res.status(200).json({ name: "John Doe" });
};

(七)Preview Mode

  • 辅助依赖于 CMS 的项目
  • CMS 是内容管理系统( content management system )的缩写,是一种帮助用户创建、管理和修改网站内容的工具,且不需要专业技术知识

1.When?

  • 使用静态渲染使页面在构建时预渲染,当页面从 CMS 中获取数据时,预览模式非常有用
  • 但是不适用于在 CMS 中创建草稿并希望立即在页面上预览草稿的更改时
  • 此时,需要 NextJS 绕过静态渲染
  • 项目部署之后,在 CMS 中做出更改时,页面不会反映修改的效果,因为只有在构建项目时才会生成页面
  • 开启预览模式后, getStaticProps 会在每次请求时执行一次
  • 预览数据接收对象参数

2.pages/news/index.js

function News({ data }) {
  return <h1 className="content">{data}</h1>;
}

export default News;

export async function getStaticProps(context) {
  console.log("Running getStaticProps", context.previewData);
  return {
    props: {
      data: context.preview ? "List of draft articles" : "List of published articles",
    },
  };
}

3.pages/api/preview.js

export default function handler(req, res) {
  res.setPreviewData({
    user: "IKUKO",
  });
  res.redirect(req.query.redirect);
}

4.验证步骤

  • yarn build
  • yarn start
  • 访问 localhost:3000/news ,控制台无输出
  • 访问 localhost:3000/api/preview?redirect=/news ,控制台输出信息,且浏览器存储了 cookie

5.pages/api/disable-preview.js

  • 访问 localhost:3000/api/disable-preview ,且浏览器清空了 cookie
export default function handler(req, res) {
  res.clearPreviewData();
  res.end("Preview mode disabled");
}

(八)Redirects

  • 用户将旧路径收藏为书签时,可通过此属性将用户引导至新路径

1.next.config.js

module.exports = {
  reactStrictMode: true,
  redirects: async () => {
    return [
      {
        source: "/about", // 重定向作用路径,访问时会跳转到destination
        destination: "/", // 默认重定向到根页面
        permanent: true, // 当前跳转配置是否永久
      },
      {
        source: "/old-blog/:id",
        destination: "/new-blog/:id",
        permanent: true,
      },
    ];
  },
};

(九)Environment Variables

  • 定义环境变量,使用 process.env.XXX 可在预渲染时(SG 和 SSR)访问,但无法在页面中展示,因为 NextJS 将环境变量封装在内部
  • 使用 NEXT_PUBLIC_ 开头定义的变量可以在页面展示
  • 一般用于设置密钥、谷歌分析代码等需要保密的字段

1..env.local

DB_USER=IKUKO
DB_PASSWORD=Password
NEXT_PUBLIC_ANALYTICS_ID=123

2.pages/blog/[blogId].js

import Head from "next/head";

function Blog({ title, description }) {
  return (
    <>
      <Head>
        <title>{title}</title>
        <meta name="description" content={description} />
      </Head>
      <h1 className="content">
        Env User {process.env.DB_USER} Password {process.env.DB_PASSWORD} ID
        {process.env.NEXT_PUBLIC_ANALYTICS_ID}
      </h1>
    </>
  );
}

export default Blog;

export async function getServerSideProps() {
  const user = process.env.DB_USER;
  const password = process.env.DB_PASSWORD;
  console.log(`Connecting to database with username ${user} and password ${password}.`);
  return {
    props: {
      title: "Article Title",
      description: "Article description",
    },
  };
}
上次编辑于: