![48f664363371ade512c264096f5f6283.png](https://i-blog.csdnimg.cn/blog_migrate/db9dd375a3098af124cc6521d4f4a245.jpeg)
Next.js 是一个轻量级的 React 服务端渲染应用框架:
https://nextjs.frontendx.cn/docsnextjs.frontendx.cn1、什么是服务器渲染
后端先调用【数据库】,获得数据之后,将数据和页面元素进行拼装,组合成完整的 html 页面,再直接返回给浏览器,以便用户浏览。
整个渲染过程,是在服务器端执行!浏览器只负责去展示!
例如:http://www.cnblogs.com/cate/design/
![d2e0802f46534a7d2566625aa82be459.png](https://i-blog.csdnimg.cn/blog_migrate/6f747f3a202f9547ec39c8eae5f659aa.jpeg)
2、怎么判断这个网站是不是服务器渲染?
打开一个网站,右键,查看网页源代码,有与网站内容相关的代码,就是服务器渲染的。
3、什么是客户端渲染?
数据由浏览器通过 ajax 请求动态取得,再通过 js 将数据填充到 dom 元素最终展示到网页中。
例如:http://h5.ele.me/msite/
![33647b832886356c4bb1bee699b17c54.png](https://i-blog.csdnimg.cn/blog_migrate/3554336dea17a835fbfeaf65d9f674a8.jpeg)
4、服务器渲染 VS 客户端渲染
![a4b490d656e50802125e2d61c857043e.png](https://i-blog.csdnimg.cn/blog_migrate/a5781c2fc1c75c26cf20bf0f2f1604c7.png)
![18eecc022c7c7953a998b7898b0d56f0.png](https://i-blog.csdnimg.cn/blog_migrate/ac7a4939ca6c0c87a30e66050442faf6.png)
5、什么是 next.js ?
Next.js 是一个轻量级的 React 服务端渲染应用框架。
Next.js 使 React 应用 更简单。
安装:
npm install --save next react react-dom
初始化一个 npm 的项目(即初始化一个 package.json 文件):
npm init -Y
![0e0261e93d0f078d15039b4b9b0d6743.png](https://i-blog.csdnimg.cn/blog_migrate/8fc15765cfd7a5a62c0eb7d384eb51ff.png)
在根目录创建一个 :
pages 文件 --> index.js 文件
![fff1b1849a2839278285f73feb8086df.png](https://i-blog.csdnimg.cn/blog_migrate/1d37b3eaeaa8bf64abe6fd342c4e35cc.png)
将下面脚本添加到 package.json 中:
{
"scripts": {
"dev": "next",
"build": "next build",
"start": "next start"
}
}
即:
{
"name": "next",
"version": "1.0.0",
"description": "",
"main": "index.js",
"dependencies": {
"next": "^9.3.6",
"react": "^16.13.1",
"react-dom": "^16.13.1"
},
"devDependencies": {},
"scripts": {
"dev": "next",
"build": "next build",
"start": "next start"
},
"author": "",
"license": "ISC"
}
index.js :(有状态组件)
import React, { Component } from 'react'
export default class Index extends Component {
render() {
return (
<div>
<h1>hello world</h1>
</div>
)
}
}
这个组件还有更简单的写法:(无状态组件)
export default () => (
<div>
<h1>hello world!!!</h1>
</div>
)
运行程序:
npm run dev
![2fc00056f2ae02fcf76428639a0a55db.png](https://i-blog.csdnimg.cn/blog_migrate/df2eeaba569dd514b3296e098a968654.png)
当我们在 index.js 文件中修改一下内容,再看 localhost:3000
热替换!!——没有刷新就显示出来了
6、什么是 react 同构!!???
客户端与服务器端使用相同的组件,使用同一份代码。
服务器端:至负责首次渲染。
客户端:负责行为和交互等等。(比如点击事件)
![8425818a1f534e2c3b77983a13ac6dde.png](https://i-blog.csdnimg.cn/blog_migrate/2ee6fa45ab35ded2027a667a2282985b.png)
7、使用 CSS / Sass / Less / Stylus files 样式
- @zeit/next-css
- @zeit/next-sass
- @zeit/next-less
- @zeit/next-stylus
![d3f87bd6a5da23efa2313758f6150d18.png](https://i-blog.csdnimg.cn/blog_migrate/d42ac4c17bb5a1fa4ebcdbc9f05c1eda.jpeg)
安装:
npm install --save @zeit/next-css
配置文件:
// next.config.js
const withCSS = require('@zeit/next-css')
module.exports = withCSS()
8、静态文件服务(如图像)
在根目录下新建文件夹叫static
。代码可以通过/static/
来引入相关的静态资源。
export default () => <img src="/static/my-image.png" alt="my image" />
9、定制 head
index.js :
import Head from 'next/head'
export default () => (
<div>
<Head>
<title>next 教程</title>
<meta charSet="utf-8" />
</Head>
<p>Hello world!</p>
</div>
)
![fdf9cf391f2be48ad6682f0ee0d215bc.png](https://i-blog.csdnimg.cn/blog_migrate/5014ae369fe20b9beda70afe96e34e2b.png)
![9749280d737c5ff73701366f146f1f72.png](https://i-blog.csdnimg.cn/blog_migrate/9aba587b315a6885b6e7acf77634295e.png)
头部/底部
![1442fa58f4c01fc042d7d7211e5755aa.png](https://i-blog.csdnimg.cn/blog_migrate/84fe220a0dbe1b06a83da33c3279f185.png)
Layout .js:
import Head from 'next/head'
export default ({children}) => (
<div>
<Head>
<title>头部测试</title>
</Head>
{children}
<footer>
版权所有.未经许可.不可转载
</footer>
</div>
)
index.js:
import Layout from './components/layout'
export default () => (
<Layout>
<div>
<h1>Hello world!</h1>
</div>
</Layout>
)
list.js:
import React, { Component } from 'react'
import '../styles/list.css'
import Layout from './components/layout'
export default class List extends Component {
state = {
list:[ "a" , "b" , "c" ]
}
render() {
return (
<Layout>
<div>
<ul>
{
this.state.list.map(item =>(
<li>{item}</li>
))
}
</ul>
</div>
</Layout>
)
}
}
![6c3e5a1d6825f7fa38eb7b0b6e8cb8d4.png](https://i-blog.csdnimg.cn/blog_migrate/f7cc01258517475ade6dde27a780b6e1.png)
10、数据获取和生命周期
如果你需要一个有状态、生命周期或有初始数据的 React 组件(而不是无状态函数)
如下所示:
import React from 'react'
export default class extends React.Component {
//状态
static async getInitialProps({ req }) {
const userAgent = req ? req.headers['user-agent'] : navigator.userAgent
return { userAgent }
}
//方法,返回
render() {
return (
<div>
Hello World {this.props.userAgent}
</div>
)
}
}
当页面渲染时加载数据,我们使用了一个异步方法 getInitialProps 。它能异步获取 JS 普通对象,并绑定在 props 上。
当服务渲染时,getInitialProps 将会把数据序列化,就像 JSON.stringify 。所以确保getInitialProps 返回的是一个普通 JS 对象,而不是 Date, Map 或 Set 类型。
当页面初始化加载时,getInitialProps 只会加载在服务端。没有跨域的限制。
只有当路由跳转(Link组件跳转或 API 方法跳转)时,客户端才会执行 getInitialProps。
注意:getInitialProps 将不能使用在子组件中。只能使用在 pages 页面组件中。
getInitialProps 的属性:
- pathname ------- URL 的 path 部分
- query ------------ URL 的 query 部分,并被解析成对象
- asPath ----------- 显示在浏览器中的实际路径(包含查询部分),为String类型
- req --------------- HTTP 请求对象 (只有服务器端有)
- res ---------------- HTTP 返回对象 (只有服务器端有)
- jsonPageRes ----- 获取数据响应对象 (只有客户端有)
- err ---------------- 渲染过程中的任何错误
![261daf6d4669d9c568126636418b6a3c.png](https://i-blog.csdnimg.cn/blog_migrate/58d51565fedb978c2e1adc6ab29548a3.jpeg)
![8923080eeba596dd836bfa395f86b509.png](https://i-blog.csdnimg.cn/blog_migrate/3e6f2a9555ab8efcae55c1d18ee073eb.png)
11、路由
路由的基本功能:next 默认按照文件结构(路径),进行页面跳转!
路由又分为:
基本路由:按照文件结构,进行页面跳转。(基本路由跳转又分为:link 跳转和编程式跳转。)
动态路由:
路由页面跳转的 3 种方式:
link 跳转
编程式跳转
参数传递——路由跳转一般会传递一些参数
<Link>
组件实现客户端的路由切换:
引入
import Link from 'next/link'
layout.js:
import Head from 'next/head'
import Link from 'next/link'
export default ({children}) => (
<div>
<Head>
<title>头部测试</title>
</Head>
<div>
<Link href='/'>主页</Link> |
<Link href='/list'>列表</Link> |
<Link href='/nestStyle'>内联样式</Link>
</div>
{children}
<footer>
版权所有.未经许可.不可转载
</footer>
</div>
)
index.js:
import Layout from './components/layout'
export default () => (
<Layout>
<div>
<h1>Hello world!</h1>
</div>
</Layout>
)
list.js:
import React, { Component } from 'react'
import Layout from './components/layout'
import '../styles/list.css'
import Router from 'next/router'
export default class List extends Component {
state = {
list:[ "a" , "b" , "c" ]
}
render() {
return (
<Layout>
<div>
<ul>
{
this.state.list.map((item, index) =>(
<li key={index} onClick={() => Router.push('/detail?arg=' + item)}>{item}</li>
))
}
</ul>
</div>
</Layout>
)
}
}
注意:<Link>
支持任何有onClick
事件的组件。
如果你不包含<a>
标签,它仅给组件添加onClick
事件,而不会添加href
属性!!
所以想要 list 页面也能跳转,需要:
nextStyle.js:
import React, { Component } from 'react'
import Layout from './components/layout'
export default class NextStyle extends Component {
render() {
return (
<Layout>
<div>
<style jsx>{`
h1 {
background-color: blue;
}
`}</style>
<h1>这里是内联样式!!(但不推荐这种写法)</h1>
</div>
</Layout>
)
}
}
这样就实现了路由传参
Router.push('/detail?arg='+ item)}
但 push 支持对象形式的
替换路由:
<Link>
组件默认将新 url 推入路由栈中。可以使用replace
属性来防止添加新输入。
// pages/index.js
import Link from 'next/link'
export default () =>
<div>
Click{' '}
<Link href="/about" replace>
<a>here</a>
</Link>{' '}
to read more
</div>
暴露 href
给子元素:
import Link from 'next/link'
import Unexpected_A from 'third-library'
export default ({ href, name }) =>
<Link href={href} passHref>
<Unexpected_A>
{name}
</Unexpected_A>
</Link>
禁止滚动到页面顶部:
<Link>
的默认行为就是滚到页面顶部。
当有 hash 定义时(#),页面将会滚动到对应的 id 上,就像<a>
标签一样。
为了预防滚动到顶部,可以给<Link>
加scroll={false}
属性。
<Link scroll={false} href="/?counter=10"><a>Disables scrolling</a></Link>
<Link href="/?counter=10"><a>Changes with scrolling to top</a></Link>
命令式:
也可以用next/router
实现客户端路由切换。
import Router from 'next/router'
export default () =>
<div>
Click <span onClick={() => Router.push('/about')}>here</span> to read more
</div>
拦截器 popstate
:
有些情况(比如使用custom router),你可能想监听popstate
,在路由跳转前做一些动作。 比如,你可以操作 request 或强制 SSR 刷新。
import Router from 'next/router'
Router.beforePopState(({ url, as, options }) => {
// I only want to allow these two routes!
if (as !== "/" || as !== "/other") {
// Have SSR render bad routes as a 404.
window.location.href = as
return false
}
return true
});
如果你在beforePopState
中返回 false,Router
将不会执行popstate
事件。
Router
对象的 API 如下:
route
- 当前路由的String
类型pathname
- 不包含查询内容的当前路径,为String
类型query
- 查询内容,被解析成Object
类型. 默认为{}
asPath
- 展现在浏览器上的实际路径,包含查询内容,为String
类型push(url, as=url)
- 页面渲染第一个参数 url 的页面,浏览器栏显示的是第二个参数 urlreplace(url, as=url)
- performs areplaceState
call with the given urlbeforePopState(cb=function)
- 在路由器处理事件之前拦截.
push
和 replace
函数的第二个参数as
,是为了装饰 URL 作用。如果你在服务器端设置了自定义路由将会起作用。
URL 对象用法:
push
或 replace
可接收的 URL 对象(<Link>
组件的 URL 对象一样)来生成 URL。
import Router from 'next/router'
const handler = () =>
Router.push({
pathname: '/about',
query: { name: 'Zeit' }
})
export default () =>
<div>
Click <span onClick={handler}>here</span> to read more
</div>
路由事件:
可以监听路由相关事件。 下面是事件支持列表:
routeChangeStart(url)
- 路由开始切换时触发routeChangeComplete(url)
- 完成路由切换时触发routeChangeError(err, url)
- 路由切换报错时触发beforeHistoryChange(url)
- 浏览器 history 模式开始切换时触发hashChangeStart(url)
- 开始切换 hash 值但是没有切换页面路由时触发hashChangeComplete(url)
- 完成切换 hash 值但是没有切换页面路由时触发
这里的url
是指显示在浏览器中的 url。如果你用了Router.push(url, as)
(或类似的方法),那浏览器中的 url 将会显示 as 的值。
正确使用路由事件routeChangeStart
的例子:
const handleRouteChange = url => {
console.log('App is changing to: ', url)
}
Router.events.on('routeChangeStart', handleRouteChange)
如果你不想长期监听该事件,你可以用off
事件去取消监听:
Router.events.off('routeChangeStart', handleRouteChange)
如果路由加载被取消(比如快速连续双击链接):
Router.events.on('routeChangeError', (err, url) => {
if (err.cancelled) {
console.log(`Route to ${url} was cancelled!`)
}
})