六、Miscellaneous杂项
大约 5 分钟约 1427 字
(一)App Layout
components/Header.js
1.function Header() {
return <div className="layout-header">Header</div>;
}
export default Header;
components/Footer.js
2.function Footer() {
return <div className="layout-footer">Footer</div>;
}
export default Footer;
styles/layout.css
3..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;
}
pages/_app.js
4.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;
pages/about.js
5.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
pages/_app.js
1.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;
pages/about.js
2.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 />
</>
);
};
pages/blog/[blogId].js
3.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
设置自定义占位图片路径
pages/pets.js
1.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
jsconfig.json
1.{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/layout/*": ["components/layout/*"]
}
}
}
pages/_app.js
2.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;
components/layout/Header.js
3.function Header() {
return <div className="layout-header">Header</div>;
}
export default Header;
components/layout/Footer.js
4.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
上托管应用程序,而无需维护服务器 - 不能使用
ISR
或SSR
- 客户端数据获取渲染动态内容
- 适合登录页面、博客和任何在构建时渲染内容的应用程序
- 将所有页面导出为静态的
package.json
2.{
"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
会在每次请求时执行一次 - 预览数据接收对象参数
pages/news/index.js
2.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",
},
};
}
pages/api/preview.js
3.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
pages/api/disable-preview.js
5.- 访问
localhost:3000/api/disable-preview
,且浏览器清空了 cookie
export default function handler(req, res) {
res.clearPreviewData();
res.end("Preview mode disabled");
}
(八)Redirects
- 用户将旧路径收藏为书签时,可通过此属性将用户引导至新路径
next.config.js
1.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
- 定义环境变量,使用
p
可在预渲染时(SG 和 SSR)访问,但无法在页面中展示,因为rocess.env.XXX NextJS
将环境变量封装在内部 - 使用
NEXT_PUBLIC_
开头定义的变量可以在页面展示 - 一般用于设置密钥、谷歌分析代码等需要保密的字段
.env.local
1.DB_USER=IKUKO
DB_PASSWORD=Password
NEXT_PUBLIC_ANALYTICS_ID=123
pages/blog/[blogId].js
2.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",
},
};
}