react
项目,nextjs
采用 page router
及 第三代 css
框架 Tailwind
pages/_app.js
_app
容器组件 { Component, pageProps }
import Layout from '../components/layout';
export default function MyApp({ Component, pageProps }) {
return (
<Layout>
<Component {...pageProps} />
</Layout>
);
}
Dynamic Segments can be access from useRouter
.[folderName]
[...folderName]
Catch-all Segments[[...folderName]]
可选Route | Example URL | params |
---|---|---|
pages/posts/[slug].js | /posts/a | { slug: 'a' } |
pages/optional/[...slug].js | /optional/a/b/c | { slug: ['a', 'b', 'c'] } |
pages/optional/[[...slug]].js | /optional | {} |
pages/optional/[[...slug]].js | /optional/a/b/c | { slug: ['a', 'b', 'c'] } |
(folder) | Group routes without affecting routing |
---|---|
_folder | Opt folder and all child segments out of routing |
@folder | Named slot |
---|---|
(.)folder | Intercept same level |
(..)folder | Intercept one level above |
(..)(..)folder | Intercept two levels above |
(...)folder | Intercept from root |
head
组件渲染流程下面是 Next.js 在处理 Head
组件时的流程:
HeadManager
对象。Head
组件时,会将其包含的元素添加到 HeadManager
对象中。HeadManager
对象发送给客户端。HeadManager
对象。HeadManager
对象中的元素更新到 <head>
中。HeadManager
对象的 updateHead
方法,使其将 Head
组件中的新元素添加到页面的 <head>
部分中。<head>
部分中的元素时(例如切换到新页面),HeadManager
对象的 updateHead
方法都会被触发,以实时更新视图。 +-------------+ +----------------------+
| Next.js | | Browser |
| Server | | |
+------+------| | |
| | | |
| | | |
| | | |
| | 1. Generate HTML with |
| |<---------------------------------------------+
| | HeadManager object on each page
| |
| |
| |
| | 2. Send HTML with HeadManager object
| | to client
| +---------------------------------------------->+
| |
| |
| | 4. Initialize new HeadManager
| |<----------------------------------------------+
| | object on client
3. Render | |
component| | 5. Update <head> using new
with | |<---------------- HeadManager object
Head | |
component| | 6. Perform client-side routing
| |<---------------- to new page
| |
| | 7. Repeat steps 3-6 for each page change
| |
process.env
.env.$(NODE_ENV).local
.env.local (Not checked when NODE_ENV is test.)
.env.$(NODE_ENV)
.env
Next.js
中,每个页面都是一个 React 组件,可以通过导出默认属性来定义页面的行为和内容getServerSideProps
:用于在每次请求时获取页面的数据,返回一个对象,包含页面的 props。这个方法在每次请求时都会被调用,可以用于动态生成页面内容。getInitialProps
:用于在每次请求时获取页面的数据,返回一个对象,包含页面的 props。这个方法已经被废弃,推荐使用 getServerSideProps
或 getStaticProps
。pageProps
:包含页面的 props,可以在组件中直接使用。Component
:页面的 React 组件,可以在组件中直接使用。getStaticPaths (Static Generation):
为动态路由参数构建所需的路径getStaticProps:(Static Generation)
: 负责根据指定的动态路由参数获取数据router.push('/?counter=10', '/about?counter=10', { shallow: true })
export const config = {
api: {
bodyParser: {
sizeLimit: '1mb',
},
},
};
data-fetching strategy
static generation
server-side
middleware.js
matching paths order
server code
// middleware.js
export default function myServerMiddleware(req, res, next) {
console.log('This is my server middleware');
next();
}
// pages/api/mypage.js
import myServerMiddleware from '../../middleware';
// 页面组件或 API 路由处理函数
function handler(req, res) {
return res.status(200).json({ text: 'Hello' });
}
// 使用服务端中间件
export default myServerMiddleware(handler);
client code
// middleware.js
function myClientMiddleware(request, response, next) {
console.log('This is my client middleware');
// 在请求头中添加自定义信息
request.headers["x-my-header"] = "hello";
next();
}
export default myClientMiddleware;
useEffect
// pages/index.js
import myClientMiddleware from '../middleware';
function IndexPage() {
useEffect(() => {
myClientMiddleware();
}, []); // 在组件加载时执行中间件函数
return <div>Hello world</div>;
}
export default IndexPage;
路由配置
// pages/middleware.js
const legacyPrefixes = ['/docs', '/blog'];
export default async function middleware(req) {
const { pathname } = req.nextUrl;
if (legacyPrefixes.some((prefix) => pathname.startsWith(prefix))) {
return NextResponse.next();
}
// apply trailing slash handling
if (
!pathname.endsWith('/') &&
!pathname.match(/((?!\.well-known(?:\/.*)?)(?:[^/]+\/)*[^/]+\.\w+)/)
) {
req.nextUrl.pathname += '/';
return NextResponse.redirect(req.nextUrl);
}
}
通过配置实现
// next.config.js
module.exports = {
async rewrites() {
return [
{
source: '/api/:path*',
destination: 'http://localhost:3000/api/:path*',
},
]
},
async redirects() {
return [
{
source: '/old-path',
destination: '/new-path',
permanent: true,
},
]
},
}
同构应用
同构模块的出现是为了解决单页应用(SPA)在网络环境差、首次加载时间长、SEO 不友好等方面的问题。
在单页应用中,大部分 HTML、CSS、JS 等资源只在首次加载时请求一次,以后的操作只会请求 JSON 数据,通过 AJAX 异步刷新页面。这种应用方式的好处在于用户操作更顺畅,但缺 点也很明显:首次加载速度慢、SEO 不友好。
单页应用首次加载慢的原因是因为在请求 HTML 文件之外,还需要加载大量的 JavaScript 文件来构建页面,这些文件在首次加载时会花费很长时间。而且搜索引擎的爬虫只会对页面进行解析并拿取其中的文本信息,对于单页应用中使用 ajax 获取内容生成的数据会不良影响 SEO。
为了解决以上问题,同构模块就出现了。同构应用是指在服务端运行一段 JS 代码,生成 HTML 文件后再返回给客户端。也就是说,同样一段代码,在服务器端和客户端都会执行,处理逻辑相同,生成的结果也完全一致。因此同构应用能够在第一次请求时快速返回完整的 HTML 页面,提高页面加载速度,而且因为服务端构建页面返回给客户端所得到的 HTML 已经包含所有的内容,包括从服务端读取的数据,因此对搜索引擎的爬取也是十分友好的。
nextjs
前后端同构模块, build --> hydration
两个主阶段渲染前后端同构基本原理
将一部分代码在服务器端运行,将另一部分代码在浏览器端运行 并将它们的状态和数据同步. ReactDOM.hydrate()方法将服务器端生成的
HTML
(含注入的服务端数据__NEXT_DATA__
)在客户端
ReactDomServer.renderToString()
: 服务端渲染React.hydrate()
客户端渲染 水化?ReactDOM.render()
基本渲染ReactDom.createPortal()
脱离父组件渲染,如弹窗...DOM
Fiber
支持可中断渲染 16+browser
相关树 Dom
树, css rule
规则树, render
渲染树useCallback
将函数的引用进行缓存,避免在每次组件重新渲染时都要重新创建函数useRef
在函数式组件中引用 DOM 元素或任何值。它类似于使用 ref 属性的类组件useState
在函数式组件中添加状态。它返回一个包含状态值和一个更新状态的函数的数组。useReducer
接受一个 reducer
函数和初始状态,返回一个包含当前复杂状态和 dispatch
函数的数组useEffect
在函数式组件中定义副作用useContext
在函数式组件中使用 React
上下文useMemo
在函数式组件中缓存计算结果,只有当依赖项更改时才会重新计算A page is UI that's unique to a route
// app/feed/[item]/page.js
export default function Page({ params, searchParams }) {
return <h1>My Page</h1>;
}
params (optional)
路径格式 | 请求路径 | 参数对象 |
---|---|---|
app/shop/[slug]/page.js |
/shop/1 |
{ slug: '1' } |
app/shop/[category]/[item]/page.js |
/shop/1/2 |
{ category: '1', item: '2' } |
app/shop/[...slug]/page.js |
/shop/1/2 |
{ slug: ['1', '2'] } |
searchParams (optional)
URL | searchParams |
---|---|
/shop?a=1 |
{ a: '1' } |
/shop?a=1&b=2 |
{ a: '1', b: '2' } |
/shop?a=1&a=2 |
{ a: ['1', '2'] } |
Page | Route | Result |
---|---|---|
app/page.js | app/route.js | Conflict |
app/page.js | app/api/route.js | Valid |
app/[user]/page.js | app/api/route.js | Valid |
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。