Next.js服务器端渲染
Next.js服务器端渲染
学习目标
- 了解Next.js的作用
- 掌握Next.js中的路由
- 掌握Next.js中布局组件的创建
- 掌握Next.js中的静态文件服务
- 掌握Next.js中获取页面数据的方法
- 掌握Next.js中组件样式的书写
- 使用Next.js完成豆瓣电影案例
- 能够自定义头部元素head
2.1 什么是Next.js?
Next.js是一个基于React的一个服务端渲染简约框架。它使用React语法,可以很好的实现代码的模块化,有利于代码的开发和维护。
Next.js带来了很多好的特性:
- 默认服务端渲染模式,以文件系统为基础的客户端路由
- 代码自动分割使页面加载更快
- 以webpack的热替换(HMR)为基础的开发环境
- 使用React的JSX和ES6的module,模块化和维护更方便
- 可以运行在Express和其他Node.js的HTTP 服务器上
- 可以定制化专属的babel和webpack配置
使用服务器端渲染好处:
- 对SEO友好
- 提升在手机及低功耗设备上的性能
- 快速显示首页
2.2 Next.js初体验
mkdir hello-next |
配置package.json中的scripts属性
{ |
此时npm run dev
会得到一个404页面
创建一个pages/index.js
const Index = () => ( |
创建一个pages/next-route/teacher.js页面
const Teacher = () => ( |
2.3 页面导航
2.3.1 路由跳转
- Link组件
- Link组件内不能直接写文字,必须使用标签包裹,标签可以是任何标签,但是必须只能保证Link组件内只有一个子元素;
- 给Link组件设置样式不会生效,因为Link组件是一个HOC(高阶组件),但是可以给它里面的子元素设置样式;
import Link from 'next/link' |
组件<Link>
可接收 URL 对象,而且它会自动格式化生成 URL 字符串。例如:
<Link href={{pathname: '/teachers', query: {id: 1}}}> |
- 命名式路由
import Router from 'next/router' |
URL对象语法:
Router.push({pathname: '/teacher', query: {id: 1}}) |
注意:如果没有匹配到的话,默认会去找_error.js
中定义的组件; 路由跳转不会向服务器发送请求页面的请求。
2.3.2 创建组件
普通组件
组件的创建可以在任何的文件夹下面,但是不要放在pages下面,因为组件并不需要url
布局组件
利用this.props.children
全局布局组件, 创建_app.js,模板入下:
import React from 'react'
import App, {Container} from 'next/app'
class Layout extends React.Component {
render () {
const {children} = this.props
return <div className='layout'>
{children}
</div>
}
}
export default class MyApp extends App {
render () {
const {Component, pageProps} = this.props
return <Container>
<Layout>
<Component {...pageProps} />
</Layout>
</Container>
}
}
2.3.3 query strings
- 创建一个带query的链接
- 如果你想应用里每个组件都处理路由对象,你可以使用
withRouter
高阶组件。从next/router中引入withRouter,注入路由对象到Next.js中的组件作为组件属性,从而获取query对象 - 组件使用props.router.query.xxx获取query strings
2.3.4 Clean URLs with Route Masking
通过as属性,给browser history来个路由掩饰,但是按刷新按钮路由就找不到了,因为服务器回去重新找/p/xxxx页面,但是实际上此时并不存在xxxx页面
// /post?title=xxxx 会变成 /p/xxxx |
2.3.5 服务器端支持Clean URLs
- 安装express
npm install --save express
- 创建server.js,添加如下内容
const express = require('express') |
- 修改package.json文件中scripts字段
"scripts": { |
- 创建自定义路由
server.get('/teacher/:id', (req, res) => { |
2.4 静态文件服务
项目的根目录新建 static
文件夹,代码通过 /static/
开头的路径来引用此文件夹下的文件,例如:
export default () => <img src="/static/logo.png" />
2.5 获取页面数据
- 下载isomorphic-unfetch :
npm install --save isomorphic-unfetch
- 引入
import fetch from 'isomorphic-unfetch';
使用异步静态方法getInitialProps
获取数据,此静态方法能够获取所有的数据,并将其解析成一个 JavaScript
对象,然后将其作为属性附加到 props
对象上
当页面初次加载时,getInitialProps
只会在服务端执行一次。getInitialProps
只有在路由切换的时候(如Link
组件跳转或命名式路由跳转)时,客户端的才会被执行。
注意:getInitialProps 不能 在子组件上使用,只能使用在pages
页面中。
// Index是一个组件 |
如果你的组件是一个类组件,你需要这样写:
export default class extends React.Component { |
getInitialProps
: 接收的上下文对象包含以下属性:
pathname
:URL
的path
部分query
:URL
的query string
部分,并且其已经被解析成了一个对象asPath
: 在浏览器上展示的实际路径(包括query
字符串)req
:HTTP request
对象 (只存在于服务器端)res
:HTTP response
对象 (只存在于服务器端)jsonPageRes
: 获取的响应数据对象 Fetch Response (只存在于客户端)err
: 渲染时发生错误抛出的错误对象
// 在另外一个组件中,可以使用context参数(即上下文对象)来获取页面中的query |
2.6 组件样式
css样式文件
css in js
styled-jsx
scoped
如果添加了
jsx
属性,只作用于当前组件,不包括子组件
<style jsx>{` |
global
作用于当前组件,包括子组件
<style jsx global>{``}</style> |
2.7 豆瓣电影案例
接口
获取电影列表:http://localhost:3301/in_theaters
(in_theaters可以替换为coming_soon及top250)
获取电影详情:http://localhost:3301/in_theaters/1?_embed=details
2.7.1 豆瓣电影首页
MovieHeader
组件样式
.movie-header { |
2.7.2 豆瓣电影列表页
.movie-type { |
2.7.3 豆瓣电影详情页
.detail { |
2.8 自定义头部元素head
引入next/head
export default () => { |
注意:在卸载组件时,<head>
的内容将被清除。请确保每个页面都在其<head>
定义了所需要的内容,而不是假设其他页面已经加过了