学习记录
React的服务端渲染技术---Next.js
项目的创建与目录结构的详解项目的创建有两种创建项目的方式手动创建点击查看 create-next-app脚手架创建(主要介绍)首先需要全局安装 create-next-appnpm install -g create-next-app然后在一个空目录中创建 next 项目这里使用 npx 来创建项目,低版本的 node 没有 npx,所以需要额外的安装,一般情况下,略微高点版本的 node 都自带有 npx 命令 npm install -g npxnp.
2021-01-10 02:15:27
213
                <h1>项目的创建与目录结构的详解</h1> 

项目的创建

有两种创建项目的方式

  • 手动创建点击查看
  • create-next-app脚手架创建(主要介绍)

首先需要全局安装 create-next-app

npm install -g create-next-app

 然后在一个空目录中创建 next 项目

这里使用 npx 来创建项目,低版本的 node 没有 npx,所以需要额外的安装,一般情况下,略微高点版本的 node 都自带有 npx 命令

 npm install -g npx
npx create-next-app next-create

目录结构详解

  • components文件夹:这里是专门放置自己写的组件的,这里的组件不包括页面,指公用的或者有专门用途的组件。

  • node_modules文件夹:Next项目的所有依赖包都在这里,一般我们不会修改和编辑这里的内容。

  • pages文件夹:这里是放置页面的,这里边的内容会自动生成路由,并在服务器端渲染,渲染好后进行数据同步。

  • static文件夹: 这个是静态文件夹,比如项目需要的图片、图标和静态资源都可以放到这里。

  • .gitignore文件: 这个主要是控制git提交和上传文件的,简称就是忽略提交。

  • package.json文件:定义了项目所需要的文件和项目的配置信息(名称、版本和许可证),最主要的是使用npm install 就可以下载项目所需要的所有包。

 Page和Component的使用

新建一个页面且可以访问

page 目录下新建一个 home.js,具体代码如下

export default function Home(){
    return (
        <button>home页</button>
    )
}

next 自动为我们配置好了路由,所以访问这个 home 页就可以直接通过 http://localhost:3000/home 访问

如果需要访问多级地址,就可以写嵌套的目录结构,比如 http://localhost:3000/article/detail 

> pages
 |
> ------- home目录
 |
> ------- article目录
 |
> ---- detail.js

使用Components来定义组件

比如我们要制作一个全局底部组件,就可以直接在 components 里面建一个 footer.js 作为 footer 组件

export default ({children}) => {
    return (
        <footer>
            {children}
        </footer>
    )
}

注意:组件函数接收一个参数是个 对象,这个对象里面有一个 children 属性,这个 children 属性值就是使用的时候写进去的值 

然后在页面引入这个底部组件

import Footer from '../components/footer';

使用方法就很简单了

<Footer>底部内容</Footer>

 路由的跳转

标签式跳转<Link>

首先在 pages 目录下先建两个需要跳转的页面,这里以 homelist 为例

// home -- index.js

export default function Home() {
return (
<div>home页</div>
)
}

//list -- index.js
export default function List () {
return (
<div>list页</div>
)
}

 这里我们可以通过 http://localhost:3000/homehttp://localhost:3000/list 来访问了

然后在 page/index.js 里面写两个按钮需要点击跳转 

import Link from 'next/link';
import { Fragment } from 'react';

export default function Index() {
return (
<Fragment>
<Link href="/home"><a>去home</a></Link>
<Link href="/list"><a>去列表</a></Link>
</Fragment>
)
}

 现在就可以实现分别点击按钮来跳转了

用 <Link> 标签进行跳转是非常容易的,但是有一个小坑需要注意一下,就是他不支持兄弟标签并列的情况。所以需要一个标签来包裹。

<!--错误写法-->

<Link>
    <span>去</span>
    <span>home</span>
</Link>

<!--正确写法-->

<Link>
<a>
<span>去</span>
    <span>home</span>
</a>
</Link>

函数式跳转(Router模块跳转)

需要先引入 Router

import Router from 'next/router'

 我们可以通过一个按钮执行一个方法,在这个方法中通过 Router 来跳转

// Router函数式跳转的写法

import Link from 'next/link';
import Router from 'next/router';
import { Fragment } from 'react';

export default function Home() {
const goHome = () => {
Router.push('/home')
}
const goList = () => {
Router.push('/list')
}
return (
<Fragment>
{/*
<Link href="/home"><a>去home</a></Link>
<Link href="/list"><a>去列表</a></Link>
*/}
<button onClick={goHome}>去home</button>
<button onClick={goList}>去列表</button>
</Fragment>
)
}

 路由跳转时的参数与接收(query)

注意:在 Next.js 中只能用 query 来传参

现在想要实现一个在首页点击去 list 传递一个参数 id1

 标签式传递参数<Link>

具体代码如下,直接在 Link 标签的 href 属性上拼接即可

<Fragment>
    <Link href="/home"><a>去home</a></Link>
    <Link href="/list?id=1"><a>去列表</a></Link>
</Fragment>

 函数式传参(Router

import Router from 'next/router';
import { Fragment } from 'react';

export default function Home() {
const goHome = () => {
Router.push('/home')
}
//方法1:拼接在push里面
const goList = () => {
Router.push('/list?id=1')
}
//方法2:以对象的形式
const goList = () => {
Router.push({
pathname:'/list',
query:{
id:1
}
})
}
return (
<Fragment>
<button onClick={goHome}>去home</button>
<button onClick={goList}>去列表</button>
</Fragment>
)
}

 接收传递过来的参数

接受传递过来的参数需要用到 withRouter,所以这里先引入

import { withRouter} from 'next/router'

 然后需要改写一下 List 页面

import { withRouter } from 'next/router';
import { Fragment } from 'react';

const List = ( {router} ) => {
return (
<Fragment>
<div>{router.query.id}</div>
<div>列表页</div>
</Fragment>
)
}

export default withRouter(List);

必须要将页面函数作为参数传给 withRouter 并且将 withRouter 暴露出去才可以使用传递给当前页面的参数  

 钩子事件

  • routerChangeStart 路由发生变化时
  • routerChangeComplete 路由结束变化时
  • beforeHistoryChange 浏览器history触发前
  • routerChangeError 路由跳转发生错误时
  • hashChangeStart Hash模式转变之前
  • hashChangeComplete Hash模式转变结束
import Link from 'next/link';
import Router from 'next/router';
import { Fragment } from 'react';

const Home = () => {
const goHome = () => {
Router.push('/home')
}
const goList = () => {
Router.push('/list?id=1')
}

Router.events.on('routeChangeStart',(...args)=>{
console.log('1.routeChangeStart->路由开始变化,参数为:',...args)
})

Router.events.on('routeChangeComplete',(...args)=>{
console.log('2.routeChangeComplete->路由结束变化,参数为:',...args)
})

Router.events.on('beforeHistoryChange',(...args)=>{
console.log('3,beforeHistoryChange->在改变浏览器 history之前触发,参数为:',...args)
})

Router.events.on('routeChangeError',(...args)=>{
console.log('4,routeChangeError->跳转发生错误,参数为:',...args)
})

Router.events.on('hashChangeStart',(...args)=>{
console.log('5,hashChangeStart->hash跳转开始时执行,参数为:',...args)
})

Router.events.on('hashChangeComplete',(...args)=>{
console.log('6,hashChangeComplete->hash跳转完成时,参数为:',...args)
})

return (
<Fragment>
<button onClick={goHome}>去home</button>
<button onClick={goList}>去列表</button>
</Fragment>
)
}

export default Home;

在Next中使用Axios请求数据

Next.js框架中提供了getInitialProps静态方法用来获取远端数据

 首先需要安装 axios

import { withRouter } from 'next/router';
import { Fragment } from 'react';
import axios from 'axios';

// 下面请求返回的数据是什么,这里就用什么接受
// 下面请求返回的是data 所以这里用data来接收
const List = ( {router,data} ) => {
return (
<Fragment>
<div>{router.query.id}</div>
<div>列表页</div>
<ul>
{
data.map((item,index) => {
return (
<li key={index}>{item.content}</li>
)
})
}
</ul>
</Fragment>
)
}

//在这里请求
List.getInitialProps = async () => {
return new Promise((resolve,reject) => {
axios.get('xxx').then(res => {
//注意返回值
resolve(res.data)
}).catch(err => reject(err))
})
}
/*
返回的res.data为
data:[
{id:1,content:'111'},
{id:2,content:'222'},
{id:3,content:'333'},
{id:4,content:'444'}
]
*/

export default withRouter(List);

 注意:需要注意的一点是返回值是什么,页面函数的参数里面就用什么来接收

使用style JSX编写页面的css样式

Next.js中引入一个CSS样式是不可以用的,如果想用,需要作额外的配置。因为框架为我们提供了一个style jsx特性,也就是把CSS用JSX的语法写出来

这里我们先在 pages 目录下新建一个 test.js 文件

function Test(){
    return (
        <>
            {/* 必须加上下面这段style才会修改样式,{` `} 同样不可缺 */}
            <style jsx>
                {`
                    div{color:blue}
                    .test{color:red}
                `}
            </style>
            <div>这是一句测试文字</div>
            <div className="test">这是一句测试文字</div>
        </>
    )
}
export default Test;

加入了Style jsx代码后,Next.js会自动加入一个随机类名,这样就防止了CSS的全局污染

动态显示样式

比如我们要点击一个按钮再让他添加这个样式

import {useState} from 'react';

function Test(){
// 定义一个color初始值为blue
const [color,setColor] = useState('blue')
// 定义一个方法来改变颜色
const changeColor = ( ) => {
setColor(color == 'blue' ? 'red' : 'blue')
}
return (
<>
<div>这是一句测试文字</div>
<div onClick={changeColor}>这是一测试文字</div>
{/* 这里要想使用上面定义的color必须使用 ${color} */}
<style jsx>
{ div { color:${color} } }
</style>
</>
)
}
export default Test;

自定义Head,更加友好的SEO

在各个页面加入Head

首先在 pages 目录下新建一个 head.js 

// 引入Head组件
import Head from 'next/head';

const Header = () => {
return (
<div>
<Head>
<title>自定义标题内容</title>
<meta charSet='utf-8' />
</Head>
</div>
)
}
export default Header;

定义全局的Head

定义全局的 Head 意义不大,但还是可以学习一下

components 目录下定义全局 MyHeader.js

Next 中已经为我们封装好 Head ,全局定义 Head 的话相当于对 Next 封装的 Head 再次封装,意义不大

import Head from 'next/head'

const MyHeader = ()=>{
return (
<>
<Head>
<title>全局title</title>
</Head>
</>
)
}

export default MyHeader

然后修改 pages 目录下的 head.js

// import Head from 'next/head';
import MyHeader from '../components/MyHeader'

const Header = () => {
return (
<div>
<MyHeader />
{/* <Head>
<title>自定义标题内容</title>
<meta charSet='utf-8' />
</Head> */}
</div>
)
}
export default Header;

Next框架下使用Ant Design UI

Next 默认是不支持 Css 文件的,所以我们需要先安装一个插件来让 Next 支持 Css

安装插件@zeit/next-css进行配置,让他支持css

首先在根目录下新建一个 static/css 目录,在这个目录下新建一个 test.css 文件

body{
    color:green;
}

然后在 pages 目录下的 list 页面中引入

import '../../static/css/test.css';

发现并不生效,还报了一堆的错,这个时候我们需要安装插件让他支持 CSS 文件

npm install @zeit/next-css -s

安装完成之后,在项目根目录下新建 next.config.js 文件

下面这段代码是配置文件,直接复制粘贴即可

const withCss = require('@zeit/next-css')

if(typeof require !== 'undefined'){
require.extensions['.css']=file=>{}
}

module.exports = withCss({})

然后重启服务,访问 http://localhost:3000/list 发现字体颜色都变了

按需加载 Ant Design

需要安装 Ant Design 库 和 babel-plugin-import 插件

npm install antd babel-plugin-import -s

安装完成之后,在项目根目录下新建 .babelrc 文件,编写如下配置信息

{
    "presets":["next/babel"],  //Next.js的总配置文件,相当于继承了它本身的所有配置
    "plugins":[     //增加新的插件,这个插件就是让antd可以按需引入,包括CSS
        [
            "import",
            {
                "libraryName":"antd",
                "style":"css"
            }
        ]
    ]
}

接下来我们去 list 页面使用 ant 组件

import { Fragment } from "react";
import Router from "next/router";
import { Button } from 'antd'; //引入ant的button组件
import '../../static/css/test.css';

const List = () => {
const goHome = () => {
Router.push({
pathname:'/'
})
}

return (
    &lt;Fragment&gt;
        {/* 使用ant的button组件 */}
        &lt;Button type="primary"&gt;Primary Button&lt;/Button&gt;
        {/* &lt;p&gt;&lt;button onClick={goHome}&gt;返回首页&lt;/button&gt;&lt;/p&gt; */}
        &lt;p&gt;列表页&lt;/p&gt;
    &lt;/Fragment&gt;
)

}

export default List;

打开浏览器,发现效果已经出来。

Next生产环境打包

打包:next build

运行:next start -p 80

把这两个命令修改到 package.json

"scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start -p 80"
 }

然后在终端依次执行 npm run build npm run start 

当我们运行 npm run build 之后报错,是因为我们在加入 Ant Design 样式时产生的,可以修改为全局引入

page 目录下,新建一个 _app.js 文件,然后写入下面的代码。

import App from 'next/app'

import 'antd/dist/antd.css'

export default App

然后我们在终端依次执行 npm run build npm run start 

发现可以部署成功了。

注意:

当打包的时候报如下错误

需要先删除 node_modules 目录

然后使用 npm cache clean --force 清除 npm 缓存

最后再次执行 npm install

然后再次在终端依次执行 npm run build npm run start 


根据 技术胖个人博客 学习所做的笔记

个人博客:点此进入(http://xueshuai.top)

前端交流群:1063233592