React服务器端渲染值Next.js-程序员宅基地

技术标签: ViewUI  webpack  json  javascript  

昨天leader给分配了新任务,让熟悉一下ssr,刚开始有点懵,啥玩意?百度了一下,不就是服务器端渲染(server side render,简称: ssr)。

 

ssr简介

服务端渲染一个很常见的场景是当用户(或搜索引擎爬虫)第一次请求页面时,用它来做初始渲染。当服务器接收到请求后,它把需要的组件渲染成 HTML 字符串,然后把它返回给客户端(这里统指浏览器)。之后,客户端会接手渲染控制权。

当在服务器使用 Redux 渲染时,一定要在响应中包含应用的 state,这样客户端可以把它作为初始 state。这点至关重要,因为如果在生成 HTML 前预加载了数据,我们希望客户端也能访问这些数据。否则,客户端生成的 HTML 与服务器端返回的 HTML 就会不匹配,客户端还需要重新加载数据。

把数据发送到客户端,需要以下步骤:

  • 为每次请求创建全新的 Redux store 实例;

  • 按需 dispatch 一些 action;

  • 从 store 中取出 state;

  • 把 state 一同返回给客户端。

在客户端,使用服务器返回的 state 创建并初始化一个全新的 Redux store。Redux 在服务端惟一要做的事情就是,提供应用所需的初始 state

以上是官方文档对ssr及其一些操作的解释。对于半吊子配置水平的我来说,肯定要寻求最简便的替代方案啦! so,强大的Next.js终于被扒出来了 。^_^

 

Next.js的原则和思想

  • 不需要除 Next 之外,多余的配置和安装(比如 webpack,babel);
  • 使用 glamor 处理样式(不知道什么时候glamor的,请点这里);
  • 自动编译和打包;
  • 热更新;
  • 方便的静态资源管理;
  • 成熟灵活的路由配置,包括路由级别 prefetching;

多说无益,我们来通过一个简单的demo熟悉一下Next.js。

 

Demo:英超联赛积分榜

相信所有的开发者都厌恶超长时间的安装和各种依赖、插件配置。不要担心,Next.js 作为一个独立的 npm package 最大限度的替你完成了很多耗时且无趣的工作。我们首先需要进行安装:

# 本地新建项目文件夹,并进行初始化
npm init
# 安装Next、React及其它一些依赖
npm install next --save
npm install react --save
npm install react-dom --save
npm install axios --save

注:通过npm拉取的next依赖是最新的4版本,对于react的版本必须要求是在16以上(如果是16以下的react版本,请使用next版本2或3)

安装结束后,我们就可以开启脚本:

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

接下来所需要做的很简单,就是在根目录下创建一个 pages 文件夹(必须是根目录,文件夹名称必须是pages),并在其下新建一个 index.js 文件:

// ./pages/index.js

import React from 'react'

export default () => (
  <h1>This is just so easy!</h1>
)

启动项目:

npm start

效果是这样的:

 

为了验证是服务器端渲染,我们查看一下源码:

就是这么简单,清新。如果我们自己手段实现这一切的话,除了 NodeJS 的种种繁琐不说,webpack 配置,node_modules 依赖,babel插件等等就够折腾半天的了。

 

添加 Page Head

在 ./pages/index.js 文件内,我们可以添加页面 head 标签、meta 信息、样式资源等等:

// ./pages/index.js

import React from 'react'
import Head from 'next/head'

export default () => (
  <div>
    <Head>
        <title>League Table</title>
        <meta name="viewport" content="initial-scale=1.0,  width=device-width" />
        <link rel="stylesheet" href="https://unpkg.com/[email protected]/build/pure-min.css" />
    </Head>
    <h1>This is just so easy!</h1>
  </div>
)

这个 head 不是指真实的 DOM,别忘了 React 虚拟 DOM 的概念。其实这是 Next 提供的 Head 组件,不过最终一定还是被渲染成为真实的 head 标签。

 

发送 Ajax 请求

Next 还提供了 getInitialProps 方法,这个方法支持异步选项,并且是服务端/客户端同构的。我们可以使用 async/await 方式,处理异步请求。请看下面的示例:

import React from 'react'
import Head from 'next/head'
import axios from 'axios';

export default class extends React.Component {
    // 通过async/await异步操作getInitialProps
    static async getInitialProps () {
        const res = await axios.get('http://api.football-data.org/v1/competitions/426/leagueTable');
        // 把请求的返回值作为属性值return出去
        return {data: res.data}
      }
 }

这里使用了 axios 类库来发送 HTTP 请求。网络请求是异步的,因此需要在未来某个合适的时候(请求结果返回时)接收数据。这里使用先进的 async/await,以同步的方式处理,从而避免了回调嵌套和 promises 链。

将异步获得的数据返回,它将自动挂载在 props 上(注意 getInitialProps 方法名,不明白的客官可以翻看一下react生命周期),render 方法里便可以通过 this.props.data 获取:

import React from 'react';
import Head from 'next/head';
import axios from 'axios';
import Link from 'next/link';

export default class extends React.Component {
  static async getInitialProps () {
    const res = await axios.get('http://api.football-data.org/v1/competitions/426/leagueTable');
    return {data: res.data}
  }
  
  render () {
    const logoStyle = {
      width: '30px'
    }
    return (
      <div>
        <Head>
          <title>League Table</title>
          <meta name="viewport" content="initial-scale=1.0, width=device-width" />
          <link rel="stylesheet" href="https://unpkg.com/[email protected]/build/pure-min.css" />
        </Head>
        <div className="pure-g">
            <div className="pure-u-1-3"></div>
            <div className="pure-u-1-3">
              <h1>英超联赛</h1>
              <table className="pure-table">
                <thead>
                  <tr style={
    {lineHeight: '200%', textAlign: 'center'}}>
                    <td style={
    {width: 50}}>排名</td>
                    <td style={
    {width: 50}}>球队</td>
                    <td>P</td>
                    <td>GL</td>
                    <td>W</td>
                    <td>D</td>
                    <td>L</td>
                    <td style={
    {width: 50, lineHeight: '200%', textAlign: 'center'}}>操作</td>
                  </tr>
                </thead>
                <tbody>
                {
     this.props.data.standing.map((standing, i) => {
                  const oddOrNot = i % 2 == 1 ? "pure-table-odd" : "";
                  return (
                      <tr key={i} className={oddOrNot} style={
    {lineHeight: '200%', textAlign: 'center'}}>
                        <td>{standing.position}</td>
                        <td><img className="pure-img logo" src={standing.crestURI}/></td>
                        <td>{standing.points}</td>
                        <td>{standing.goals}</td>
                        <td>{standing.wins}</td>
                        <td>{standing.draws}</td>
                        <td>{standing.losses}</td>
                        <td><Link href=''>查看</Link></td>
                      </tr>
                    );
                })}
                </tbody>
              </table>
            </div>
            <div className="pure-u-1-3"></div>
        </div>
      </div>
    );
  }
}

再访问3000端口看一下:

 

路由和页面跳转

也许你已经有所感知:我们已经有了最基本的一个路由。Next 不需要任何额外的路由配置信息,你只需要在 pages 文件夹下新建文件,每一个文件都将是一个独立的页面。

让我们来新建一个详情页面吧!新建 ./pages/details.js 文件:

import React from 'react'
import Head from 'next/head'
import Link from 'next/link'
import axios from 'axios';

export default class extends React.Component {
    static async getInitialProps ({query}) {
        // 从query参数中回去id
        const id = query.id;
        //通过process的browser属性判断处于何种环境:Node环境下为false,浏览器为true
        if(!process.browser) {
            // 发送服务器请求
            const res = await axios.get('http://api.football-data.org/v1/competitions/426/leagueTable')
            return {
                data: res.data,
                // 对查询的数据进行过滤和返回
                standing: res.data.standing.filter(s => s.position == id)
            }
        } else {
            // 没有请求服务器的情况下在此使用缓存
            const bplData = JSON.parse(sessionStorage.getItem('bpl'));
            // 对查询的数据进行过滤和返回
            return {standing: bplData.standing.filter(s => s.position == id)}
        }
    }

    componentDidMount () {
        // 如果没有缓存,通过localStorage在本地缓存数据
        if(!sessionStorage.getItem('bpl')) sessionStorage.setItem('bpl', JSON.stringify(this.props.data))
    }

    render() {
        const detailStyle = {
            ul: {
                marginTop: '100px'
            }
        }
        return  (
             <div>
                <Head>
                    <title>League Table</title>
                    <meta name="viewport" content="initial-scale=1.0, width=device-width" />
                    <link rel="stylesheet" href="https://unpkg.com/[email protected]/build/pure-min.css" />
                </Head>
                <div className="pure-g">
                    <div className="pure-u-8-24"></div>
                    <div className="pure-u-4-24">
                        <h2>{
    this.props.standing[0].teamName}</h2>
                        <img src={
    this.props.standing[0].crestURI} className="pure-img"/>
                        <h3>Points: {
    this.props.standing[0].points}</h3>
                    </div>
                    <div className="pure-u-12-24">
                        <ul style={detailStyle.ul}>
                            <li><strong>Goals</strong>: {this.props.standing[0].goals}</li>
                            <li><strong>Wins</strong>: {this.props.standing[0].wins}</li>
                            <li><strong>Losses</strong>: {this.props.standing[0].losses}</li>
                            <li><strong>Draws</strong>: {this.props.standing[0].draws}</li>
                            <li><strong>Goals Against</strong>: {this.props.standing[0].goalsAgainst}</li>
                            <li><strong>Goal Difference</strong>: {this.props.standing[0].goalDifference}</li>
                            <li><strong>Played</strong>: {this.props.standing[0].playedGames}</li>
                        </ul>
                        <div style={
    {marginLeft:30, marginTop: 120}}>
                            <Link href="/">返回首页</Link>
                        </div>
                    </div>
                </div>
             </div>
            )
    }
}

这个页面根据 query 变量,动态展现出球队信息。具体来看,getInitialProps 方法获取 URL query id,根据 id 筛选出(filter 方法)展示信息。因为一支球队的信息比较稳定,所以在我们在客户端使用了 sessionStorage 进行存储。

别忘了我们的主页(排行榜页面)index.js 中,也要使用相应的 sessionStorage 逻辑。同时,在 render 方法里加入一条链接到详情页的 :

<td><Link href={`/details?id=${standing.position}`}>查看</Link></td>

 

错误页面

在 Next 中,同样可以通过 error.js 文件定义错误页面。在 ./pages 下新建 error.js:

import React from 'react'

export default class extends React.Component {
  static getInitialProps ({ res, xhr }) {
    const statusCode = res ? res.statusCode : (xhr ? xhr.status : null)
    return { statusCode }
  }

  render () {
    return (
      <p>{
        this.props.statusCode
        ? `服务器错误 ${
    this.props.statusCode}`
        : '客户端错误'
      }</p>
    )
  }
}

 

总结

这篇文章实现了一个简易 demo,只是介绍了最基本的 Next.JS 搭建 React 同构应用的基本步骤。

除此之外,Next 还有非常多的功能,非常多的先进理念可以应用。

  • 比如 搭配 prefetch,预先请求资源;
  • 再如动态加载组件(Next.js 支持 TC39 dynamic import proposal),从而减少首次 bundle size;
  • 虽然它替我们封装好了 Webpack、Babel 等工具,但是我们又能 customizing,根据需要自定义。

查看原文

转载于:https://www.cnblogs.com/AllenR/p/7928725.html

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_30454481/article/details/95384335

智能推荐

c# 调用c++ lib静态库_c#调用lib-程序员宅基地

文章浏览阅读2w次,点赞7次,收藏51次。四个步骤1.创建C++ Win32项目动态库dll 2.在Win32项目动态库中添加 外部依赖项 lib头文件和lib库3.导出C接口4.c#调用c++动态库开始你的表演...①创建一个空白的解决方案,在解决方案中添加 Visual C++ , Win32 项目空白解决方案的创建:添加Visual C++ , Win32 项目这......_c#调用lib

deepin/ubuntu安装苹方字体-程序员宅基地

文章浏览阅读4.6k次。苹方字体是苹果系统上的黑体,挺好看的。注重颜值的网站都会使用,例如知乎:font-family: -apple-system, BlinkMacSystemFont, Helvetica Neue, PingFang SC, Microsoft YaHei, Source Han Sans SC, Noto Sans CJK SC, W..._ubuntu pingfang

html表单常见操作汇总_html表单的处理程序有那些-程序员宅基地

文章浏览阅读159次。表单表单概述表单标签表单域按钮控件demo表单标签表单标签基本语法结构<form action="处理数据程序的url地址“ method=”get|post“ name="表单名称”></form><!--action,当提交表单时,向何处发送表单中的数据,地址可以是相对地址也可以是绝对地址--><!--method将表单中的数据传送给服务器处理,get方式直接显示在url地址中,数据可以被缓存,且长度有限制;而post方式数据隐藏传输,_html表单的处理程序有那些

PHP设置谷歌验证器(Google Authenticator)实现操作二步验证_php otp 验证器-程序员宅基地

文章浏览阅读1.2k次。使用说明:开启Google的登陆二步验证(即Google Authenticator服务)后用户登陆时需要输入额外由手机客户端生成的一次性密码。实现Google Authenticator功能需要服务器端和客户端的支持。服务器端负责密钥的生成、验证一次性密码是否正确。客户端记录密钥后生成一次性密码。下载谷歌验证类库文件放到项目合适位置(我这边放在项目Vender下面)https://github.com/PHPGangsta/GoogleAuthenticatorPHP代码示例://引入谷_php otp 验证器

【Python】matplotlib.plot画图横坐标混乱及间隔处理_matplotlib更改横轴间距-程序员宅基地

文章浏览阅读4.3k次,点赞5次,收藏11次。matplotlib.plot画图横坐标混乱及间隔处理_matplotlib更改横轴间距

docker — 容器存储_docker 保存容器-程序员宅基地

文章浏览阅读2.2k次。①Storage driver 处理各镜像层及容器层的处理细节,实现了多层数据的堆叠,为用户 提供了多层数据合并后的统一视图②所有 Storage driver 都使用可堆叠图像层和写时复制(CoW)策略③docker info 命令可查看当系统上的 storage driver主要用于测试目的,不建议用于生成环境。_docker 保存容器

随便推点

网络拓扑结构_网络拓扑csdn-程序员宅基地

文章浏览阅读834次,点赞27次,收藏13次。网络拓扑结构是指计算机网络中各组件(如计算机、服务器、打印机、路由器、交换机等设备)及其连接线路在物理布局或逻辑构型上的排列形式。这种布局不仅描述了设备间的实际物理连接方式,也决定了数据在网络中流动的路径和方式。不同的网络拓扑结构影响着网络的性能、可靠性、可扩展性及管理维护的难易程度。_网络拓扑csdn

JS重写Date函数,兼容IOS系统_date.prototype 将所有 ios-程序员宅基地

文章浏览阅读1.8k次,点赞5次,收藏8次。IOS系统Date的坑要创建一个指定时间的new Date对象时,通常的做法是:new Date("2020-09-21 11:11:00")这行代码在 PC 端和安卓端都是正常的,而在 iOS 端则会提示 Invalid Date 无效日期。在IOS年月日中间的横岗许换成斜杠,也就是new Date("2020/09/21 11:11:00")通常为了兼容IOS的这个坑,需要做一些额外的特殊处理,笔者在开发的时候经常会忘了兼容IOS系统。所以就想试着重写Date函数,一劳永逸,避免每次ne_date.prototype 将所有 ios

如何将EXCEL表导入plsql数据库中-程序员宅基地

文章浏览阅读5.3k次。方法一:用PLSQL Developer工具。 1 在PLSQL Developer的sql window里输入select * from test for update; 2 按F8执行 3 打开锁, 再按一下加号. 鼠标点到第一列的列头,使全列成选中状态,然后粘贴,最后commit提交即可。(前提..._excel导入pl/sql

Git常用命令速查手册-程序员宅基地

文章浏览阅读83次。Git常用命令速查手册1、初始化仓库git init2、将文件添加到仓库git add 文件名 # 将工作区的某个文件添加到暂存区 git add -u # 添加所有被tracked文件中被修改或删除的文件信息到暂存区,不处理untracked的文件git add -A # 添加所有被tracked文件中被修改或删除的文件信息到暂存区,包括untracked的文件...

分享119个ASP.NET源码总有一个是你想要的_千博二手车源码v2023 build 1120-程序员宅基地

文章浏览阅读202次。分享119个ASP.NET源码总有一个是你想要的_千博二手车源码v2023 build 1120

【C++缺省函数】 空类默认产生的6个类成员函数_空类默认产生哪些类成员函数-程序员宅基地

文章浏览阅读1.8k次。版权声明:转载请注明出处 http://blog.csdn.net/irean_lau。目录(?)[+]1、缺省构造函数。2、缺省拷贝构造函数。3、 缺省析构函数。4、缺省赋值运算符。5、缺省取址运算符。6、 缺省取址运算符 const。[cpp] view plain copy_空类默认产生哪些类成员函数

推荐文章

热门文章

相关标签