react服务端渲染Next的学习

记录一下Next的学习,
1:手动安装
2:自动安装
好,开始手动安装
//先安装依赖包

npm  init
cnpm  install  react  react-dom  next --save 

//安装完成后,修改package.json,添加如下命令

  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev":"next",
    "build":"next build",
    "start":"next start"
  },

//测试,新建一个pages文件夹,next会自动给我们设置路由,
//新建一个Index.js,代码如下

//react hooks
function Index(){
    return (
        <div>Hello,next</div>
    )       
}


export default Index;

//启动,测试

npm  run  dev
image.png
image.png

启动成功啦

·······························································································
现在我们用工具脚手架来创建一个项目,工作中都是用这个方式哦
先来全局安装一下

cnpm  install  create-next-app -g
image.png

创建项目
目前可以支持三种方式的创建,分别是用npx,yarn和create-next-app命令来进行安装
npx 是Node自带的npm模块,所以你只要安装了Node都是可以直接使用npx命令的。

但低版本的Node是不带这个命令的,所以你需要手都安装一下。

cnpm install -g npx

好,开始创建项目

npx create-next-app next-react
image.png

启动

npm  run  dev
image.png

看到上图就是成功啦
在pages新建一个index.js页面来测试

function user(){
    return(
        <button>测试按钮</button>
    )
}

export default user;

image.png

浏览器输入http://localhost:3000/user,发现自动给我们添加了路由,好方便亚麻跌
image.png

类似于一个js就是一个路由,那么如果是2级比如http://localhost:3000/concat/phone,怎么实现了?
其实也很简单,新建一个concat文件夹,里面新建phone.js就可以啦
//phone.js

export default ()=>{
    return <div>好好学习React</div>
}
image.png
image.png

那么如何新建一个组件了?

我们再components文件下下面,新建一个test.js,用来写我们的子组件

export  default ({children})=>{
    return <button>我是一个子组件{children}</button>
}

ps:这里传值只能写children,用别的就不行哦

在首页中使用
index.js

import Test from '../components/test'
<Test >我是传递值</Test>

最后效果


image.png

接下来我们实现路由的跳转,具体有2个方式Link和Router.push,好上代码
新建一个home.js
//home.js


import Link from 'next/link';

import PageA from './pageA.js';
import PageB from './pageB.js';

function  home(){
    return(
        <>
            <div>我是Home页面</div>
            <Link  href='/pagea'><a >去A页面</a></Link>
            <Link  href='/pageb'><a>去B页面</a></Link>
        </>
    )
    
}

export default home;

ps:官方推荐link里面用a标签包裹,否则会有警告

//pageA.js

import Link from 'next/link';
export  default ()=>{
    return (
        <>
            <Link href='/home'><a >返回首页</a></Link>
            <div>我是AAA页面</div>
        </>
        )
}

//pageB.js

import Link from 'next/link';
export  default ()=>{
    
    return (
        <>
            <Link href='/home'><a >返回首页</a></Link>
            <div>我是BBB页面</div>
        </> 
    )
}

效果图


image.png
image.png
image.png

好,我们用单击事件来是线路有跳转

import Router  from 'next/router'
function  home(){
    return(
        <>
            <div>我是Home页面</div>
            <Link  href='/pagea'><a >去A页面</a></Link>
            <Link  href='/pageb'><a>去B页面</a></Link>
            <div>
                <button onClick={()=>{Router.push('/pagea')}}>点击去A页面</button>
                <button onClick={()=>{Router.push('/pageb')}}>点击去B页面</button>
            </div>
        </>
    )
    
}
image.png

image.png
image.png

用方法也是可以的哦

<div>
                <button onClick={()=>{Router.push('/pagea')}}>点击去A页面</button>
                <button onClick={()=>{Router.push('/pageb')}}>点击去B页面</button>
                <button onClick={gotoPage}>点击去C页面</button>
            </div>

接下来讲解一下,路由传递参数和接受参数,next只支持query传递参数哦

<Link  href='/pagea?name=深圳'><a >去A页面</a></Link>
<Link  href='/pageb?name=成都'><a>去B页面</a></Link>

这里传递2个参数name值,然后我们再pageA和pageB获取name值

import Link from 'next/link';
import {withRouter} from 'next/router';

 const pageA = ({router})=>{
    return (
        <>
            <Link href='/home'><a >返回首页</a></Link>
            <p>我是参数:{router.query.name}</p>
            <div>我是AAA页面</div>
        </>
        )
}

export  default withRouter(pageA);

主要是使用了withRouter来接受路由参数哦

import Link from 'next/link';
import {withRouter} from 'next/router';

const pageB= ({router})=>{
    
    return (
        <>
            <Link href='/home'><a >返回首页</a></Link>
            <div>我是接受到的参数:{router.query.name}</div>
            <div>我是BBB页面</div>
        </> 
    )
}
export  default withRouter(pageB)
image.png

image.png

函数传递也是可以的

//home.js
    function  gotoPage(){
        Router.push('/pageC?name=武汉')
    };
//pageC.js
import Link from 'next/link';
import {withRouter} from 'next/router';
const pageC= ({router})=>{
    return (
        <>
            <Link href='/home'><a >返回首页</a></Link>
            <div>接受到的参数{router.query.name}</div>
            <div>我是CCC页面</div>
        </> 
    )
}
export  default withRouter(pageC)
image.png

传递参数用面向对象试一下可以不?

    function  gotoPage(){
        Router.push({
            pathname:'/pageC',
            query:{
                name:"我爱武汉"
            }
        })
    };

效果是一样一样的


image.png

接下来讲解下路由的生命周期钩子事件

//routeChangeStart 路由发送变化之前 
//routeChangeComplete   路由变化结束
//beforeHistoryChange    路由history开始变化
//routerChangeError   路由发送错误 
//hashChangeStart     hash路由开始改变之前
//  hashChangeComplete  hash路由开始改变完成     

具体代码
home.js

    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('routerChangeError',(...args)=>{
        console.log('4-routerChangeError,路由发送错误',...args)
    });
    //hash模式
    Router.events.on('hashChangeStart',(...args)=>{
        console.log('5-hashChangeStart,hash路由开始改变之前',...args)
    });
    Router.events.on('hashChangeComplete',(...args)=>{
        console.log('6-hashChangeComplete,hash路由开始改变完成',...args)
    });

效果图


image.png

hash模式下

<Link  href='#pageb?name=成都'><a>Hash模式</a></Link>
image.png

接下来讲解远程获取数据,官方规定必须放在getInitialProps里面才可以

>cnpm  install axios --save
pageA.getInitialProps= async ()=>{
    //let api = "http://localhost:3333/api/list";
    let api = "/api/list";
    const  result  =  new  Promise((resolve)=>{
            axios(api).then(res=>{
                console.log(res.data.data);
                resolve(res.data.data)
            }).catch(err=>{
                console.log(err);
            });
    });
    return await result;
}

image.png

接下来讲解一下,如何在next中写css

        <>
            <Link href='/home'><a >返回首页</a></Link>
            <div>接受到的参数{router.query.name}</div>
            <div >我是CCC页面</div>
            <div className="red">我是CCC页面</div>
            <style jsx>
            {`
                div{color:blue}
                .red{color:red}
            `}  
            </style>
        </> 

image.png

那么如何动态切换样式了,点一下变红点一下变蓝色

import  {useState} from 'react';
import Link from 'next/link';
import {withRouter} from 'next/router';
const pageC= ({router})=>{
    const [color,setColor]=useState('blue');
    const changeColor = ()=>{
        setColor(color=="blue"?"red":"blue")
    }
    return (
        <>
            <Link href='/home'><a >返回首页</a></Link>
            <div>接受到的参数{router.query.name}</div>
            <div >我是CCC页面</div>
            <div className="red">我是CCC页面</div>
            <button onClick={changeColor}>改变颜色</button>
            <style jsx>
            {`
                div{color:${color}}
                .red{color:red}
            `}  
            </style>
        </> 
    )
}
export  default withRouter(pageC)

大功告成,接下来实现模块懒加载,优化打开速度

>cnpm  install moment --save
import  {useState} from 'react'
import {moment} from 'moment';

const  time= ()=>{
    const [nowTime,setTime]=useState(Date.now());
    const changeTime= ()=>{
        setTime(moment(Date.now()).format())
    }
    return(
        <>
        <div>{nowTime}</div>
        <button onClick={changeTime}>改变时间格式</button>
        </>
    )
}
export default  time;

使用懒加载之后

import  {useState} from 'react'

const  time= ()=>{
    const [nowTime,setTime]=useState(Date.now());
    const changeTime = async ()=>{
        let  moment =await import('moment');
        setTime( moment.default(Date.now()).format())
    }
    return(
        <>
        <div>{nowTime}</div>
        <button onClick={changeTime}>改变时间格式</button>
        </>
    )
}
export default  time;

ps:注意一定要加.default,否则报错,如何判断依据懒加载成功了?


image.png

会发现从0.js又生成1.js,说明成功了~~~~~~

那么如何懒加载自定义组件了????

1:先再components里面新建one.js

export default ()=>{
    return <h3>我是自定义组件哦</h3>
}

2:使用

import dynamic from 'next/dynamic'
import One  from '../components/one';
<One />

写完代码后,可以看到自定义组件是懒加载的,只有在jsx里用到<One/>时,才会被加载进来,如果不使用就不会被加载。

当我们作的应用存在首页打开过慢和某个页面加载过慢时,就可以采用Lazy Loading的形式,用懒加载解决这些问题。

接下来讲解如何利用head实现网页的标题title

import Head  from 'next/head'
<Head>
    <title>我要成为React大神</title>
    <meta charset='utf-8' /> 
</Head>
image.png

那么如何全局引用了?
思路:万物皆组件,封装为一个组件,其他页面引用就可以啦
新建一个globalHeader.js作为全局组件
//globalHeader.js

import Head  from  'next/head'

export default ()=>{
    return(
        <>
            <Head>
                <title>我是全局标题</title>
            </Head>
        </>
    )
    
}

其他页面使用

import  GlobalHeader  from '../components/globalHeader';
<GlobalHeader />

效果图


image.png
我们知道next不能直接导入css使用,这就很尴尬了,那么如何解决了?
image.png

这里用了一个三方库@zeit/next-css来解决

cnpm install --save @zeit/next-css

根目录下新建一个next.config.js

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

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

module.exports = withCss({})

修改配置文件后,一定要重启服务


image.png

大功告成,yeah~~~~~
接下来我们安装antd,这样界面就可以好看很多
1:安装antd

cnpm install antd --save

//注意:antd 默认支持基于 ES module 的 tree shaking,不使用以下插件也会有按需加载的效果。

按需加载

A使用 babel-plugin-import(推荐)。
B手动引入
这里我们用按需加载
2:配置按需加载
先安装按需加载的模块

>cnpm  install babel-plugin-import --save

根目录下面新建.babelrc 文件

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

ps:presets是保留以前的配置,注意官网的libraryDirectory这个千万不要加,有个坑,加了编译报错无法访问
使用

import {Button} from 'antd';
<Button>我是antd按钮</Button>
image.png

最后打包上线

npm  run  build

但是当你使用了Ant Desgin后,在打包的时候会遇到一些坑。我们引入了就会有如下问题
报错如下


image.png

那么如何解决了?

1:修改.babelrc文件,去掉"style": "css"这一行

{
  "presets":["next/babel"], 
  "plugins": [
    ["import", {
      "libraryName": "antd"
    }]
  ]
}

2:在pages文件下下面新建一个_app.js,内容如下

import App  from  'next/app';

import 'antd/dist/antd.css';

export default App;

3:重新打包测试

npm run  build  

看到如下界面就成功啦


image.png

4:启动测试

  "scripts": {
    "dev": "node server.js",
    "build": "next build",
    "start": "next start -p 80"
  },

启动

npm  run  start
image.png

没有问题,大功告成~~~~~~~~~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,544评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,430评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,764评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,193评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,216评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,182评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,063评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,917评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,329评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,543评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,722评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,425评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,019评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,671评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,825评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,729评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,614评论 2 353

推荐阅读更多精彩内容