内容简介:在介绍路由之前,想先简单说一下目录结构,其中有些东西对于路由讲解还是很有帮助的这是
在介绍路由之前,想先简单说一下目录结构,其中有些东西对于路由讲解还是很有帮助的
├── .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
就不说了。
pages
和 components
pages
是next.js中非常重要的一个目录,其中每一个js文件就代表一个页面,但是有两个例外,一个是上一篇文章中用到的 _app.js
,一个是 _document.js
。我们在pages下再创建一个a.js和test/b.js,然后看看效果
// a.js export default () => <div>this is a page</div> // test/b.js export default () => <div>this is b page</div> 复制代码
我们可以发现next.js会将pages下的js文件根据其路径名和文件名自动生成对应的路由。
但是我们再写页面的时候不可能将所有东西都放在pages下,我们可以将具体内容作为组件放在 components
目录中,然后在 pages
中相应的js文件中引入。如果是用脚手架 工具 生成项目的小伙伴可以很直观的看到 components
目录中有 head.js
和 nav.js
两个组件,在 pages/indx.js
中将其引入并使用
.next:在我们运行过next.js项目之后,会自动生成 .next
目录,这里存放的内容是next.js将我们写的pages和components等源码打包编译后的结果,我们通过浏览器访问可以拿到的内容其实就来自这里,在后续进行上限打包时候也会生成这个目录.
Tips:请不要修改这个目录中的内容
其他 staic
目录存放静态文件,比如图片,css,favicon.ico等 .babelrc
文件存放babel配置 next.config.js
存放next的配置 server.js
是我们上一篇写Koa服务器程序的代码
next.js路由
Link
组件
我们可以删除index.js中的所有内容,重写为:
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页面也不会有刷新的情况。
还有一点需要说明的是,Link通过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中的Router对象
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路由的方式其实都是在本地浏览器环境创建的一个路由,而服务端并不知情,如果这个路由服务端并不存在,那么就会拿到404 not found。
此处不知道有没有小伙伴会问:既然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
就可以正常访问了
next.js中的路由钩子
这里说的钩子,相信大多数了解过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路由在本页跳转不会更改历史,所以我们将会看到这样的效果
这个实验怎么做呢?我们可以去更改/pages/a.js,引入Link组件,给原来返回的内容包裹上Link和a标签
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错误,因此要借助后端框架实现路由映射,最后我们提到了路由钩子。希望这篇文章对小伙伴们有用,感谢阅读。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- vue路由篇(动态路由、路由嵌套)
- 小程序封装路由文件和路由方法,5种路由方法全解析
- Vue的路由及路由钩子函数
- gin 源码阅读(二)-- 路由和路由组
- vue router 路由鉴权(非动态路由)
- Flutter进阶:路由、路由栈详解及案例分析
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。