Nextjs入门

Next.js

Next.js 是一个轻量级的 React 服务端渲染应用框架。有了它我们可以简单轻松的实现React的服务端渲染,从而加快首屏打开速度,也可以作SEO(收索引擎优化了)。在没有Next.js的时候,用React开发需要配置很多繁琐的参数,如Webpack配置,Router配置和服务器端配置等….。如果需要作SEO,要考虑的事情就更多了,怎么样服务端渲染和客户端渲染保持一致就是一件非常麻烦的事情,需要引入很多第三方库。但有了Next.js,这些问题都解决了,使开发人员可以将精力放在业务逻辑上,从繁琐的配置中解放出来。

简介

Next.js 是一个轻量级的 React 服务端渲染应用框架。

用一个框架,就要知道它的优点(或者是解决了我们什么问题):

  • 完善的React项目架构,搭建轻松。比如:Webpack配置,服务器启动,路由配置,缓存能力,这些在它内部已经完善的为我们搭建完成了。
  • 自带数据同步策略,解决服务端渲染最大难点。把服务端渲染好的数据,拿到客户端重用,这个在没有框架的时候,是非常复杂和困难的。有了Next.js,它为我们提供了非常好的解决方法,让我们轻松的就可以实现这些步骤。
  • 丰富的插件帮开发人员增加各种功能。每个项目的需求都是不一样的,包罗万象。无所不有,它为我们提供了插件机制,让我们可以在使用的时候按需使用。你也可以自己写一个插件,让别人来使用。
  • 灵活的配置,让开发变的更简单。它提供很多灵活的配置项,可以根据项目要求的不同快速灵活的进行配置。

目前Next.js是React服务端渲染的最佳解决方案,所以如果想使用React来开发需要SEO的应用,基本上就要使用Next.js。

create-next-app 快速创建Next.js项目

项目结构介绍

  • components文件夹:这里是专门放置自己写的组件的,这里的组件不包括页面,指公用的或者有专门用途的组件。
  • node_modules文件夹:Next项目的所有依赖包都在这里,一般我们不会修改和编辑这里的内容。
  • pages文件夹:这里是放置页面的,这里边的内容会自动生成路由,并在服务器端渲染,渲染好后进行数据同步。
  • public文件夹: 这个是静态文件夹,比如项目需要的图片、图标和静态资源都可以放到这里。
  • .gitignore文件: 这个主要是控制git提交和上传文件的,简称就是忽略提交。
  • package.json文件:定义了项目所需要的文件和项目的配置信息(名称、版本和许可证),最主要的是使用npm install 就可以下载项目所需要的所有包。

Page和Component的使用

直接在pages文件夹下,新建一个xxx.js页面,写好相应的代码后,Next框架就自动做好了路由。如果要做更深的路由,在pages文件夹下再建相应的文件夹,然后在新的文件夹里面新建页面,以此类推即可。

pages里面的文件名和components文件夹里面的文件名可以相同,不会冲突。

在components文件夹目录新建组件,在pages里面引入即可。

路由-基础和基本跳转

路由跳转两种形式:

  • 标签式导航:利用标签<Link>

    引入import Link from 'next/link'

    使用<Link href="/"><a>返回首页</a></Link>

    注意:不写<a>标签时不支持兄弟标签并列,使用<a>便可使用

    1
    2
    3
    4
    5
    6
    <Link href="/jspangA">
    <a>
    <span>去JspangA页面</span>
    <span>前端博客</span>
    </a>
    </Link>
  • 编程式跳转:用js编程的方式进行跳转,也就是利用Router组件

    引入import Router from 'next/router'

    使用: <button onClick={()=>{Router.push('/jspangA')}}>去JspangA页面</button>这种写法简单,但是耦合性太高,可以修改一下 ,把跳转放到一个方法里,然后调用方法。

    1
    2
    3
    4
    5
    function gotoA(){
    Router.push('/jspangA')
    }

    <button onClick={gotoA}>去JspangA页面</button>

路由-跳转时用query传递和接收参数

项目开发中一般需要动态跳转,动态跳转就是跳转时需要带一个参数或几个参数过去,然后在到达的页面接受这个传递的参数,并根据参数不同显示不同的内容。比如新闻列表,然后点击一个要看的新闻就会跳转到具体内容。这些类似这样的需求都都是通过传递参数实现的。

只能用query传递参数:在Next.js中只能通过通过query(?id=1)来传递参数,而不能通过(path:id)的形式传递参数。

标签式导航传递参数:

1
2
3
<Link href="/jpangA?name=技胖"><a>技胖</a></Link>
或者(优雅的写法)
<Link href={{pathname:'/jpangA',query:{name:'技胖'}}}><a>技胖</a></Link>

这种写法有两个参数,一个是pathname,一个是queryquery里面包含传递的参数

编程式跳转传递参数:

1
2
3
4
5
6
7
function gotojpang(){
Router.push('/jpangA?name=技胖')
}

<div>
<button onClick={gotojpang}>技胖</button>
</div>

​ 或者:

1
2
3
4
5
6
7
8
function gotojpang(){
Router.push({
pathname:'/jpangA',
query:{
name:'技胖'
}
})
}

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//①引入withRouter
import {withRouter} from 'next/router'
import Link from 'next/link'

//②这里要传入参数router
const Jspang = ({router})=>{
return (
<>
//③通过这种形式获得参数
<div>{router.query.name},他来了</div>
<Link href="/"><a>返回首页</a></Link>
</>
)
}

//④导出要使用withRouter
export default withRouter(Jspang)

路由-六个钩子事件

路由的钩子事件,也就是当路由发生变化时,可以监听到这些变化事件,执行对应的函数。利用钩子事件是可以作很多事情的,比如转换时的加载动画,关掉页面的一些资源计数器…..

  • routerChangeStart路由发生变化时

    使用Router组件,然后用on方法来进行兼听

  • routerChangeComplete路由结束变化时

  • beforeHistoryChange浏览器history触发前

    history就是HTMP中的API,Next.js路由变化默认都是通过history进行的,所以每次都会调用。 不适用history的话,也可以通过hash。

    正确顺序为:①③②

  • routeChangeError路由跳转发生错误时

    注意:404找不到路由页面不算错误

转变成hash路由模式

以下两种事件都是针对hash的。

  • hashChangeStart:hash跳转开始时执行
  • hashChangeComplete:hash跳转完成时执行
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
46
47
48
49
50
51
52
import React from 'react'
import Link from 'next/link'
import Router from 'next/router'

function gotojpang(){
Router.push('/jspang?name=技术胖')
}

//使用Router组件,on方法进行兼听
Router.events.on('routeChangeStart', (...args) => {
console.log('1.routeChangeStart->路由开始变化,参数为:', ...args)
})

Router.events.on('routeChangeComplete', (...args) => {
console.log('2.routeChangeComplete->路由结束变化,参数为:', ...args)
})

Router.events.on('beforeHistoryChange', (...args) => {
console.log('3,beforeHistoryChange->在改变浏览器 history之前触发,参数为:', ...args)
})

Router.events.on('routeChangeError', (...args) => {
console.log('4,routeChangeError->跳转发生错误,参数为:', ...args)
})

Router.events.on('hashChangeStart', (...args) => {
console.log('5,hashChangeStart->hash跳转开始时执行,参数为:', ...args)
})

Router.events.on('hashChangeComplete', (...args) => {
console.log('6,hashChangeComplete->hash跳转完成时,参数为:', ...args)
})
const Home = () => (

<>
<div>我是首页</div>
<div>
<Link href="/jspang?name=技胖"><a>技胖</a></Link>
<Link href="/jspang?name=术胖"><a>术胖</a></Link>
</div>
<div>
<button onClick={gotojpang}>技胖</button>
</div>

//hash模式
<div>
<Link href="#jspang"><a>技术胖</a></Link>
</div>
</>
)

export default Home

在getInitialProps中使用Axios获取远端数据

Next.js框架中提供了getInitialProps静态方法用来获取远端数据,这个是框架的约定,所以你也只能在这个方法里获取远端数据。不要在生命周期里面获得,虽然可以,但是不符合约定。

安装axios: yarn add axios

引入import axios from 'axios'

获取数据例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//使用getInitialProps远程获取数据
Jspang.getInitialProps = async ()=>{
const promise =new Promise((resolve)=>{
axios('https://www.easy-mock.com/xxx').then(
(res)=>{
console.log('远程数据结果:',res)
resolve(res.data.data)
}
)
})
return await promise
}

//获取数据之后,把得到的数据传递给页面组件,用{}显示出来就可以了。
//res.data.data里面有一个list数组
const Jspang = ({router,list})=>{
return (
<>
<div>{router.query.name},来了 .<br/>{list}</div>//直接就可以使用
<Link href="/"><a>返回首页</a></Link>
</>
)
}

使用Style JSX编写页面的CSS样式

Next.js中引入一个CSS样式是不可以用的,如果想用,需要作额外的配置。因为框架为我们提供了一个style jsx特性,也就是把CSS用JSX的语法写出来。

一个特性:自动添加随机类名,不会污染全局css—加入了Style jsx代码后,Next.js会自动加入一个随机类名,这样就防止了CSS的全局污染。比如我们把代码写成下面这样,然后在浏览器的控制台中进行查看,你会发现自动给我们加入了类名,类似jsx-xxxxxxxx

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function Jspang(){
return (
<>
<div>技术胖免费前端教程</div>
<div className="jspang">技术胖免费前端教程</div>
//使用这种形式些样式
<style jsx>
{`
div { color:blue;}
.jspang {color:red;}
`}
</style>
</>
)
}
export default Jspang

动态显示样式

比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import React, {useState} from 'react'
function Jspang(){
const [color,setColor] = useState('blue')
const changeColor=()=>{
setColor(color=='blue'?'red':'blue')
}
return (
<>
<div>技术胖免费前端教程</div>
<div><button onClick={changeColor}>改变颜色</button></div>
<style jsx>
{`
div { color:${color};}
`}
</style>
</>
)
}
export default Jspang

Lazy Loading实现模块懒加载

当项目越来越大的时候,模块的加载是需要管理的,如果不管理会出现首次打开过慢,页面长时间没有反应一系列问题。这时候可用Next.js提供的LazyLoading来解决这类问题。让模块和组件只有在用到的时候在进行加载,一般我把这种东西叫做“懒加载”.它一般分为两种情况,一种是懒加载(或者说是异步加载模块,另一种是异步加载组件

当我们作的应用存在首页打开过慢和某个页面加载过慢时,就可以采用Lazy Loading的形式,用懒加载解决这些问题。

懒加载模块

比如我们引入开发中常用的模块Moment.js,它是一个JavaScript日期处理类库。安装:yarn add moment

例如(未使用懒加载):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import React, {useState} from 'react'
import moment from 'moment' //引入模块

function Time(){

const [nowTime,setTime] = useState(Date.now())

const changeTime=()=>{
//使用
setTime(moment(Date.now()).format())
}
return (
<>
<div>显示时间为:{nowTime}</div>
<div><button onClick={changeTime}>改变时间格式</button></div>
</>
)
}
export default Time

这个看起来很简单和清晰的案例,缺存在着一个潜在的风险,就是如何有半数以上页面使用了这个momnet的库,那它就会以公共库的形式进行打包发布,就算项目第一个页面不使用moment也会进行加载,这就是资源浪费,对于我这样有代码洁癖的良好程序员是绝对不允许的。下面我们就通过Lazy Loading来进行改造代码。

改良(使用懒加载):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import React, {useState} from 'react'
//删除import moment
function Time(){
const [nowTime,setTime] = useState(Date.now())

const changeTime= async ()=>{ //把方法变成异步模式
const moment = await import('moment') //等待moment加载完成
setTime(moment.default(Date.now()).format()) //注意使用defalut
}
return (
<>
<div>显示时间为:{nowTime}</div>
<div><button onClick={changeTime}>改变时间格式</button></div>
</>
)
}
export default Time

懒加载自定义组件

首先要在懒加载这个组件的文件汇总引入dynamicimport dynamic from 'next/dynamic'

使用:

1
2
3
4
5
6
7
8
9
10
11
import dynamic from 'next/dynamic' //引入dynamic
//使用如下方式引入组件
const One = dynamic(import('../components/one'))
function Time(){
return (
<>//使用
<One/>
</>
)
}
export default Time

写完代码后,可以看到自定义组件是懒加载的,只有在jsx里用到``时,才会被加载进来,如果不使用就不会被加载。

自定义Head更加友好的SEO操作

定制<Head>标签

  • 方法一:在各个页面加上<Head>标签(推荐)

    例如在pages中的某一个页面:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    import Head from 'next/head' //引入Head
    function Header(){
    return (
    <>
    //使用Head
    <Head>
    <title>技术胖是最胖的!</title>
    <meta charSet='utf-8' />
    </Head>
    <div>JSPang.com</div>
    </>
    )
    }
    export default Header
  • 方法二:定义全局的<Head>

    这种方法相当于自定义了一个组件,然后把在组件里定义好,以后每个页面都使用这个组件,其实这种方法用处不大,也不灵活。因为Next.js已经把<Head>封装好了,本身就是一个组件,我们再次封装的意义不大。

Next.js框架下使用Ant Design UI

让Next.js支持CSS文件

安装@zeit/next-css包,它的主要功能就是让Next.js可以加载CSS文件,有了这个包才可以进行配置:yarn add @zeit/next-css

配置:建立一个next.config.js(建在最外面的那个目录)这个就是Next.js的总配置文件。

1
2
3
4
5
6
7
const withCss = require('@zeit/next-css')

if(typeof require !== 'undefined'){
require.extensions['.css']=file=>{}
}

module.exports = withCss({})

按需加载Ant Design

加载Ant Design在我们打包的时候会把Ant Design的所有包都打包进来,这样就会产生性能问题,让项目加载变的非常慢。这肯定是不行的,现在的目的是只加载项目中用到的模块,这就需要我们用到一个babel-plugin-import文件。

先安装Ant Design库:yarn add antd

安装babel-plugin-import插件:yarn add babel-plugin-import

配置babel-plugin-import插件:在项目根目录建立.babelrc文件,然后写入如下配置文件。

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

配置好了以后,webpack就不会默认把整个Ant Design的包都进行打包到生产环境了,而是我们使用那个组件就打包那个组件,同样CSS也是按需打包的。

使用:

1
2
3
4
5
6
7
8
import {Button} from 'antc'
function Page(){
return(
<>
<Button>我是按需引入的按钮</Button>
</>
)
}

Next.js生产环境打包

其实Next.js大打包时非常简单的,只要一个命令就可以打包成功。但是当你使用了Ant Desgin后,在打包的时候会遇到一些坑。

打包 :next build

运行:next start -p 80

先把这两个命令配置到package.json文件里,比如配置成下面的样子。

1
2
3
4
5
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start -p 80"
},

然后在终端里运行一下yarn build,如果这时候报错,其实是我们在加入Ant Design的样式时产生的,你可以改用全局引入CSS解决问题。

在page目录下,新建一个_app.js文件,然后写入下面的代码。

1
2
3
4
5
import App from 'next/app'

import 'antd/dist/antd.css'

export default App

这样配置一下,就可以打包成功了,然后再运行yarn start来运行服务器,看一下我们的header页面,也是有样式的。说明打包已经成功了。