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