内容简介:在介绍路由之前,想先简单说一下目录结构,其中有些东西对于路由讲解还是很有帮助的这是
在介绍路由之前,想先简单说一下目录结构,其中有些东西对于路由讲解还是很有帮助的
├── .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页面也不会有刷新的情况。
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错误,因此要借助后端框架实现路由映射,最后我们提到了路由钩子。希望这篇文章对小伙伴们有用,感谢阅读。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Pro Django
Marty Alchin / Apress / 2008-11-24 / USD 49.99
Django is the leading Python web application development framework. Learn how to leverage the Django web framework to its full potential in this advanced tutorial and reference. Endorsed by Django, Pr......一起来看看 《Pro Django》 这本书的介绍吧!