Skip to content

Latest commit

 

History

History
455 lines (267 loc) · 10.4 KB

Next.js学习笔记.md

File metadata and controls

455 lines (267 loc) · 10.4 KB

Next.js学习笔记

Next.js框架简介

Next.js 是一个基于 React 的综合性服务端网页渲染框架。

像 Umi、Antd-Pro 应用场景为 管理后台,而 Next.js 则强调前台页面,或者说是 完整的网站应用。


Next.js 英文官网https://nextjs.org/

Next.js 中文站点(非官方)https://www.nextjs.cn/


Next.js 的官方文档写的非常好,直接看官方文档很容易上手 Next.js。

当前 Next.js 最新版本为 13.0.5。

本文以下内容仅为自己在初学 Next.js 时的一些记录。


创建一个 Next.js 项目

假设我们想创建的 Next.js 项目名为 my-next-app,那么执行下面命令:

npx create-next-app my-next-app

或者是

yarn create next-app my-next-app

这个命令几乎和 create-react-app 是一样的,只不过是把其中的 react 替换成了 next


项目使用 TypeScript

如果项目打算使用 TypeScript,那么只需在创建的命令中加入参数 --typescript,即:

yarn create next-app my-next-app --typescript


是否使用 ESLint ?

当执行命令后,该脚手架中间会有一次询问:是否使用 ESLint?

可根据自己需求来决定,默认为 Yes。


如果你只是最基础的 Next.js 项目,那么默认只需安装:react、react-dom、next

如果你使用 TypeScript,那么额外需安装:typescript、@types/react、@types/node、@types/react-dom

如果你使用 ESLint,那么额外需安装:eslint、eslint-config-next


若安装卡顿可切换成淘宝源

yarn config set registry 'https://registry.npm.taobao.org'

项目目录

默认创建好的项目内容都存放在 根目录下的 pages 中。

结合实际情况,我个人建议是:

  1. 项目根目录下创建 src 目录
  2. 将 pages 移动到 src 里
  3. 在 src 下创建其他各种目录,例如 hooks、utils、components 等

修改配置

tsconfig.json

将默认的编译目标由 ES5 修改为 esnext


配置路径映射(alias)

Next.js 框架自带支持路径映射,需要做的就是直接在 tsconfig.json 文件中增加即可。

例如:

{
    "compilerOptions": {
         "target": "esnext",
+        "baseUrl": ".",
+        "paths": {
+            "@/components/*": ["src/components/*"],
+            "@/geometrys/*": ["src/geometrys/*"],
+            "@/hooks/*": ["src/hooks/*"],
+            "@/shaders/*": ["src/shaders/*"],
+            "@/utils/*": ["src/utils/*"]
+        }
    }
}

如果你项目没有使用 TypeScript,那你可能需要修改的文件是 jsconfig.json


next.config.js

可以在此文件中增加对 webpack 的一些配置。

例如,增加 .wgsl 文件的处理规则

/** @type {import('next').NextConfig} */
const nextConfig = {
    reactStrictMode: true,
    swcMinify: true,
+    webpack: (config, options) => {
+        config.module.rules.push({
+            test: /\.wgsl$/,
+            type: "asset/source"
+        })
+        return config
+    }
}

module.exports = nextConfig

.vscode/settings.json

在根目录创建 ./vscode/settings.json 文件,可以增加一些自己的 VSCode 设置。

我个人比较习惯增加一些配置:

{
    "editor.formatOnSave": true,
    "editor.codeActionsOnSave": {
        "source.fixAll.eslint": true
    },
    "eslint.validate": [
        "javascript",
        "html"
    ],
    "eslint.options": {
        "extensions": [
            ".js",
            ".jsx",
            ".ts",
            ".tsx"
        ]
    },
    "editor.detectIndentation": false,
    "editor.tabSize": 4,
    "javascript.format.insertSpaceBeforeFunctionParenthesis": true,
    "vscode-edge-devtools.webhint": false
}

页面开发

页面结构

我们看一下一个常规的 Next 页面代码结构:

import Head from 'next/head'

export default function Home() {
    return (
    <div>
      <Head>
        <title>Create Next App</title>
        <meta name="description" content="Generated by create next app" />
        <link rel="icon" href="/favicon.ico" />
      </Head>
      <main>...</main>
      <footer>...</footer>
    </div>
}

Next 与 React 页面开发的相同之处:

  1. 都支持 函数组件
  2. 都是在 return 对象中编写具体内容
  3. return 对象的最外成都需要一个独立的标签

Next 与 React 页面开发的不同之处:

React: 我们编写的组件(.tsx) 仅为页面 body 中某 div 对应的内容

react 项目中最终呈现的 html 是由 /public/index.html 加上我们编写的组件共同构成

Next: 我们编写的组件(.tsx) 还包含 头部信息标签 <head>

next 项目中每一个页面都可以独立定义自己的 <head> 标签内容

在 .tsx 中 return 的内容需要包含 <Head> 标签,最终会转化为 html 中的 <head> 标签

请注意,在 Next 中是没有 <Body> 标签的


结论:Next 把每个页面的 <head> 标签控制权也交给了我们。


但是,这也变相要求我们需要对每一个页面都增加上 <Head> 标签。

实际开发中,为了简化一些组件共同的 <Head> 标签,我们可以对页面进行简单改造。


页面改造

假定我们实际开发中,有以下需求:

  1. 不同页面上 <head> 标签主要差异为 <title> 的值,其他相同
  2. 不同页面上都有一些相同的模块组件,例如都需要一个 <PageList>

那么我们可以创建一个 基础页面组件(BasePage),用来做为每一个页面的基础框架。


实现思路:

  1. 我们把不同页面的 标题文字(string) 作为一个 参数 title 传递进来
  2. 我们把不同页面的 具体内容(ReactNode) 作为 BasePage 的 children 传递进来

BasePage.tsx

import { ReactNode } from 'react'
import Head from 'next/head'
import PageList from '@/components/page-list'

interface BasePageProps {
    title?: string
    children?: ReactNode
}

const BasePage = ({ title = 'Hello WebGPU', children = [] }: BasePageProps) => {
    return (
        <div className='container'>
            <Head>
                <title>{title}</title>
                <meta name="description" content="Next.js Samples" />
                <link rel="icon" href="/favicon.ico" />
            </Head>
            <PageList />
            <main className='main'>
                {
                    children
                }
            </main>
        </div>
    )
}

export default BasePage

有了 BasePage 组件以后,我们再想创建具体的页面,例如 XxxPage,我们就可以这样写。

XxxPage.tsx

const XxxPage: FC = () => {
    const [xx,setXx] = useState()
    useEffect(()=>{ ... },[])
    return (
        <BasePage title='Hello Next.js'>
            <div> .... </div>
            <div> .... </div>
        </BasePage>
    )
}

提醒:对于 <BasePage> 标签内部,就好像普通的网页组件一样编写内容即可。


组件内部获取当前路由

在 Next.js 组件中,可以通过 useRouter() 获取当前路由。

const router = useRouter()
console.log(router.route) //当前页面的路由(除域名端口外的url)

在上面页面改造时,我们提到了不同页面共同使用到的一个组件 <PageList>

实际上在它的内部,就可以通过获取当前路由进行相关动态变化。


通过动态导入+禁用SSR来解决 JS 找不到 window/document 的问题

特别强调一下:

由于 Next.js 默认是开启 SSR 的,这意味着你直接写的 JS 代码实际上并不是直接运行在浏览器环境中的。

你可以理解为是运行在 Node.js 环境中的,也可以理解为 它仅仅是代码片段。

由于不是浏览器环境,造成一个直接结果就是:你直接编写的 JS 中不存在 window 和 document 对象,当然 useEffect() 中代码是个例外,在 useEffect() 中的 JS 是存在 window 和 document 对象的。


解决方案:

假设你的 JS 代码中需要访问 window 和 document 对象,那么你有 2 个选择:

  1. 把相关代码在 useEffect() 中

    实际中,你可以单独编写一个 hook 函数,内部通过 useEffect() 来对外提供某些 window/document 的属性值

  2. 动态引入并且禁用 SSR

    换句话说,就是不再让你写的 JS 当做代码片段,而是以 动态导入 的形式来引入到网页(浏览器环境)中。


动态引入与禁用SSR:

这部分知识点,位于 Next.js 文档中的:高级特性(Advanced Features) > 动态导入(Dynamic Import)

中文文档:https://nextjs.org/docs/advanced-features/dynamic-import

英文文档:https://www.nextjs.cn/docs/advanced-features/dynamic-import


假定你编写了一个组件 hello.tsx,那么动态引入该组件的方式为:

const DynamicComponentWithNoSSR = dynamic(
  () => import('../components/hello'),
  { ssr: false }
)

function Home() {
  return (
    <div>
      <Header />
      <DynamicComponentWithNoSSR />
      <p>HOME PAGE is here!</p>
    </div>
  )
}

上述代码中通过定义一个名为 DynamicComponentWithNoSSR 的组件,将 hello.tsx 作为它的动态实际内容引入使用。

你可以把 DynamicComponentWithNoSSR 看作是一个 占位组件

同时第 2 个参数中明确配置 ssr:false,这样意味着 hello.tsx 组件是运行在浏览器环境中了,它的内部就可以随心所欲使用 window/document 对象了。


解决找不到 window/document 对象,究竟采用哪种方式,是把代码都写到 useEffect() 中,还是 动态引入禁用 SSR,需要根据实际情况来决定。


Next.js 框架非常灵活(页面、组件、路由、SSR),如果想深入学习,需要看大量别人写的代码(套路),里面有非常多的奇淫技巧。