next.js从入门到实战

4,650 阅读10分钟

官网上Next.js简介:Next.js 是一个轻量级的React服务端渲染框架

首先整理了一下各种渲染方式的优缺点

一) 服务端渲染/客户端渲染/同构渲染的优缺点

I )为什么要使用服务端来渲染,有什么优点?

至于为什么要服务端渲染,我相信大家都有所闻,而且每个人都能说出几点来。

  • 首屏等待

在 SPA 模式下,所有的数据请求和 Dom 渲染都在浏览器端完成,所以当我们第一次访问页面的时候很可能会存在“白屏”等待,而服务端渲染所有数据请求和 html内容已在服务端处理完成,浏览器收到的是完整的 html 内容,可以更快的看到渲染内容,在服务端完成数据请求肯定是要比在浏览器端效率要高的多。

  • SEO的优化

    有些网站的流量来源主要还是靠搜索引擎,所以网站的 SEO 还是很重要的,而 SPA 模式对搜索引擎不够友好,要想彻底解决这个问题只能采用服务端直出。


II)服务端渲染的优缺点

1)服务端渲染的优点
  • 前端渲染时间。因为后端拼接htm,浏览器只需直接渲染出来。
  • 有利于SEO。服务端有完整的html页面,所以爬虫更容易获得信息,更利于seo
  • 无需占用客户端资源,模板解析由后端完成,客户端只需解析标准的html页面,这样对客户端的资源占用更少,尤其是移动端,可以更加省电。
  • 后端生成静态化文件。即生成缓存片段,这样就可以减少数据库查询浪费的时间了,且对于数据变化不大的页面非常高效 等。
2)服务端渲染缺点
  • 不利于前后端分离,开发效率很低。
  • 占用服务器端资源。请求过多对服务器压力很大。
  • 即使局部页面发生变化也需要重新请求整个页面,费流量等。

III )客户端渲染的优缺点

1)客户端渲染的优点
  • 节省后端资源
  • 多端渲染
  • 前后端分离,大大提升开发效率
  • 局部刷新等
2)客户端渲染的缺点
  • 首屏性能差
  • 白屏时间过长
  • 无法(很艰难)进行seo优化等

IV)Next/nust.js是完全属于服务端渲染吗?

对比一下就会发现一个很有意思的事,服务端渲染的优点就是客户端渲染的缺点,服务端渲染的缺点同时也是客户端渲染的优点,反之亦然。看之前不是很理解,由于服务端渲染的各种缺点我们已经告别了服务端渲染的时代,进入了前后端分离的客户端渲染时代,又为什么重新回到了服务端渲染?

了解发现,其实把Next/nust.js成为SSR不是很准确,应该是Isomorphic render(同构渲染),那么什么是同构渲染?


V)什么是同构渲染

前后端同构是指在前后端维护同一份代码。它是在SPA的基础上,利用服务端渲染(SSR)直出首屏,解除单页面应用(SPA)在首屏渲染上面临的窘境。明确地说,同构是将传统的纯服务端直出的首屏优势和SPA的站内体验优势结合起来,以取得最优解的解决方案。

同构渲染就是实现了ssr和spa两种技术的结合,同时可以最大限度的重用代码(同构),减少开发维护成本,采用 react 或者 vue 等前端框架相结合 node (ssr) 来实现。


二)Next初探

I )next.js 的渲染流程


II)next.js的优点

  • 完善的React项目架构,搭建轻松。
  • 自带数据同步策略,解决服务端渲染最大难点.
  • 配置灵活
  • 丰富的插件帮开发人员增加各种功能。

III)快速创建Next.js项目

1 )Setup

要创建 Next.js 应用,请打开你的终端窗口,cd 进入您要在其中创建应用程序的目录,然后运行以下命令:

npm init next-app nextjs-blog --example "https://github.com/vercel/next-learn-starter/tree/master/learn-starter"

其背后的工作是通过调用 create-next-app 工具完成的,该工具为你创建了一个 Next.js 应用程序。

到这里项目就创建成功了

2)运行开发服务
cd nextjs-blog
yarn dev


运行成功ing.....


III)页面间导航

在pages下新建一个test.js的页面,页面可以随意输入一些内容,这里按照hooks的方式编写你的函数型组件

function Text (){
    return(
        <h3>测试</h3>
    )
}
export default Text;

然后在网页的地址栏中http://localhost:3000/test 然后我们发现,页面上已经显示test.js的页面内容了,这是因为Next框架就自动作好了路由,这个也算是Next的一个重要优点。发现Next框架真的减轻了我们大量的工作。


IV)引入个小组件

可以再根目录下创建一个components的文件夹,来存放我们的公用组件。 先来创建一个button的组件,首先在components文件夹下创建button.js文件,然后编辑页面,先按最简单的写法来创建一个按钮

export default ({children})=><button>{children}</button>

小组件创建完成后,在pages下的index.js页面引入该文件

import Button from '../components/button'

然后在页面上引入该组件

<Button>按钮</Button>

然后发现已经在页面上渲染了,好想和react并没有什么区别。也就是说Next框架并没有给我们带来太多的学习成本,但是为我们减轻了很多配置工作。


V)路由-基本跳转

1)标签式导航< Link >

首先引入标签式导航< Link >

import Link from 'next/link'

引入之后尝试跳转刚才的test.js 页面

<Link href="/test">Test</Link>

跳转成功,但是要记录一个< Link >的小坑,发现< Link >下如果不写内容,或者写多个标签都会报错

<Link href="/test"></Link>
<Link href="/test">
   <span>test`1</span>
   <span>test`2</span>
 </Link>

所以Link标签下有且只能有一个子元素

2)Router模块进行编程式导航

首先也是引入Router模块

import Router from 'next/router'

然后尝试跳转

<button onClick={()=>{Router.push('/test')}}>去test页面</button>

两种方法都可以跳转,可以根据业务需求具体选择


Ⅵ)路由-query传参

首先是标签式< Link >传参

<Link href="/test?id=123">
     <span>test</span>
</Link>

或者

<Link href={{pathname:'/test',query:{id:'123'}}}>
    <span>test</span>
</Link>

然后变成是导航传参

<button onClick={()=>{Router.push('/test?id=123')}}>去test页面</button>

或者

<button onClick={()=>{Router.push({pathname:'/test',query:{id:'123'}})}}>去test页面</button>

好像没啥区别.......


Ⅶ)路由-接收参数

接收参数 需要用到withRouter 它是Next.js框架的高阶组件,用来处理路由的。 传递门之高阶组件
接下来重新编写test.js页面 首先引入withRouter

import { withRouter } from 'next/router'

然后在页面中使用

import { withRouter } from 'next/router'
import Link from 'next/link'

const  Test = ({router}) =>{
    return(
        <div>
            <p>{router.query.id}</p>
            <Link href={{pathname:'/'}}>
                <span>回首页</span>
            </Link>
        </div>
    )
}
export default withRouter(Test)

这里可以看到接收到的id值123


Ⅷ)路由-钩子事件

next.js的路由钩子一共有六个

当路由发生变化时会触发钩子事件,这里用到Router的on方法来监听,钩子事件第二个参数为路由参数,这里来编辑index.js页面。

可以通过Router监听路由器内部发生的不同事件

// 监听
Router.events.on('routeChangeStart', handleRouteChange)
// 关闭
Router.events.off('routeChangeStart', handleRouteChange)
1)routeChangeStart

路由开始发生变化时触发

 Router.events.on('routeChangeStart',(...args)=>{
   console.log('路由开始变化',...args)
})

##### 2)routeChangeComplete 路由结束变化时触发 ``` Router.events.on('routeChangeComplete',(...args)=>{ console.log('路由结束变化',...args) }) ```
##### 3) beforeHistoryChange 在改变浏览器 history之前触发 ``` Router.events.on('beforeHistoryChange,(...args)=>{ console.log('浏览器 history改变之前',...args) }) ```
4) routeChangeError

跳转发生错误触发

Router.events.on('routeChangeError,(...args)=>{
  console.log('跳转发生错误',...args)
})

5) hashChangeStart

hash模式路由改变刚开始

Router.events.on('hashChangeStart,(...args)=>{
  console.log('hash模式路由改变刚开始',...args)
})

6) hashChangeComplete

hash模式路由改变结束

Router.events.on('hashChangeComplete,(...args)=>{
  console.log('hash模式路由改变结束',...args)
})

***

Ⅸ)getInitialProps 获取远程数据

我们通常需要从远程数据源获取数据.Next.js 自己有标准 API 来获取页面数据.我们通常使用异步函数 getInitialProps 来完成此操作 .这样,我们可以通过远程数据源获取数据到页面上,并将其作为 props 传递给我们的页面.getInitialProps 在服务器和客户端上均可使用.

可以使用fetch或者axios,看个人习惯

1) fetch

首先安装fetch

npm install --save isomorphic-unfetch

然后再页面上使用

  Home.getInitialProps = async function (){
     const res = await fetch('https://api.tvmaze.com/search/shows?q=batman');
     const data = await res.json();
     console.log(data.map(entry => entry.show))
     
   }
2) axios

也可以使用axios 首先安装axios

npm install --save axios

然后在页面中使用

Home.getInitialProps = async ()=>{
const promise =new Promise((resolve)=>{
       axios('https://api.tvmaze.com/search/shows?q=batman').then(
           (res)=>{
               console.log('远程数据结果:',res.data)
               resolve({shows:res.data.map(entry => entry.show)})
           }
       )
})
return await promise
}

这里拿到数据后在页面上渲染。 接收props参数

<ul>
      {props.shows.map(show => (
        <li key={show.id}>
          <Link href="/detail/[id]">
            <a>{show.name}</a>
          </Link>
        </li>
      ))}
 </ul>

X)编写组件的样式

1)框架自带的一种语法style jsx ,来尝试一下让字体变成蓝色

直接写在return 中,可以在页面最外层添加一个<></>

<>
    <div className="container">
      <Test>按钮</Test>
    </div>
    <style jsx>
      {`
          button { color:blue;}
      `}
    </style>
    </>

然后运行一下即可看到 button的字体变成了蓝色。

2) 然后我们来动态改变一下组件的样式

首先引入react hooks中的useState

import React, {useState} from 'react'

然后来定义color 以及setColor

const [color,setColor] = useState('red')

加入点击事件

const changeColor=()=>{
    setColor(color === 'blue' ?'red':'blue')
}

和按钮button

<button onClick={changeColor}>改变字体颜色</button>

最后在style中写入变量color

 button { color:${color};}

运行页面,点击按钮发现button的字体颜色可以动态切换,很是方便。

f12打开控制台还会发现,加入了Style jsx代码后,Next.js会自动加入一个随机类名,这样就防止了CSS的全局污染。


3)尝试引入less 和 css

当页面比较复杂的时候,我们以jsx方式来编写样式,难免的会降低我们的开发效率,所以这里尝试一下引入less 首先安装less 和 css

npm install --save @zeit/next-less less
npm install --save @zeit/next-css

然后第二步在根目录下创建next.config.js配置文件 这里我们同时引入css和sass哦


const withCss = require('@zeit/next-css')

const withLess = require('@zeit/next-less')
if(typeof require !== 'undefined'){
    require.extensions['.css']=file=>{}
}

if (typeof require !== "undefined") {
   require.extensions[".withLess"] = file => {};
 }
module.exports = withLess(withCss({}))

小可爱这里记住这里要重启服务哦


Ⅺ)引入antd

首先安装antd依赖

npm install antd --save

然后来安装和配置babel-plugin-import 插件

npm install babel-plugin-import --save-dev

安装完成后,在项目根目录建立.babelrc文件,然后写入如下配置文件。

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

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

这里记住这里要重启服务哦 配置完成后我们在页面中尝试引入Button组件

import {Button} from 'antd'

然后使用

<Button onClick={changeColor}>改变字体颜色</Button>

嗯哼,这里就生效了


Ⅻ)部署 Next.js 应用

先安装 now,一个静态资源托管服务器

npm i -g now

打包

yarn build

结果发现报错了,其实是我们在加入Ant Design的样式时产生的,这个已经在Ant Design的Github上被提出了,但目前还没有被修改,你可以改完全局引入CSS解决问题。

解决办法

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

import App from 'next/app'

import 'antd/dist/antd.css'

export default App

再次运行yarn build 发现可以打包成功,最后运行

yarn start

可以在页面上运行