• 新版 Next.js 从入门到入土


    本教程用的Next.js 是 13 版本

    Next.js 简介

    1. 完善的React项目,搭建轻松
    2. 自带数据同步,解决服务端渲染最大难点
    3. 丰富的插件
    4. 灵活配置

    创建第一个项目

    手动创建

    初始化
    npm init
    
    • 1
    安装所需要的依赖包
    npm install --save react react-don next
    
    • 1
    增加快捷命令
    "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1",
        "dev": "next",
        "build": "next build",
        "start": "next start"
      },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    创建测试文件

    在根目录下创建pages文件夹,并在该文件下创建 index.js

    pages 文件夹是Next 规定的,在这个文件夹下写入的文件,Next.js 会自动创建对应的路由

    function Index() {
        return (
            
    Halo Next.js
    ) } export default Index
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    运行 npm run dev

    creact-next-app 脚手架创建Next.js 项目

    创建项目
    npx create-next-app@latest
    
    • 1

    第一次创建项目,若没有安装nextjs 会提示是否安装

    What is your project named? my-app                              // 项目名
    Would you like to use TypeScript? No / Yes                      // TypeScript 
    Would you like to use ESLint? No / Yes                          // ESLint
    Would you like to use Tailwind CSS? No / Yes                    // Tailwind CSS
    Would you like to use `src/` directory? No / Yes                // src 作为根目录
    Would you like to use App Router? (recommended) No / Yes        // 路由
    Would you like to customize the default import alias? No / Yes  // 自定义默认导入别名
    What import alias would you like configured? @/*                // 配置什么导入别名
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    运行 npm run dev

    Next.js 的page和component

    创建一个新的页面

    在page目录下创建 about.js

    function About () {
        return (
            
    About nextjs
    ) } export default About
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    访问 http://localhost:3000/about

    在 Next.js 中,一个 page(页面) 就是一个从 .jsjsx.ts.tsx 文件导出(export)的 React 组件 ,这些文件存放在 pages 目录下。每个 page(页面)都使用其文件名作为路由(route)

    创建二级目录页面

    在page目录下创建 home 文件 并在该文件下创建 home.js

    function Home () {
        return (
            
    home nextjs
    ) } export default Home
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    访问 http://localhost:3000/home/home

    Component组件的制作

    创建组件

    在src目录下创建 components 目录,并在该目录下创建 buttonComponent.js 文件

    export default ({children})=>
    
    • 1
    引用

    在home.js 引入

    import dynamic from 'next/dynamic'
    
    const ButtonComponent = dynamic(
        () => import('@/components/buttonComponent'),
    // { ssr: false }  // 是否关闭 ssr(服务端渲染) 默认是开启
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    使用
    按钮
    
    • 1

    路由

    标签式跳转

    在home页面新增两个页面

    homeA.js

    import React from "react";
    import Link from "next/link";
    
    const HomeA = () => {
        return (
            <>
                
    我是HomeA 页面
    去Home页面
    ) } export default HomeA
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    homeB.js

    import React from "react";
    import Link from "next/link";
    
    const HomeB = () => {
        return (
            <>
                
    我是HomeB 页面
    去Home页面
    ) } export default HomeB
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    修改home页面内容

    import React from "react"
    import Link from "next/link"
    
    function Home () {
        return (
            
    home nextjs
    去homeA页面
    去homeB页面
    ) } export default Home
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    早期版本 Link标签下是要接上a标签的,当前版本(13.4.19)如果加上a标签会报错

    Router模块进行跳转(编程式跳转)

    修改home.js页面

    import React from "react"
    import Router from "next/router"
    
    const goHomeA = () => {
        Router.push('/home/homeA')
    }
    
    const goHomeB = () => {
        Router.push('/home/homeB')
    }
    
    function Home () {
        return (
            
    home nextjs
    去homeA页面
    去homeB页面
    ) } export default Home
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    传参与接收

    Next.js 只能通过 query 来传递参数

    标签式

    修改home.js页面

    import React from "react"
    import Link from "next/link"
    
    function Home () {
        return (
            
    home nextjs
    张三
    李四
    ) } export default Home
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    修改homeA.js页面

    withRouter 是 Next.js 框架的高级组件,用来处理路由用的

    import React from "react";
    import Link from "next/link";
    import { withRouter } from "next/router";
    
    function Home () {
        return (
            
    home nextjs
    写法一
    { pathname: '/home/homeA', query: { name: '李四', age: 20 } }}>
    写法二
    ) } export default withRouter(HomeA)
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    编程式
    import React from "react"
    import Router from "next/router"
    
    const goHomeA = () => {
        Router.push('/home/homeA?name=张三&age=18')
    }
    
    const goHomeA2 = () => {
        Router.push({
            pathname: '/home/homeA',
            query: {
                name: '李四',
                age: 20
            }
        })
    }
    
    function Home () {
        return (
            
    home nextjs
    写法一
    写法二
    ) } export default Home
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    钩子函数

    History
    import React from "react"
    import Router from "next/router"
    
    Router.events.on('routeChangeStart', (...args) => {
        console.log('routeChangeStart -> 路由开始变化', ...args)
    })
    
    Router.events.on('routeChangeComplete', (...args) => {
        console.log('routeChangeComplete -> 路由结束变化', ...args)
    })
    
    Router.events.on("beforeHistoryChange", (...args) => {
        console.log('beforeHistoryChange -> 在改变浏览器 history 之前触发', ...args)
    })
    
    Router.events.on('routeChangeError', (...args) => {
        console.log('routeChangeError -> 跳转发生错误', ...args)
    })
    
    const goHomeA = () => {
        Router.push('/home/homeA?name=张三&age=18')
    }
    
    const goHomeA2 = () => {
        Router.push({
            pathname: '/home/homeA',
            query: {
                name: '李四',
                age: 20
            }
        })
    }
    
    function Home () {
        return (
            
    home nextjs
    写法一
    写法二
    ) } export default Home
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    Hash
    Router.events.on('hashChangeStart', (...args) => {
        console.log('hashChangeStart -> 路由开始变化', ...args)
    })
    
    Router.events.on('hashChangeComplete', (...args) => {
        console.log('hashChangeComplete -> 路由结束变化', ...args)
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在getInitialProps中获取远端数据

    getInitialProps 是挂在 React 组件上的静态方法

    如果你使用的是 Next.js 9.3 或更高版本,我们建议你使用 getStaticPropsgetServerSideProps 来替代 getInitialProps

    官方推荐的是fetch

    fetch 请求

    在page目录新建一个request.js 页面

    import { withRouter } from "next/router";
    
    function Request ({router, data}) {
        return (
            <>
                
    {router.name}
    请求页面 {data}
    ) } Request.getInitialProps = async () => { const res = await fetch('https://api.github.com/repos/vercel/next.js') const json = await res.json() console.log(json) return { stars: json.stargazers_count } } export default withRouter(Request)
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    index.js

    import Router from "next/router"
    
    const goRequest = () => {
      Router.push({
        pathname: '/request',
        query: {
            name: '李四',
            age: 20
        }
      })  
    }
    
    export default function Home() {
      return (
        <>
          
    首页
    去Request页面
    ) }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    运行页面,可以发现,getInitialProps 会在服务端渲染时执行,也会在客户端渲染时执行

    1. 当页面通过页面刷新等直接形式访问时,会触发 Nextjs 使用服务端渲染的方式返回页面数据

    此时 getInitialProps 会在服务端执行,浏览器端不会执行

    1. 当页面通过浏览器端路由跳转的形式访问时(如浏览器前进后退),该页面渲染不会触发 Nextjs 服务端渲染

    所以实际上 getInitialProps 方法会根据当前页面渲染时的端侧不同,自主地选择在 Node 端还是 Client 端执行

    getStaticProps

    getStaticProps 会在每次页面访问时被请求

    修改request.js

    import { withRouter } from "next/router";
    
    function Request ({router, content}) {
        return (
            <>
                
    {router.name}
    请求页面 {content}
    ) } export const getStaticProps = async () => { const res = await fetch('https://api.github.com/repos/vercel/next.js') const json = await res.json() console.log(json) return { props: { content: json.stargazers_count } }; }; export default withRouter(Request)
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    getStaticProps是用于在构建时预先执行getInitialProps进行的处理并预先生成静态文件的API。 不会在客户端上运行。 始终在服务器端运行。

    getServerSideProps

    import { withRouter } from "next/router";
    
    function Request ({router, content}) {
        return (
            <>
                
    {router.name}
    请求页面 {content}
    ) } export const getServerSideProps = async context => { const res = await fetch('https://api.github.com/repos/vercel/next.js') // if (!res) { // notFound 强制页面跳转到 404 // return { // notFound: true // }; // redirect 来将页面重定向 // return { // redirect: { // destination: '/', // permanent: false // } // }; // } const json = await res.json() console.log(json) return { props: { content: json.stargazers_count } }; } export default withRouter(Request)
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    通过 next.jsgetServerSideProps,我们在开发中可以很好的协调前后端数据,一些页面初始化数据、页面鉴权可以直接在 getServerSideProps 中进行处理,这样可以大大简化页面逻辑,还保障前后端的统一性。

    JSX 编写页面的CSS样式

    基础写法

    新建style.js 页面

    const Style = () => {
        return (
            <>
                
    style 页面
    基础
    ) } export default Style
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    要注意,style 后面要jsx next.js 会自动加入一个随机类名,这样就防止CSS的全局污染,如上述代码 base 会变成 base-xxxxxx

    动态样式

    修改style.js 页面

    import React, {useState} from "react"
    
    const Style = () => {
    
        const [color, setColor] = useState('blue')
        const [fontSize, setFontSize] = useState('16')
        const [margin, setMargin] = useState('40')
    
        const changeColor = () => {
            setColor(color === 'blue' ? 'red': 'blue')
        }
    
        const changeFontSize = () => {
            setFontSize(fontSize === '16' ? '20': '16')
        }
    
        const changeMargin = () => {
            setMargin(margin  === '10' ? '40': '10')
        }
    
    
        return (
            <>
                
    style 页面
    基础
    ) } export default Style
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    模块懒加载

    新建 import.js 页面

    引入 dayjs 库

    npm i dayjs
    
    • 1

    如果我们在页面直接引入,那它就会以公共库的形式进行打包发布,就算项目第一个页面不使用moment也会进行加载,这就是资源浪费

    懒加载引入的第三方库

    import.js

    import React,{useState} from "react";
    
    const Import = () => {
        const [time, setTime] = useState()
    
        const changeTime = async () => {
            const dayjs = await import('dayjs')        
            setTime(dayjs.default(Date.now()).format('YYYY-MM-DD HH:mm:ss'))
        }
    
        return (
            <>
                
    import 页面
    当前时间为:{time}
    ) } export default Import
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    可以看到我们是在需要的地方才引入

    要注意 使用 default 才能生效

    懒加载组件

    利用 dynamic 引入组件实现

    import dynamic from 'next/dynamic'
    
    const ButtonComponent = dynamic(() => import('@/components/buttonComponent'))
    
    const Import = () => {
        return (
            <>
                
    import 页面
    按钮 ) } export default Import
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    自定义组件是懒加载的,只有在jsx里用到时,才会被加载进来,如果不使用就不会被加载

    head 组件

    那为了更好的进行SEO优化,可以自己定制标签

    创建header.js页面

    Next.js已经把封装好了,本身就是一个组件,可以直接

    import Head from 'next/head'
    
    const Header = ()=>{
        return (
            <>
                
                     头部    
                
            
        )
    }
    
    export default Header
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    Next.js框架下使用Ant Design UI

    Ant Design是一款阿里开源的前端组件库

    从React的角度来讲,它就是一个组件库,里边封装了开发中最常用的一些组件,让我们可以通过简单的配置就可以使用他们

    让Next.js 支持引入CSS文件

    首先创建一个 pages/_app.js(如果不存在的话)。 然后import 该 styles.css 文件。

    样式表的全局特性

    旧版本可以通过 @zeit/next-sass 支持css,这个在新版本中已移除

    Next.js 通过 [name].module.css 文件命名约定来支持 CSS 模块

    CSS 模块通过自动创建唯一的类名从而将 CSS 限定在局部范围内。 这使您可以在不同文件中使用相同的 CSS 类名,而不必担心冲突。

    此行为使 CSS 模块成为包含组件级 CSS 的理想方法。 CSS 模块文件 可以导入(import)到应用程序中的任何位置

    不加module next.js框架会误以为是全局样式,会引发冲突报错

    import styles from '@/styles/test.module.css'
    
    const Ant = () => {
        return (
            <>
                
    Ant 页面

    测试

    ) } export default Ant
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    支持scss

    安装scss

    npm install sass
    
    • 1

    用法与 css一致

    import styles from '@/styles/test.module.scss'
    
    const Ant = () => {
        return (
            <>
                
    Ant 页面

    测试

    ) } export default Ant
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    安装 ant

    npm install antd --save
    
    • 1

    引入 ant 并使用

    新建react.js 页面

    import React from 'react';
    import { DatePicker } from 'antd';
    
    const App = () => {
      return ;
    };
    
    export default App;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    babel

    为了不让webpack 把整个Ant Design的包都进行打包到生产环境

    我们需要你用到 babel

    npm install --save babel-plugin-import
    
    • 1

    在项目根目录建立.babelrc文件

    {
        "presets":["next/babel"],  //Next.js的总配置文件,相当于继承了它本身的所有配置
        "plugins":[     //增加新的插件,这个插件就是让antd可以按需引入,包括CSS
            [
                "import",
                {
                    "libraryName":"antd"
                }
            ]
        ]
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    这样我们使用那个组件就打包那个组件,同样CSS也是按需打包的

    Next.js生产环境打包

    配置package.json 文件夹

    "start": "next start -p 8088" 
    
    • 1

    运行打包

    npm run build
    
    • 1

    运行打包好的文件

    npm run start
    
    • 1
  • 相关阅读:
    【HCIE】15.OSPFV3
    3. 一级缓存解析
    推箱子问题
    “用爱发电”难以为继?开源还需要真金白银投入
    RPC分布式网络通信框架项目
    STL容器——priority_queue的模拟实现
    PaddleSeg (2) 模型训练
    Oracle/PLSQL: Sinh Function
    项目场景 with ERRTYPE = cudaError CUDA failure 999 unknown error
    .NET周报 【5月第2期 2023-05-14】
  • 原文地址:https://blog.csdn.net/weixin_44872023/article/details/132628279