昨天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/purecss@0.6.1/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/purecss@0.6.1/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/purecss@0.6.1/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,根据需要自定义。

查看原文

React服务器端渲染值Next.js的更多相关文章

  1. React服务器端渲染框架next.js项目实战及部署上下文context问题解决办法

    在前段时间折腾过搭建react服务器端项目架构,点击这里了解下,正好最近有个重构PC网站的需求,考虑SEO等因素.在做技术选型的时候决定采用nextjs.项目开发过程中,nextjs的体验非常棒,个人 ...

  2. 基于React服务器端渲染的博客系统

    系统目录及源码由此进入 目录 1. 开发前准备 1.1 技术选型1.2 整体设计1.3 构建开发 2. 技术点 2.1 react2.2 redux, react-router2.3 server-r ...

  3. 玩转 React 服务器端渲染

    React 提供了两个方法 renderToString 和 renderToStaticMarkup 用来将组件(Virtual DOM)输出成 HTML 字符串,这是 React 服务器端渲染的基 ...

  4. React 服务器端渲染流程

    其实我们在访问客户端渲染的页面时,请求到的只是一个 html 空壳,里面引入了一个 js 文件,所有的内容都是通过 js 进行插入的,正是因为页面是由 js 渲染出来的,所以会带来如下几个问题: 1. ...

  5. react 服务器端渲染 ssr 中 localstorage/history/window is not defined 解决方案

    1.原因 ssr 会在后端执行组件的 componentWillMount 以及在它这个生命周期之前的生命周期 也就是说 ssr 阶段是不会执行 componentDidMount 方法的 当你在 c ...

  6. 【大前端之前后分离01】JS前端渲染VS服务器端渲染

    前言 之前看了一篇文章:@Charlie.Zheng Web系统开发构架再思考-前后端的完全分离,文中论述了为何要前后分离,站在前端的角度来看,是很有必要的:但是如何说服团队使用前端渲染方案却是一个现 ...

  7. react基础学习和react服务端渲染框架next.js踩坑

    说明 React作为Facebook 内部开发 Instagram 的项目中,是一个用来构建用户界面的优秀 JS 库,于 2013 年 5 月开源.作为前端的三大框架之一,React的应用可以说是非常 ...

  8. react实现ssr服务器端渲染总结和案例(实例)

    1.什么是 SSR SSR 是 server side render 的缩写,从字面上就可以理解 在服务器端渲染,那渲染什么呢,很显然渲染现在框架中的前后端分离所创建的虚拟 DOM 2.为什么要实现服 ...

  9. React 服务器渲染原理解析与实践

    第1章 服务器端渲染基础本章主要讲解客户端与服务器端渲染的概念,分析客户端渲染和服务器端渲染的利弊,带大家对服务器端渲染有一个粗浅认识. 1-1 课程导学1-2 什么是服务器端渲染1-3 什么是客户端 ...

随机推荐

  1. PHP 页面静态化/纯静态化/伪静态化

    个人博客迁移至独立博客:https://blog.plcent.com/,欢迎大家访问 概念 PHP静态化分为:纯静态化 和 伪静态化:纯静态化又分为:局部静态化 和 完全静态化 纯静态化:是把PHP ...

  2. javascrip实现:若选中TreeView的父节点checkbox,则其子节点全部选中;子节点全部没选中,则父节点也会没选中。

    <script type="text/javascript"> function public_GetParentByTagName(element, tagName) ...

  3. awake()和start()还有update(),fixedupdate()的差别

    1.首先看一下untiy官方对awake()和start()的定义 awake()和start()函数会在脚本加载后自动调用,awake()会先被调用,即使脚本未被调用.最好用来设置脚本之间的引用和初 ...

  4. 双十一临近,怎样让买家流畅地秒杀? ——腾讯WeTest独家开放电商产品压测服务

    WeTest 导读 十一月临近,一年一度的电商大戏"双十一"又将隆重出场,目前各大商家已经开始各类优惠券的发放,各类大促的商品表单也已经提前流出,即将流入各个用户的购物车中. 作为 ...

  5. web消息推送-goesay

    原文:http://www.upwqy.com/details/22.html 1 GoEasy简介: GoEasy - Web实时消息推送服务专家 最简单的方式将消息从服务器端推送至客户端 最简单的 ...

  6. 1192: [HNOI2006]鬼谷子的钱袋

    1192: [HNOI2006]鬼谷子的钱袋 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 3530  Solved: 2575[Submit][St ...

  7. The Euler function(线性筛欧拉函数)

    /* 题意:(n)表示小于n与n互质的数有多少个,给你两个数a,b让你计算a+(a+1)+(a+2)+......+b; 初步思路:暴力搞一下,打表 #放弃:打了十几分钟没打完 #改进:欧拉函数:具体 ...

  8. JavaScript 和 TypeScript 交叉口 —— 类型定义文件(*.d.ts)

    在 <从 JavaScript 到 TypeScript 系列> 文章我们已经学习了 TypeScript 相关的知识. TypeScript 的核心在于静态类型,我们在编写 TS 的时候 ...

  9. 有道云翻译接口 Show类

    package com.yangchong.fanyi; import java.awt.EventQueue;import java.awt.Toolkit; import javax.swing. ...

  10. setInterval计时器延时问题

    计时器延时问题 js计时器 使用setTimeout.setInterval函数时,第二个参数的设置的时间间隔t是自该函数(setTimeout(f1,t).setInterval(f1,t))被调用 ...