javascript技巧

关注公众号 jb51net

关闭
首页 > 网络编程 > JavaScript > javascript技巧 > 服务端渲染nextjs项目接入

服务端渲染nextjs项目接入经验总结分析

作者:路边县

这篇文章主要为大家介绍了服务端渲染nextjs项目接入经验总结分析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

背景介绍

服务端渲染 nextjs@13 项目接入经验总结,本文重点介绍基本知识点/常用的知识点/关键知识点

为提高首屏渲染速度减少白屏时间提高用户体验及丰富技术面,开始调研和接入nextjs框架

nextjs是一套成熟的同构框架(一套代码能运行在服务端也能运行在浏览器)对比传统的客户端渲染的优势是首屏是带数据的。其它后续操作是一样的。理论上能比客户端渲染看到数据能快个100-200ms具体看实际统计,

服务端渲染大概流程图(图片来源于网络)

客户端渲染大概流程图

对比流程图服务端渲染更加简洁。

使用

环境

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'

服务器组件和客户端组件

服务器组件需要运行在服务器
主要特点有请求数据,服务端环境等

客户端组件运行在浏览器 标识 文件第一行增加 'use client'主要特点有事件,浏览器环境,react hooks

比较

操作服务器组件客户端组件
请求数据
访问后端资源(直接)
在服务器上保留敏感信息(访问令牌、API密钥等)
保持对服务器的大量依赖性/减少客户端JavaScript
添加交互性和事件侦听器(onClick、onChange等)
使用状态和生命周期(useState、useReducer、useEffect等)
浏览器API
自定义hooks
使用React Class组件

开始填充业务代码

修改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>
  );
}

获取数据

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>
}

把浏览器的信息转发到服务端
这个例子是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()
};

处理全局通讯和数据

在/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://...')
  // 具体逻辑根据实际的来
  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添加

路由名称根据实际来

{
  basePath: '/router'
}

然后在流水线nginx配置路由 /router* 转发到这个应用

如果 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://nextjs.org/docs

https://vercel.com/guides/react-context-state-management-nextjs

以上就是服务端渲染nextjs项目接入经验总结分析的详细内容,更多关于服务端渲染nextjs项目接入的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文