服務(wù)端渲染nextjs項(xiàng)目接入經(jīng)驗(yàn)總結(jié)分析
背景介紹
服務(wù)端渲染 nextjs@13
項(xiàng)目接入經(jīng)驗(yàn)總結(jié),本文重點(diǎn)介紹基本知識(shí)點(diǎn)/常用的知識(shí)點(diǎn)/關(guān)鍵知識(shí)點(diǎn)
為提高首屏渲染速度減少白屏?xí)r間提高用戶體驗(yàn)及豐富技術(shù)面,開(kāi)始調(diào)研和接入nextjs框架
nextjs是一套成熟的同構(gòu)框架(一套代碼能運(yùn)行在服務(wù)端也能運(yùn)行在瀏覽器)對(duì)比傳統(tǒng)的客戶端渲染的優(yōu)勢(shì)是首屏是帶數(shù)據(jù)的。其它后續(xù)操作是一樣的。理論上能比客戶端渲染看到數(shù)據(jù)能快個(gè)100-200ms
具體看實(shí)際統(tǒng)計(jì),
服務(wù)端渲染大概流程圖(圖片來(lái)源于網(wǎng)絡(luò))
客戶端渲染大概流程圖
對(duì)比流程圖服務(wù)端渲染更加簡(jiǎn)潔。
使用
環(huán)境
Node.js >= 18.17 nextjs13
安裝
npx create-next-app@13
選擇 src/ 目錄 和 使用 App Router
大致目錄結(jié)構(gòu)
... 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文件命名
內(nèi)置API
head標(biāo)簽 import Head from 'next/head'
圖片標(biāo)簽 import Image from 'next/image'
跳轉(zhuǎn)標(biāo)簽 import Link from 'next/link'
script
import Script from 'next/script'路由相關(guān)
import { useRouter, useSearchParams, useParams, redirect } from 'next/navigation'請(qǐng)求頭 import { headers } from 'next/headers'
服務(wù)器組件和客戶端組件
服務(wù)器組件需要運(yùn)行在服務(wù)器
主要特點(diǎn)有請(qǐng)求數(shù)據(jù),服務(wù)端環(huán)境等
客戶端組件運(yùn)行在瀏覽器 標(biāo)識(shí) 文件第一行增加 'use client'
主要特點(diǎn)有事件,瀏覽器環(huán)境,react hooks
比較
操作 | 服務(wù)器組件 | 客戶端組件 |
---|---|---|
請(qǐng)求數(shù)據(jù) | ? | ? |
訪問(wèn)后端資源(直接) | ? | ? |
在服務(wù)器上保留敏感信息(訪問(wèn)令牌、API密鑰等) | ? | ? |
保持對(duì)服務(wù)器的大量依賴(lài)性/減少客戶端JavaScript | ? | ? |
添加交互性和事件偵聽(tīng)器(onClick、onChange等) | ? | ? |
使用狀態(tài)和生命周期(useState、useReducer、useEffect等) | ? | ? |
瀏覽器API | ? | ? |
自定義hooks | ? | ? |
使用React Class組件 | ? | ? |
開(kāi)始填充業(yè)務(wù)代碼
修改html頁(yè)面
文件位置在/src/app/layout.tsx,可以進(jìn)行標(biāo)題修改等一系操作
import Head from "next/head"; export default async function RootLayout(props: any) { return ( <html lang="en"> <Head> <title>頁(yè)面標(biāo)題</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> }
把瀏覽器的信息轉(zhuǎn)發(fā)到服務(wù)端
這個(gè)例子是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 = '修改之后的名稱(chēng)'; }); }} > 修改名稱(chēng) </div> </main> ); }
跳轉(zhuǎn)
如果沒(méi)有用戶信息需要跳轉(zhuǎn)到登錄頁(yè)
import { redirect } from 'next/navigation' async function fetchTeam(id) { const res = await fetch('https://...') // 具體邏輯根據(jù)實(shí)際的來(lái) 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
添加
路由名稱(chēng)根據(jù)實(shí)際來(lái)
{ basePath: '/router' }
然后在流水線nginx配置路由 /router*
轉(zhuǎn)發(fā)到這個(gè)應(yīng)用
如果 basePath
配置的 /router/'
對(duì)應(yīng)nginx配置 /router/*
編寫(xiě) Dockerfile
由于 FROM nodejs@xx 過(guò)不了鏡像掃描 鏡像里面又沒(méi)有Node.js >= 18.17的只能使用提供最基礎(chǔ)的鏡像了
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
以上就是服務(wù)端渲染nextjs項(xiàng)目接入經(jīng)驗(yàn)總結(jié)分析的詳細(xì)內(nèi)容,更多關(guān)于服務(wù)端渲染nextjs項(xiàng)目接入的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
JavaScript之通過(guò)年月獲取月份的天數(shù)、日期格式化、時(shí)間、補(bǔ)零、Date、toLocaleString、Intl、
這篇文章主要介紹了JavaScript之通過(guò)年月獲取月份的天數(shù)、日期格式化、時(shí)間、補(bǔ)零、Date、toLocaleString、Intl、DateTimeFormat、format的相關(guān)知識(shí),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2024-03-03js函數(shù)參數(shù)設(shè)置默認(rèn)值的一種變通實(shí)現(xiàn)方法
js函數(shù)中有個(gè)儲(chǔ)存參數(shù)的數(shù)組arguments,因此js版支持參數(shù)默認(rèn)值的函數(shù)可以通過(guò)另外一種變通的方法實(shí)現(xiàn)2014-05-05laydate只顯示時(shí)分 不顯示秒的功能實(shí)現(xiàn)方法
今天小編就為大家分享一篇laydate只顯示時(shí)分 不顯示秒的功能實(shí)現(xiàn)方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-09-09uniapp開(kāi)發(fā)小程序的開(kāi)發(fā)規(guī)范總結(jié)
uni-app 是一個(gè)使用 vue.js 開(kāi)發(fā)跨平臺(tái)應(yīng)用的前端框架,下面這篇文章主要給大家介紹了關(guān)于uniapp開(kāi)發(fā)小程序的開(kāi)發(fā)規(guī)范,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-07-07javascript Blob對(duì)象實(shí)現(xiàn)文件下載
這篇文章主要為大家介紹了vue組件通信的幾種方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助2021-12-12詳解ES6 export default 和 import語(yǔ)句中的解構(gòu)賦值
這篇文章主要介紹了詳解ES6 export default 和 import語(yǔ)句中的解構(gòu)賦值,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2019-05-05JavaScript正則表達(dá)式匹配 div style標(biāo)簽
這篇文章主要介紹了JavaScript正則表達(dá)式匹配<div><style>標(biāo)簽 的相關(guān)資料,需要的朋友可以參考下2016-03-03解決Webpack 熱部署檢測(cè)不到文件變化的問(wèn)題
下面小編就為大家分享一篇解決Webpack 熱部署檢測(cè)不到文件變化的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-02-02