本文将会介绍next.js的路由相关内容,做为一个新手,笔者自知文章并无太多深层次的东西,只但愿帮助到跟笔者同样的入门级小伙伴。本文基础为上一篇next.js+koa2+antd环境轻松搭建javascript
在介绍路由以前,想先简单说一下目录结构,其中有些东西对于路由讲解仍是颇有帮助的css
├── .next
│ ├── build-manifest.json
│ ├── react-loadable-manifest.json
│ ├── server
│ └── static
├── components
│ ├── head.js
│ └── nav.js
├── pages
│ ├── _app.js
│ └── index.js
├── static
│ └── favicon.ico
├── server.js
├── .babelrc
├── .gitignore
├── next.config.js
├── package.json
├── README.md
└── yarn.lock
复制代码
这是next.js+koa2+antd环境轻松搭建一文中建立的next+koa2+antd的文件目录,其中的
README.md
,package.json
,yarn.lock
,.gitignore
就不说了。vue
pages
和components
pages
是next.js中很是重要的一个目录,其中每个js文件就表明一个页面,可是有两个例外,一个是上一篇文章中用到的_app.js
,一个是_document.js
。咱们在pages下再建立一个a.js和test/b.js,而后看看效果java
// a.js
export default () => <div>this is a page</div>
// test/b.js
export default () => <div>this is b page</div>
复制代码
可是咱们再写页面的时候不可能将全部东西都放在pages下,咱们能够将具体内容做为组件放在components
目录中,而后在pages
中相应的js文件中引入。若是是用脚手架工具生成项目的小伙伴能够很直观的看到components
目录中有head.js
和nav.js
两个组件,在pages/indx.js
中将其引入并使用react
.next: 在咱们运行过next.js项目以后,会自动生成.next
目录,这里存放的内容是next.js将咱们写的pages和components等源码打包编译后的结果,咱们经过浏览器访问能够拿到的内容其实就来自这里,在后续进行上限打包时候也会生成这个目录.git
Tips:请不要修改这个目录中的内容express
其余 staic
目录存放静态文件,好比图片,css,favicon.ico等 .babelrc
文件存放babel配置 next.config.js
存放next的配置 server.js
是咱们上一篇写Koa服务器程序的代码npm
Link
组件咱们能够删除index.js中的全部内容,重写为:json
import Link from 'next/link' //引入Link组件
import { Button } from 'antd' //引入antd中的Button组件
export default () => {
return (
<Link href="/a"> <Button>跳转到A</Button> </Link>
)
}
复制代码
这里咱们引入Link组件并将Button包裹在Link组件中,Link的href
属性可让咱们选择跳转到的路由,这里咱们/a
是跳转到咱们上文中建立的a.js对应的页面后端
值得说明的是,Link的机制并非在Button上增长了a标签实现的跳转,而是对其中的组件添加了click事件,而后在浏览器中建立一个路由,在最终渲染的结果上并不会对Button包裹任何元素,咱们也能够看到点击Button页面也不会有刷新的状况。
React.Children.only
规定了它所包含的元素只能有一个,若是咱们再并列Button写一个标签就会报错
<Link href="/a">
<Button>跳转到A</Button>
<Button>也跳转到A</Button>//报错
</Link>
复制代码
若是咱们有这种需求,能够将两个Button包裹在<div>
中
<div>
<Button>跳转到A</Button>
<Button>也跳转到A</Button>
</div>
复制代码
此时点击两个按钮都会跳转到a页面,(知道事件冒泡的小伙伴可能对为何都能跳转到a页面,不清楚的小伙伴能够去查查事件冒泡和捕获)
next.js为咱们提供的并不仅是
Link
组件,它还为咱们提供了Router的方式 Tips: 这里的Router不是一个组件而是一个路由对象,Link的实现原理也是基于Router对象
咱们先来看看代码:
import Link from 'next/link'
import Router from 'next/router' // 新引入进来的
import { Button } from 'antd'
export default () => {
const goToB = () => {
Router.push('/test/b')
}
return (
<> <Link href="/a"> <Button>跳转到A</Button> </Link> <Button onClick={goToB} >跳转到B</Button> // 新增的一个Button组件 </> ) } 复制代码
咱们能够看到新增了一个Button组件,它有一个goToB
的onClick事件,点击以后会执行goToB
函数。goToB
作了什么呢?它给Router添加了一个路由/test/b
对应咱们以前/test/b.js
渲染的内容,此时咱们就能够跳转到B页面了,是否是很简单。
在next.js中,没法经过
/test/:id
这种参数路由的方式获取到参数,它只能经过query
的方式获取参数,即/test?id=xx
的方式
写法:
<Link href="/a?id=1">
<Button>跳转到A</Button>
</Link>
复制代码
使用Router的方式也能够经过问号这种形式来写,不过还能够经过给Router.push()
传递一个对象来写
const goToB = () => {
Router.push({
pathname: '/test/b',
query: {
id: 2
}
})
}
复制代码
此时对应a页面的内容要得到传递过来的id=1,就得稍微改写一下:
import { withRouter } from 'next/router' //新引入的
const A = ({ router }) => <div>this is a page,参数是{router.query.id}</div>
export default withRouter(A)
复制代码
咱们引入了withRouter
组件,它是一个高阶组件(HOC),即参数为被包裹的组件,返回值也是一个组件(不了解的小伙伴能够去看看react文档中高阶组件的部分)。
咱们这里不直接导出A组件了,而是导出withRouter包裹后的组件,而且给A组件传入了router参数,经过router.query.id获取传递过来的id。 咱们能够看看效果
上面说过next.js中没有参数路由,只能经过
/test?id=xxx
来传递参数,可是咱们就是想用参数路由怎么办,虽然咱们作不到,可是能够模拟一下,经过/test/xxx
来传递参数
写法:
<Link href="/a?id=1" as="/a/1">
<Button>跳转到A</Button>
</Link>
复制代码
Router.push()的写法:
Router.push({
pathname: '/test/b',
query: {
id: 2
}
}, '/test/b/2')
复制代码
达到的效果是同样的
请注意 咱们将http://localhost:3000/a/1复制在新标签页打开,会出现404
为何会出现这种状况? 有没有小伙伴记得上文提到过,上文中说过一句话而后在浏览器中建立一个路由
此处不知道有没有小伙伴会问:既然next.js路由跳转都是在本地浏览器建立的路由,那为何经过Button点击进入的localhost:3000/a
复制以后在新标签页打开倒是正常的?这是由于服务器端咱们确实存在pages/a.js
啊,此处恰好浏览器端认识/a
路由,可是/a/1
加上id后浏览器端就没定义过这个pages/a/1.js
文件,因此什么都匹配不到
那这种状况该怎么处理,请不要担忧,咱们安装的koa会提供全面的路由,咱们只须要作个路由映射就行了,使用express或者egg.js也是一样的道理。
首先咱们执行npm install koa-router --save
或者yarn add koa-router
(笔者使用的是yarn)安装koa路由中间件 在server.js中引入并使用
/* * @Author: yishuai * @Date: 2019-04-21 09:57:53 * @Last Modified by: yishuai * @Last Modified time: 2019-04-21 12:22:22 */
const Koa = require('koa')
const Router = require('koa-router') // 引入路由
const next = require('next')
const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()
app.prepare().then(() => {
const server = new Koa()
const router = new Router() // 定义路由
// 设置路由,与next.js的路由进行映射
router.get('/a:id', async (ctx) => {
// handle传入的第三个参数跟咱们next.js中用Router.push({})传入的数组同样
await handle(ctx.req, ctx.res, {
pathname: '/a',
query: {
id
}
})
ctx.respond = false
})
// 使用路由
server.use(router.routes())
server.use(async (ctx, next) => {
await handle(ctx.req, ctx.res)
ctx.respond = false
})
server.listen(3000, () => {
console.log('server is running at http://localhost:3000')
})
})
复制代码
具体映射方式在上方代码的注释中,就不详细说了不熟悉koa的小伙伴去看看koa的文档,了解一下koa路由的使用。 而后打开浏览器新建页面从新输入localhost:3000/a/1
就能够正常访问了
这里说的钩子,相信大多数了解过react或vue生命周期的小伙伴都知道生命周期钩子,这些钩子函数在必定条件下会自动执行,咱们若是在某些生命周期过程当中有自定义的需求,能够借助生命钩子函数来完成。好比react中的
componentDidMount(){xxxxxx}
能够在组件加载完成后作xxxxxxxx
事情,好比当组件加载完成后咱们要验证用户是否是登陆了,就能够把xxxxxxx
替换成咱们的验证逻辑。这里的路由钩子也是同样的道理,当路由被触发的时候就会在路由跳转前,跳转后等时间节点自动执行一些函数,这些函数就是路由钩子
我在index.js中添加了
(不是重写index.js)这样一段代码:
// 全部的路由钩子名称,写在了一个数组中
const events = [
'routeChangeStart',
'routeChangeComplete',
'routeChnageError',
'beforeHistoryChange',
'hashChangeStart',
'hashChangeComplete'
]
// 经过一个高阶函数在钩子触发后执行自定义的逻辑,这里直接输出了钩子名称和钩子函数的参数
function makeEvent(type) {
return (...args) => {
console.log(type, ...args)
}
}
//经过forEach遍历 绑定钩子事件
events.forEach(event => {
Router.events.on(event, makeEvent(event))
})
复制代码
上面注释中说明了一些状况,咱们能够经过events名称直观地看到next的钩子总共有六种分别是:路又开始时候,路由完成时候,路由出错时候,路由历史更改以前,哈希路由开始时候,哈希留有完成时候这6个时间节点会触发相应的路由钩子。
中间用了一个高阶函数,可能有些小伙伴会有点看不懂,这里简单介绍一下:
高阶函数简单讲就是一个函数做为另外一个函数的参数,或者一个函数做为另外一个函数的返回值。
若是说咱们只想绑定routeChangeStart
事件,则能够这样写,当触发routeChangeStart时候,输出一个内容。
Router.events.on('routeChangeStart', function(...args){
console.log('routeChangeStart',...args)
})
复制代码
咱们这里为了方便使用了forEach循环了上述代码,传递的Router.events.on()
第二个参数要接收一个函数,咱们就能够利用高阶函数,执行高阶函数后恰好返回一个函数,就用来作它的第二个参数,相信到这里再看上面的代码就清晰不少了。
咱们绑定了全部的钩子,能够去看看效果:点击跳转到A
按钮,输出如下内容
routeChangeStart
钩子,输出routeChangeStart,以及对应
...args
参数,而后此时浏览器的路由历史会发生改变,由于跳转后前一个历史就是咱们的
localhost:3000
这个页面,在历史发生改变的前一刻触发
beforeHistoryChange
,而后路由进行跳转,结束后触发
routeChangeComplete
路由更改完成的钩子。
hash路由也是同样的:相对来讲hash路由在本页跳转不会更改历史,因此咱们将会看到这样的效果
import { withRouter } from 'next/router'
import Link from 'next/link' // 新引入的
// 外层加了个a标签和Link标签,Link标签跳转到hash路由#hello
const A = ({ router }) => <Link href="#hello"><a><div>this is a page,参数是{router.query.id}</div></a></Link>
export default withRouter(A)
复制代码
好了,到此next.js的路由咱们就有了必定的认识。感谢小伙伴坚持到最后。
next.js的路由相对后端框架或者是react-router都要简单不少,本质上就只是pages路径下对应的js文件的目录和文件名称直接做为路由,而后就是支持了query传递参数,路由跳转是在本地浏览器上操做的,因此若是不借助外力,可能会有404错误,所以要借助后端框架实现路由映射,最后咱们提到了路由钩子。但愿这篇文章对小伙伴们有用,感谢阅读。