SSR服务器端渲染(Next.js总结和豆瓣电影项目)「建议收藏」

SSR服务器端渲染(Next.js总结和豆瓣电影项目)「建议收藏」一.前言先解释一下Nuxt.js和Next.js虽然只有一个字母之差,但它们是不同的两个服务端渲染框架.什么是Next.js?引用Next中文官网的一句话:Next.js是一个轻量级的React服务端渲染应用框架。Next.js带来了很多好的特性:默认服务端渲染模式,以文件系统为基础的客户端路由(注意:没有专门路由)代码自动分割使页面加载更快以webpack的热替换(HMR…

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全家桶1年46,售后保障稳定

一.前言

先解释一下Nuxt.js和Next.js虽然只有一个字母之差,但它们是不同的两个服务端渲染框架.

什么是Next.js?

引用Next中文官网的一句话:
Next.js 是一个轻量级的 React 服务端渲染应用框架。

Next.js带来了很多好的特性:

  • 默认服务端渲染模式,以文件系统为基础的客户端路由(注意:没有专门路由)
  • 代码自动分割使页面加载更快
  • 以webpack的热替换(HMR)为基础的开发环境
  • 使用React的JSX和ES6的module,模块化和维护更方便
  • 可以运行在Express和其他Node.js的HTTP 服务器上
  • 可以定制化专属的babel和webpack配置

使用Next服务器端渲染好处:

  • 对SEO友好
  • 提升在手机及低功耗设备上的性能
  • 快速显示首页

二.Next.js学习

按照国际惯例,先来一个hello world应用

先执行创建命令:

mkdir 项目名
cd 项目名
npm init -y
npm i react react-dom next --save
mkdir pages//一定要叫这个名,不能更改

Jetbrains全家桶1年46,售后保障稳定

配置package.json中的scripts属性

 "scripts": { 
   
    "dev":"next",
    "build":"next build",
    "start":"next start"
  },

在pages文件夹里创建一个index.js页面,简单写点内容:

export default () => { 
   
  return (
    <div>
      <p>hello world</p>
    </div>
  )
}

执行npm run dev,出来效果:

在这里插入图片描述

页面导航

路由跳转(组件跳转和事件跳转)
Link组件跳转:

1,引入Link组件

import Link from 'next/link';

2.使用
注意点:
路径是用href;
文字里面要用标签包裹(标签可以是a标签或者其他标签都可以,但Link标签里只能写一个其他标签);
给Link标签设置style样式是无效的,因为Link是一个高阶组件(HOC),但我们可以给子元素设置样式.
在这里插入图片描述
href属性也可以改为对象写法:

<Link href={ 
   { 
   pathname:"/next-route/teacher"}} >
        <button style={ 
   { 
   color:'red'}}>去教师页面</button>
      </Link>

同时对象写法可以传递query参数过去
代码:

<Link href={ 
   { 
   pathname:"/next-route/teacher",query:{ 
   id:1}}} >
        <button style={ 
   { 
   color:'red'}}>去教师页面</button>
</Link>

显示效果:
在这里插入图片描述

事件路由跳转

第一步:引入Router对象

import Router from "next/router";

第二步:添加跳转事件

在这里插入图片描述
留意一下:浏览器输入网址的请求跳转方式network里会请求页面和js,但通过点击跳转的方式只有js,没有再次请求页面.

自定义404页面

直接在pages文件夹里创建一个_error.js页面(只能叫这个名字)
在这里插入图片描述

创建公共导航组件(components文件夹)

不要写在pages有路由的文件夹里,在根目录里我们要创建一个单独的components文件夹,写代码如下:

import Link from "next/link"
const Mynav=()=>{ 
   
    return (
        <div>
          <Link href={ 
   { 
   pathname:"/next-route/teacher",query:{ 
   }}} >
            <button style={ 
   { 
   color:'red'}}>去教师页面</button>
          </Link>
          <Link href={ 
   { 
   pathname:"/next-route/student",query:{ 
   }}} >
            <button style={ 
   { 
   color:'red'}}>去学生页面</button>
          </Link>
       
        </div>
      )
}
export default Mynav

在路由主页中引入使用

在这里插入图片描述
导航效果就出来了:
在这里插入图片描述
student和teacher页面引入方式和上面一样.

布局组件的使用(layouts文件夹)

第一步:创建布局组件
在根目录里创建一个layouts文件夹,里面写我的布局组件,上面导航是共用的,但是下面主体内容会动态变化,怎么实现呢?直接使用react里面的this.props.children属性即可动态渲染主体内容
在这里插入图片描述
第二步:使用布局组件(核心:把布局组件写成双标签形式,在双标签里放入要显示的动态内容即可)
在这里插入图片描述
效果:
在这里插入图片描述
同样方式,在teacher和student页面也把Mynav组件去掉,也改成布局组件Mylayout动态内容显示方式(这样Mynav组件就只有在Mylayout里引入一次,这样就实现了布局组件来布局)

在这里插入图片描述
在这里插入图片描述
这样在路由主页,教师页面和学生页面都采用了布局组件,实现了Mynav导航组件只在布局组件里导入一次.比如如果我们后面还要加一个尾部固定组件的话,那我们只需要在布局组件里再增加一个尾部组件即可,这样非常方便.
在这里插入图片描述

全局布局组件

上面的Mylayout布局组件在主页,教师页和学生页等每个页面都引入了一次,有没有办法全局一次引入呢?办法如下:
在pages文件加下创建_app.js(只能叫这个名字),写如下代码(是固定写法):

import React from 'react'
import App, { 
    Container } from 'next/app'
import Mylayout from './../layouts/Mylayout'

// Layout就是要写的布局组件,其它是固定写法
class Layout extends React.Component { 
   
  render() { 
   
    const { 
    children } = this.props
    return <Mylayout>{ 
   children}</Mylayout>
  }
}

export default class MyApp extends App { 
   
  render() { 
   
    const { 
    Component, pageProps } = this.props
    return (
      <Container>
        <Layout>
          <Component { 
   ...pageProps} />
        </Layout>
      </Container>
    )
  }
}

这样其它页面都不用引入Mylayout局部组件了,只写自己的内容即可.

Link组件路由参数传递和获取的方法

现在需求是老师渲染列表页面点击某位老师要进入详情页面并把id以参数方式传递过去.
在教师页面修改如下:

// import Mynav from '../../../components/Mynav'
// import Mylayout from './../../../layouts/Mylayout'
import Link from 'next/link'
const teacherList=[
    { 
   name:"teacher1",id:1},
    { 
   name:"teacher2",id:2},
    { 
   name:"teacher3",id:3},
]
const Teacher =()=>{ 
   
    return (
        <div>
            { 
   /* <Mynav/> */}
            { 
   /* <Mylayout> <p> teacher页面</p> </Mylayout> */}
            <p> teacher页面</p>
            <ul>
            { 
   
               teacherList.map((item)=>{ 
   
                   return (
                       <li key={ 
   item.id}>
                           <Link href={ 
   `/next-route/teacher/detail?id=${ 
     item.id}`}>
                               <a>{ 
   item.name}</a>
                           </Link>
                           
                       </li>
                   )
               }) 
            }
            </ul>

           
        </div>
    )
}

export default Teacher;

准备一个老师的详情页面,引入withRouter高阶组件,在withRouter()方法里将组件传递过去,然后在props.router.query.id里得到传递过来的参数(重要:withRouter可以获取url里的参数)

import  { 
   withRouter} from 'next/router';

const Detail=withRouter((props)=>{ 
   
    console.log(props);
    
    return (
        <div>
            这是{ 
   props.router.query.id}老师详情页面
        </div>
    )
})
export default Detail;

如果不引入withRouter是得不到query这个属性,所以在next.js中一定要引入withRouter这个方法.
withRouter这个高阶组件会讲当前的路由对象注入到组件中去,并将路由对象绑定到组件的props这个参数上.

使用浅层路由优化路径显示

上面教师详情页显示路径如下
在这里插入图片描述
但路径不好看,我们怎么实现teacher/3这样简洁呢?使用next里的浅层路由即可

其实就是使用Link组件有一个as属性,它可以给路径起别名,在教师页面操作如下:
在这里插入图片描述

解决浅层路由刷新页面找不到页面的问题

上面的教师详情页当刷新页面时,会找不到页面,因为通过as属性,给browser history来个路由掩饰,但是按刷新按钮路由就找不到了,因为服务器回去重新找/p/xxxx页面,但是实际上此时并不存在xxxx页面,这个问题实际要服务器端协助解决(实际就是后台将我们别名的路由地址转为原来真实的路径),方法如下:

  1. 安装express npm install --save express
  2. 在根目录下创建server.js,添加如下内容
    const express = require('express')
    const next = require('next')

    const dev = process.env.NODE_ENV !== 'production'
    const app = next({ 
    dev })
    const handle = app.getRequestHandler()

    app.prepare()
    .then(() => { 
   
      const server = express()

      server.get('*', (req, res) => { 
   
        return handle(req, res)
      })

      server.listen(3000, (err) => { 
   
        if (err) throw err
        console.log('> Ready on http://localhost:3000')
      })
    })
    .catch((ex) => { 
   
      console.error(ex.stack)
      process.exit(1)
    })
  1. 修改package.json文件中scripts字段(作用是再跑服务器)
 "scripts": { 
   
   "dev": "node server.js",
   "build": "next build",
   "start": "NODE_ENV=production node server.js"
 }
  1. 在server.js里创建自定义路由
  server.get('/next-route/teacher/:id', (req, res) => { 
   
      const actualPage = '/next-route/teacher/detail'
      const queryParams = { 
    id: req.params.id }
      app.render(req, res, actualPage, queryParams)
    })

在这里插入图片描述

静态文件(即不需要打包的文件)

比如我们用到的图片,它是不需要打包的,做法是在根目录里新建一个static文件夹(只能叫这个名字),在要用的地方写绝对路径即可.
在这里插入图片描述

请求数据接口(isomorphic-unfetch工具请求数据,里面实现了函数组件和类组件的写法)

isomorphic-unfetch支持服务器端渲染.使用方法如下:
1.安装isomorphic-unfetch

npm install --save isomorphic-unfetch

2.使用

引入

import fetch from 'isomorphic-unfetch'

记住:fetch方法调用前要使用组件.getInitiaProps赋值一个函数,在函数里面调用fetch方法.
因为使用异步静态方法getInitialProps获取数据,此静态方法能够获取所有的数据,并将其解析成一个 JavaScript对象,然后将其作为属性附加到 props对象上
在这里插入图片描述
上面是函数组件,类组件的话写法如下:
在这里插入图片描述

这样数据就出来了.
在这里插入图片描述
注意:getInitialProps 不能 在子组件上使用,只能使用在pages文件夹的页面中进行调用。
同时,getInitialProps接收一个上下文对作为参数,这个对象包含以下属性:

  • pathname: URL的 path部分
  • query: URL的 query string部分,并且其已经被解析成了一个对象
  • asPath: 在浏览器上展示的实际路径(包括 query字符串)
  • req: HTTP request 对象 (只存在于服务器端)
  • res: HTTP response 对象 (只存在于服务器端)
  • jsonPageRes: 获取的响应数据对象 Fetch Response (只存在于客户端)
  • err: 渲染时发生错误抛出的错误对象

样式写法

next.js支持普通的react样式外,还有自己的独特样式,写法如下:

在这里插入图片描述
上面写法有两个属性要注意
jsx:它仅限作用于当前组件,子组件不会生效;
global:它不但作用域当前组件,子组件也会生效.

豆瓣电影项目

创建电影主页面pages/index.js:

在这里插入图片描述

创建公共导航组件components/Movieheader.js:

import Link from 'next/link';
const Movieheader =()=>(
   <div class="movie-header">
       <style jsx>
          { 
   ` .movie-header { position: fixed; top: 0; left: 0; right: 0; } ul { display: flex; justify-content: space-around; align-items: center; padding: 15px 0; background-color: #1e2736; margin: 0; } li { list-style: none; line-height: 30px; height: 30px; } li a { color: white; } li a:hover { color: red; } `}
       </style>
       
       <ul>
           <li>
               <Link href="/movie/type?type=in-theaters"><a>正在热映</a></Link>
           </li>
           <li>
               <Link href="/movie/type?type=cooming_soon"><a>即将上映</a></Link>
           </li>
           <li>
               <Link href="/movie/type?type=top250"><a>top250</a></Link>
           </li>
       </ul>
       
   </div> 
)

export default Movieheader;

电影列表页面pages/movie/type/index.js:

注意数据请求发送方式.

import fetch from 'isomorphic-unfetch';
import { 
   withRouter} from 'next/router'
import Link from 'next/link'
const Movietype = withRouter(props => (
  <div className="movie-type">
    <h1>这是电影详情页</h1>
    <ul>
      { 
   props.movieList.map(item => { 
   
        return (
          <div key={ 
   item.id} className="movie-box">
            { 
   /* 提示:地址里的type值获取方式可以使用withRouter高阶组件获取 */}
            <Link href={ 
   `/movie/detail?id=${ 
     item.id}&type=${ 
     props.router.query.type}`}>
              <div>
                  <img src={ 
   item.img} alt={ 
   item.title}></img>
                  <h4>{ 
   item.title}</h4>
                  <p>评分:{ 
   item.rating}</p>
              </div>
            </Link>
          </div>
        )
      })}
    </ul>
    <style jsx>
      { 
   ` .movie-type { display: flex; flex-direction: column; align-items: center; } .movie-box { display: flex; flex-direction: column; align-items: center; margin: 20px 0; padding: 10px 0; width: 140%; box-shadow: 0 0 10px #bbb; } .movie-box:hover { box-shadow: rgba(0,0,0,0.3) 0px 19px 60px; } `}
    </style>
  </div>
))

Movietype.getInitialProps = async function(context) { 
   
  let res = await fetch(`http://localhost:3301/${ 
     context.query.type}`)
  // console.log(context.query.type);
  
  let data = await res.json()
  console.log(data)
  return { 
   
    movieList: data
  }
}
export default Movietype


电影详情页面

import fetch from 'isomorphic-unfetch'
const Detail=(props)=>(
    <div className="detail">
        <div className="detail-box">
            <img src={ 
   props.detail.img} alt={ 
   props.detail.title}/>
            <h4>{ 
   props.detail.title}</h4>
            <p>电影类型:{ 
   props.detail.genres.join(',')}</p>
            <p>上映时间:{ 
   props.detail.details[0].year}</p>
            <p>剧情介绍:{ 
   props.detail.details[0].summary}</p>
        </div>
        <style jsx>{ 
   ` .detail { width: 40%; margin: 0 auto; padding: 20px; box-sizing: border-box; box-shadow: 0 0 10px #bbb; } .detail-box { text-align: center; } `}</style>
    </div>
)

Detail.getInitialProps=async function(context) { 
   
    let res= await fetch(`http://localhost:3301/${ 
     context.query.type}/${ 
     context.query.id}?_embed=details`)
    let data =await res.json();
    console.log(data);
    
return { 
   
    detail:data
}
}
export default Detail;

最后,介绍一下SEO搜索引擎优化

其实很简单,next.js自己封装了一个head组件,需要seo的地方按需引入,在里面写各自的标签即可.
在这里插入图片描述
完整效果:
在这里插入图片描述

到此,next.js就学到这里了.最后附上全部项目代码克隆链接:
git@github.com:huanggengzhong/SSR.git

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/232032.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • 惠普硬盘型号怎么看_惠普电脑序列号查询真伪

    惠普硬盘型号怎么看_惠普电脑序列号查询真伪大家都知道电脑使用起来非常方便,但遇见硬盘序列号怎么看的时候就非常头疼了,如果你是第一次遇到硬盘序列号怎么看,怎么样才能快速解决硬盘序列号怎么看带来的烦恼呢?小编为大家收集了很多关于硬盘序列号怎么看问题的解决方法,下面请看具体的解决方法步骤:如何查看电脑硬盘序列号问:惠普电脑的XP系统答:第一步单击【开始】→【运行…】,输入cmd第二步输入diskpart,按回车键第三步输入list…

  • WPF中ListBox的WrapPanel布局「建议收藏」

    WPF中ListBox的WrapPanel布局「建议收藏」WPF中ListBox的WrapPanel布局,如果排出来是纵向列表,原因是Theme错了。

  • 为什么百度查到的ip和ipconfig查到的不一样;详解公网Ip和私网ip;详解网络分类ABC;

    为什么百度查到的ip和ipconfig查到的不一样;详解公网Ip和私网ip;详解网络分类ABC; IP可以分为PublicIP和PrivateIP,出现这种规划的原因在于IPv4所能表示的IP太少而电脑太多以至于不够用,然而只有PublicIP才能直接连接上网络,所以对于那些公司,学校,政府机构等场所,就可以集中使用私有的IP进行管理,而大家可以共用一个IP去连接上公网,这样,就省下了许多宝贵的PublicIP。你有没有发现,你每次使用ipconfig查到的地址,要么就是172….

  • SpringBoot事务注解@Transactional

    SpringBoot事务注解@TransactionalSpringBoot提供了非常方便的事务操作,通过注解就可以实现事务的回滚,非常方便快捷,下面我们就说一下如何进行事务操作。1.事务说明在Spring中,事务有两种实现方式,分别是编程式事务管理和声明式事务管理两种方式。编程式事务管理:编程式事务管理使用TransactionTemplate或者直接使用底层的PlatformTransactionManager。

  • 7月上旬国内网站流量统计TOP5:新浪跻身五强居四

    7月上旬国内网站流量统计TOP5:新浪跻身五强居四

  • 简述sealed关键字_java field

    简述sealed关键字_java fieldsealed的中文意思是密封,故名思义,就是由它修饰的类或方法将不能被继承或是重写。sealed关键字:在类声明中使用sealed可防止其它类继承此类;在方法声明中使用sealed修饰符可防止扩充类重写此方法。相当于Java中的final类和final方法密封类:密封类在声明中使用sealed修饰符,这样就可以防止该类被其它类继承。如果试图将一个密封类作为其它类的基类,C#将提示出错。在哪些场合…

    2022年10月22日

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号