服務端渲染nextjs項目接入經(jīng)驗總結分析
背景介紹
服務端渲染 nextjs@13 項目接入經(jīng)驗總結,本文重點介紹基本知識點/常用的知識點/關鍵知識點
為提高首屏渲染速度減少白屏時間提高用戶體驗及豐富技術面,開始調研和接入nextjs框架
nextjs是一套成熟的同構框架(一套代碼能運行在服務端也能運行在瀏覽器)對比傳統(tǒng)的客戶端渲染的優(yōu)勢是首屏是帶數(shù)據(jù)的。其它后續(xù)操作是一樣的。理論上能比客戶端渲染看到數(shù)據(jù)能快個100-200ms具體看實際統(tǒng)計,
服務端渲染大概流程圖(圖片來源于網(wǎng)絡)

客戶端渲染大概流程圖

對比流程圖服務端渲染更加簡潔。
使用
環(huán)境
Node.js >= 18.17 nextjs13
安裝
npx create-next-app@13
選擇 src/ 目錄 和 使用 App Router
大致目錄結構
...
package.json
public
node_modules
src
|- app
|- page.tsx
|- layout.tsx
|- blog
|- page.tsx
|- layout.tsx
|- docs
|- page.tsx
|- layout.tsx
| -services
| -utils
...路由
大致路由為
注意這是約定路由 需要用page.tsx layout.tsx文件命名


內置API
head標簽 import Head from 'next/head'
圖片標簽 import Image from 'next/image'
跳轉標簽 import Link from 'next/link'
script
import Script from 'next/script'路由相關
import { useRouter, useSearchParams, useParams, redirect } from 'next/navigation'請求頭 import { headers } from 'next/headers'
服務器組件和客戶端組件
服務器組件需要運行在服務器
主要特點有請求數(shù)據(jù),服務端環(huán)境等
客戶端組件運行在瀏覽器 標識 文件第一行增加 'use client'主要特點有事件,瀏覽器環(huán)境,react hooks
比較
| 操作 | 服務器組件 | 客戶端組件 |
|---|---|---|
| 請求數(shù)據(jù) | ? | ? |
| 訪問后端資源(直接) | ? | ? |
| 在服務器上保留敏感信息(訪問令牌、API密鑰等) | ? | ? |
| 保持對服務器的大量依賴性/減少客戶端JavaScript | ? | ? |
| 添加交互性和事件偵聽器(onClick、onChange等) | ? | ? |
| 使用狀態(tài)和生命周期(useState、useReducer、useEffect等) | ? | ? |
| 瀏覽器API | ? | ? |
| 自定義hooks | ? | ? |
| 使用React Class組件 | ? | ? |
開始填充業(yè)務代碼
修改html頁面
文件位置在/src/app/layout.tsx,可以進行標題修改等一系操作
import Head from "next/head";
export default async function RootLayout(props: any) {
return (
<html lang="en">
<Head>
<title>頁面標題</title>
</Head>
<body>{props.children}</body>
</html>
);
}獲取數(shù)據(jù)
async function getData() {
const res = await fetch('https://xxxxx.com/', { cache: 'no-store' })
if (!res.ok) {
throw new Error('Failed to fetch data')
}
return res.json()
}
export default async function Page() {
const data = await getData()
return <main>{JSON.stringify(data, null, 2)}</main>
}把瀏覽器的信息轉發(fā)到服務端
這個例子是cookie有需求可以用放其它的
import { headers } from 'next/headers'
const getData = async () => {
const headersList = headers();
const cookie = headersList.get('Cookie');
const res = await fetch('https://xxx.com', {
cache: 'no-store',
headers: { cookie }
});
return res.json()
};處理全局通訊和數(shù)據(jù)
在/src/app 目錄下增加 context.tsx/src/app/context.tsx
'use client';
import { createContext, useMemo } from 'react';
import { useImmer } from 'use-immer';
export const PropsContext = createContext({});
export function Context({ children, ...other }: any) {
const [GlobalState, setGlobalState] = useImmer<any>({
...other
});
const providerValue = useMemo(
() => ({ GlobalState, setGlobalState }),
[GlobalState]
);
return (
<PropsContext.Provider value={providerValue}>
{children}
</PropsContext.Provider>
);
}/src/app/layout.tsx
import React from 'react';
import { headers } from 'next/headers'
import { Context } from './context';
const getData = async () => {
const headersList = headers();
const cookie = headersList.get('Cookie');
const res = await fetch('https://xxx.com', {headers: {
cookie
}});
return res.json()
};
export default async function RootLayout(props: any) {
const useInfo = await getData();
return (
<html lang="en">
<body>
<div>header</div>
<Context useInfo={useInfo}>{props.children}</Context>
<div>footer</div>
</body>
</html>
);
}使用/src/app/blog/page.tsx
'use client';
import { PropsContext } from '@/app/context';
import { useContext } from 'react';
export default function A2() {
const { GlobalState, setGlobalState } = useContext<any>(PropsContext);
return (
<main>
{JSON.stringify(GlobalState, null, 2)}
<div
onClick={() => {
setGlobalState((s: any) => {
s.useInfo.name = '修改之后的名稱';
});
}}
>
修改名稱
</div>
</main>
);
}跳轉
如果沒有用戶信息需要跳轉到登錄頁
import { redirect } from 'next/navigation'
async function fetchTeam(id) {
const res = await fetch('https://...')
// 具體邏輯根據(jù)實際的來
if (!res.ok) return undefined
return res.json()
}
export default async function Profile({ params }) {
const team = await fetchTeam(params.id)
if (!team) {
redirect('/login')
}
// ...
}部署
如果不在根域名下需要在 next.config.js添加
路由名稱根據(jù)實際來
{
basePath: '/router'
}然后在流水線nginx配置路由 /router* 轉發(fā)到這個應用
如果 basePath 配置的 /router/' 對應nginx配置 /router/*
編寫 Dockerfile
由于 FROM nodejs@xx 過不了鏡像掃描 鏡像里面又沒有Node.js >= 18.17的只能使用提供最基礎的鏡像了
Dockerfile
FROM hub.xxx.com/basics/alpine:3.18.2 RUN apk add nodejs=18.18.2-r0 npm=9.6.6-r0 WORKDIR /app ADD . . RUN npm i RUN npm run build EXPOSE 3000 CMD ["sh", "-c", "NODE_ENV=$NODE_ENV npm run start"]
參考文檔
https://vercel.com/guides/react-context-state-management-nextjs
以上就是服務端渲染nextjs項目接入經(jīng)驗總結分析的詳細內容,更多關于服務端渲染nextjs項目接入的資料請關注腳本之家其它相關文章!
相關文章
JavaScript求一個數(shù)組中重復出現(xiàn)次數(shù)最多的元素及其下標位置示例
這篇文章主要介紹了JavaScript求一個數(shù)組中重復出現(xiàn)次數(shù)最多的元素及其下標位置,涉及javascript數(shù)組元素遍歷、判斷、正則過濾、追加等相關操作技巧,需要的朋友可以參考下2018-07-07
JavaScript判斷數(shù)組是否存在key的簡單實例
下面小編就為大家?guī)硪黄狫avaScript判斷數(shù)組是否存在key的簡單實例。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2016-08-08
跨瀏覽器開發(fā)經(jīng)驗總結(三) 警惕“IE依賴綜合癥”
跨瀏覽器開發(fā)經(jīng)驗總結(三) 警惕“IE依賴綜合癥”2010-05-05
JavaScript中工廠函數(shù)與構造函數(shù)示例詳解
這篇文章主要給大家介紹了關于JavaScript中工廠函數(shù)與構造函數(shù)的相關資料,文中通過示例代碼介紹的非常詳細,對大家學習或者使用JavaScript具有一定的參考學習價值,需要的朋友們下面來一起學習學習吧2019-05-05

