跟文档学习next.js
Posted thomas-yang-github
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了跟文档学习next.js相关的知识,希望对你有一定的参考价值。
前言:Next.js 是一个轻量级的 React 服务端渲染应用框架。
新建文件夹安装它:
npm install --save next react react-dom
将下面脚本添加到 package.json 中:
"scripts": "dev": "next", "build": "next build", "start": "next start"
这里注意。启动项目服务后,react默认的端口号是3000,而next的入口文件默认是隐藏的,这点后面再说。所以如果你有项目端口已经用3000的端口号,那么可以修改package.json 中的脚本:
"scripts": "dev": "next -p 2019", "build": "next build", "start": "next start"
如上,在dev后面添加“-p 端口号”即可;
由于Next.js 只支持React 16,我们使用 React 16 的特性,所以不得不放弃对 React 15 以及以下版本的支持。
Next.js 具有以下几点特性:
- 默认支持服务端渲染
- 自动根据页面进行代码分割
- 简洁的客户端路由方案(基于页面
- 基于 Webpack 的开发环境,支持热模块替换
- 可以跟 Express 或者其它 Node.js 服务器完美集成
- 支持 Babel 和 Webpack 的配置项定制
- 文件系统是主要的 API. 每个.js 文件将变成一个路由,自动处理和渲染。
- 以pages文件作为服务端的渲染和索引
- 热加载
- 静态文件服务.
./static/
映射到/static/
(可以 创建一个静态项目文件夹在你的项目中,里面放置一些图片和字体等)
继续:新建 ./pages/index.js
到你的项目中;
在我们的项目中,肯定不止一个项目的。那么我们就需要在多个页面之间进行跳转,就要借助next/link
组件,下面我们新增一个页面game页面并将index页面进行改写;
game页面
Link标签支持任意react组件作为其子元素,不一定要用a标签,只要该子元素能响应onClick事件,Link标签不支持添加style和className等属性,如果要给链接增加样式,需要在子元素上添加;
由于next支持热加载,所以直接保存就可以看到效果哦;
现在已经可以正常跳转了,那么就需要传递参数了,如果需要给路由传参数,则使用query string
的形式:
static async getInitialProps ( req ) const userAgent = req ? req.headers[‘user-agent‘] : navigator.userAgent return userAgent render () return ( <div className="app-box"> <p>我是next.js页面</p> <Link href="/game?title=hello"> <a>To Game Page</a> </Link> <p>this.props.userAgent</p> </div> )
static async getInitialProps ( req, query ) const userAgent = req ? req.headers[‘user-agent‘] : navigator.userAgent return userAgent, query render () console.log(this.props.query); return ( <div className="game-box"> <p>我是game页面</p> <Link href="/"> <a>To index Page</a> </Link> <p>this.props.query.title</p> </div> )
从上面两段代码可以看到:
- 当页面渲染时加载数据,我们使用了一个异步方法getInitialProps。它能异步获取 JS 普通对象,并绑定在props上;
- 当服务渲染时,
getInitialProps
将会把数据序列化,就像JSON.stringify
。所以确保getInitialProps
返回的是一个普通 JS 对象,而不是Date
,Map
或Set
类型; - 当页面初次加载时,
getInitialProps
只会在服务端执行一次。getInitialProps
只有在路由切换的时候(如Link
组件跳转或路由自定义跳转)时,客户端的才会被执行; - 当页面初始化加载时,
getInitialProps
只会加载在服务端。只有当路由跳转(Link
组件跳转或 API 方法跳转)时,客户端才会执行getInitialProps;
- 注意:
getInitialProps
将不能使用在子组件中。只能使用在pages
页面中。
getInitialProps
入参对象的属性如下:
pathname
:URL 的 path 部分;query
: URL 的 query 部分,并被解析成对象;asPath
: 显示在浏览器中的实际路径(包含查询部分),为String
类型;req
: HTTP 请求对象 (只有服务器端有);res
: HTTP 返回对象 (只有服务器端有);jsonPageRes
:获取数据响应对象(只有客户端有);err
: 渲染过程中的任何错误;
当然除了上面的Link跳转之外,还可以通过事件跳转,那么就需要借助next/router
实现客户端路由切换:
import Router from ‘next/router‘ export default () => <div> Click <span onClick=() => Router.push(‘/game‘)>here</span> to read more </div>
这个Router
对象的 API 如下:
route
: 当前路由的String
类型;pathname
: 不包含查询内容的当前路径,为String
类型;query
: 查询内容,被解析成Object
类型. 默认为;
asPath
: 展现在浏览器上的实际路径,包含查询内容,为String
类型;push(url, as=url)
: 页面渲染第一个参数 url 的页面,浏览器栏显示的是第二个参数 url;replace(url, as=url)
: performs areplaceState
call with the given url;beforePopState(cb=function)
: 在路由器处理事件之前拦截;
注意:Router.beforePopState() 截断Router操作的设置只有在客户端生效(需在componentDidMount中设置)且进入此函数中的方法只有Router栈中有值的时候才可以!
push
和 replace
函数的第二个参数as
,是为了装饰 URL 作用。如果你在服务器端设置了自定义路由将会起作用;
ok,通过上面的了解,我们已经学会了页面跳转的几种方式,既然可以跳转,那么可以监听跳转吗?即路由事件:
监听路由相关事件。 下面是事件支持列表:
routeChangeStart(url)
--- 路由开始切换时触发routeChangeComplete(url)
--- 完成路由切换时触发routeChangeError(err, url)
--- 路由切换报错时触发beforeHistoryChange(url)
--- 浏览器 history 模式开始切换时触发hashChangeStart(url)
--- 开始切换 hash 值但是没有切换页面路由时触发hashChangeComplete(url)
--- 完成切换 hash 值但是没有切换页面路由时触发
我们尝试调用路由开始切换的事件:routeChangeStart
// 监听路由开始切换时触发 static onListenRouteChangeStart = () => Router.events.on(‘routeChangeStart‘, (url) => console.log(‘routerUrl‘ + url); );
componentDidMount () RouterApi.onListenRouteChangeStart();
如果不需要监听了,请释放它:Router.onRouteChangeStart = null;
既然都聊都了路由这块,那么就说说路由的另一个概念:浅层路由;
所谓浅路由模式,其实就是浅层路由允许你改变 URL 但是不执行getInitialProps
生命周期。你可以加载相同页面的 URL,得到更新后的路由属性pathname
和query
,并不失去 state 状态。
你可以给Router.push
或 Router.replace
方法加如:shallow: true
参数。如下面的例子所示:
const href = ‘/?counter=10‘ const as = href Router.push(href, as, shallow: true );
现在 URL 更新为/?counter=10
。在组件里查看this.props.router.query
你将会看到更新的 URL。
!需要注意的是:浅路由模式只支持相同的 URL,如果页面路由变化,还是会触发 getInitialProps;
更多的使用场景,可能类似与分页数据,我们只改变 page
,而不需要整个初始化。当然,我们可以监听:
componentWillReceiveProps(nextProps) const pathname, query = nextProps.url // fetch data based on the new query
你可以在componentdidupdate钩子函数中监听 URL 的变化:
componentDidUpdate(prevProps) const pathname, query = this.props.router // verify props have changed to avoid an infinite loop if (query.id !== prevProps.router.query.id) // fetch data based on the new query
对于路由的一些基本情况如上了,有具体问题具体分析;
那么下面我们来练习一下高阶组件:如果你想应用里每个组件都处理路由对象,你可以使用withRouter
高阶组件。下面是如何使用它;
import React, Component from ‘react‘; import withRouter from ‘next/router‘ class RedEnvelope extends Component render () return ( <div className="envelope-box"> <p>我是红包组件</p> <h1>this.props.router.query.title</h1> </div> ) export default withRouter(RedEnvelope);
我在game页面调用这个组件里面调用到this.props.router这个对象了:
ok,了解这些,就开始写项目吧,后面的配置和部署,接下来再更新
以上是关于跟文档学习next.js的主要内容,如果未能解决你的问题,请参考以下文章