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