react 返回上一页_技术微课堂丨优化保险用户体验,提升搜索引擎排名——使用NextJS玩转React服务器端渲染项目...

23cb1b77800a62977b1d38b43cd1097a.png

0 1左中括号单页面应用简介左中括号

React是最流行的JavaScript框架之一,它是典型的开发单页面应用(Single Page Application,简称SPA)的工具。在SPA中,整个应用只有一个页面,所有需要从服务器端取的数据,都通过异步接口获得,而不是写在页面的HTML中。如图所示:

b1dfd82f1977872242de247b03443666.png

SPA虽然只有单一页面,但是通常会通过路由功能,把它分成多个逻辑页面。假设 https://example.com 是SPA,它可以把https://example.com/#/ 作为逻辑页面的根目录,https://example.com/#/product-list 作为产品列表的逻辑页,https://example.com/#/product-detail/3 作为3号产品的详细页。虽然URL不同,但是它们相差的只是#后面的内容。在用户看起来,每页的界面相差非常大,是逻辑上不同的页面。

SPA的优点非常多,包括:

1.切换(逻辑)页面时性能好。这是因为只需要通过GraphQL/Rest接口取得必需的数据,不必加载整个新页面,从而避免了加载无用数据;

2.减轻服务器压力。这也是因为避免加载无用数据,降低单个请求占用的吞吐量;

3.不同前端共用同一套后端代码。PC Web、HTML5和混合式App可以共享同一套后端接口。

然而,在默认情况下SPA也是有缺点的:

1.首次加载耗时太多。在传统Web应用中,是通过服务器端吐出整个页面的HTML结构以及交互逻辑的。但在SPA中,这些是由JavaScript通过异步接口渲染的。渲染从服务器端转移到了客户端,因此首次加载要处理这些事,自然耗时较多。首次加载缓慢,对用户体验伤害非常大。

2.不利于搜索引擎优化(SEO)。因为HTML结构是由JavaScript在客户端异步画出来的,搜索引擎难以识别,从而难以很好地收录网站,造成了潜在的经济损失。

0 2左中括号使用服务器端渲染解决SPA的问题左中括号

怎样才能在享用SPA优点的同时克服SPA的缺点呢?答案是使用服务器端渲染处理首次加载。在服务器端渲染的架构下,SPA有以下特点:

1.首次加载时,服务器会把首次需要的HTML和样式吐给客户端(脱水);

2.客户端将以上数据渲染成初始页面,但是它缺乏交互能力;

3.服务器将交互能力的JavaScript吐给客户端(注水);

4.后续每次渲染,客户端都像往常的SPA一样,通过异步接口取数据,并渲染到界面。

可见,通过这种模型,将首次加载SPA做成了类似传统JSP页面的流程。同时后续的渲染,不失SPA的特点。因此,它可以解决SPA首次渲染加载慢的问题,也可以解决SPA难以被搜索引擎收录的问题。

0 3左中括号使用NextJS搭建服务器端渲染的SPA左中括号

我们以React技术栈为例,使用NextJS框架搭建示例项目。

首先安装NextJS的脚手架create-next-app,它类似大名鼎鼎的create-react-app,可以一键生成Next应用的各个初始文件。安装的命令是:

npm install --global @create-next-app/core

在安装后,我们可以在全局调用create-next-app命令。运行:

create-next-app demo

它会生成demo目录,并从npm等服务器获取各个文件,要等几分钟才能建成。

f9a4861034d34af89db4baa72d7a8d67.png

初始化完成后,通过命令cd demo进入项目目录。为了便于开发,我们安装axios库,它是用来代替fetch的:

可以看到目录结构如图所示:

9d50265ef41bb9106d3e1293b80968eb.png

其中pages目录存放的是各页面的文件,以及所有API文件。我们在开发中会主要修改这个目录中的内容。

b586eac3c3f11c835e54d3f7289d059b.gif 下面我们用它来展示我司的一部分保险产品

从泰康在线网站找一些保险产品,加在API中。方法是新建products.js,把它的内容设置为

// 所有产品的数据

const products = [

   {

       id: 1,

       name: '泰爱保·百万医疗险(尊享就医版)',

       price: 1,

   },

   {

       id: 2,

       name: 'e无忧-泰康重疾保',

       price: 53,

   },

   {

       id: 3,

       name: '泰无忧·重疾加倍保',

       price: 56,

   },

   {

       id: 4,

       name: 'e无忧 - 泰康全能保(少儿版)',

       price: 313,

   },

   {

       id: 5,

       name: '泰爱保·百万医疗险(特药版)',

       price: 155,

   },

   {

       id: 6,

       name: '泰安心·家庭财产保险',

       price: 135,

   },

   {

       id: 7,

       name: 'e畅行-全年综合意外',

       price: 122,

   },

   {

       id: 8,

       name: '小天才儿童综合保障计划',

       price: 113,

   },

   {

       id: 9,

       name: 'e畅行-女性百万意外险',

       price: 268,

   },

   {

       id: 10,

       name: 'E齿康齿科保险(普惠计划)',

       price: 588,

   },

   {

       id: 11,

       name: 'e畅游-境外旅行保至尊款',

       price: 276,

   },

   {

       id: 12,

       name: '泰无忧·住院保(2020版)',

       price: 239,

   },

   {

       id: 13,

       name: 'e无忧 -少儿重疾保',

       price: 52,

   },

   {

       id: 14,

       name: 'e畅行-短期综合意外',

       price: 7.96,

   },

   {

       id: 15,

       name: '泰无忧·高端医疗保险',

       price: 346,

   },

]

// 每个5个产品

const pageSize = 5

// 第一页的产品,用来从服务器端渲染

export const firstPageProducts = products.slice(0, pageSize)

// 根据offset参数,返回相应的产品列表,以JSON格式,并告知是否已加载完成

const render = (req, res) => {

 res.statusCode = 200

 let offset = 0

 if (req.query.offset) {

     offset = Number(req.query.offset)

 }

 const isDone = pageSize + offset >= products.length

 const list = products.slice(offset, pageSize + offset)

 const result = { isDone, list }

 res.json(result)

}

export default render

这个API文件无需配置,能通过:

http://localhost:3000/api/products 

来访问,这种将文件映射成接口的做法,是NextJS的一大特色。随后我们可以看到,整个pages目录中的其它文件,也具备这一特性。接口每次最多返回5项(一页)。并且可以在后面加?offset=X,X是数字,代表起始位置(从0开始)。

例如:http://localhost:3000/api/products?offset=2 会返回第3-7项。

在API文件中,我们指定了全部产品,并把第一页用到的产品作为firstPageProducts变量导出。后面,我们会在服务器端渲染它,而在客户端渲染其它。

现在我们在pages目录中加入products.js,并把它的内容设置为

import React from 'react'

import axios from 'axios'

// 初始载入第一页产品,用服务器端渲染

import { firstPageProducts } from './api/products'

export default class Products extends React.Component {

 constructor(props) {

     super(props)

     this.state = {

         // 初始载入第一页产品,用服务器端渲染

         list: firstPageProducts,

         isDone: false,

     }

 }

 // 加载更多产品,在客户端执行

 loadMore = async () => {

     const offset = this.state.list.length

     const response = await axios.get(`/api/products?offset=${offset}`)

     const { data } = response

     const list = [...this.state.list, ...data.list]

     const { isDone } = data

     this.setState({ list, isDone })

 }

 render() {

     return (

       

           

保险产品列表

           {

               this.state.list.map(

                   product => (

                           {product.id}. {product.name}

                   )

               )

           }

           {

               !this.state.isDone ?

               加载更多 :

               null

           }

     )

 }

}

这里面的意思是,先用import的方式把API导出的firstPageProducts做成state中的产品列表,在服务器端渲染它们(通过直接吐HTML的方式)。随后,提供一个按钮“加载更多”,用户点击时,异步拉取后续的产品。当产品全部加载时(isDone=true),移除按钮。

0 4左中括号在本地调试NextJS项目左中括号

那么,怎么把项目在本地“跑起来”呢?用到的命令就是

npm run dev

用过create-react-app的同学请注意,这里不能用npm start。 此时用浏览器打开 http://localhost:3000/products 可以看到已经渲染出来第一页的5个保险产品。

0f768c1905296d4aba8ef546b7b1dccc.png

并且,点击“加载更多”时,能够载入后面的保险产品。 React客户端渲染也能做到上述功能。但是,这里的神奇之处在于,第一页的5个保险产品是从服务器端渲染出来的!不信,你可以查看源代码

5e4c587caed683fdfe9dd3ef265b9737.png

显然,这意味着首次加载的代码,可以快速画出初始界面,并且被搜索引擎收录也不是问题! 0 5左中括号在生产环境部署NextJS项目左中括号

打包部署的命令,与create-react-app相同,它是

npm run build

此时会把网站各项打包到.next目录中。它相当于create-react-app的build目录。

但是,这还没完,因为这个项目依靠服务器上的程序,所以不可能发布为纯静态Web应用。我们必须运行跑服务器的命令。它是

npm run start

此时,我们可以在 http://localhost:3000/products 看到效果了。效果图与调试的界面完全相同,不必再贴出来。细心的人会注意到,它为什么与调试的网址一致呢?是的,网址没变,但这次运行的是经过优化过的、可以对外发布的Production代码了。

如果你细心,会发现网页源代码中虽然也具备服务器端渲染的特征,包含初始的几个产品,但源代码有所变化。原来,里面的JS等资源已经经过处理,成为正宗的Production代码!

d635e88b9fe0cff98fd5980146ecbbdc.png

对外发布网站怎能用3000端口。一般我们会通过nginx等工具做端口转发,使得它发布在80端口。

到了这步还有很大的服务器端的优化空间,例如使用pm2,本篇不赘述。

通过服务器端渲染,我们既享受了SPA的各项优点,又克服了SPA的缺点。同时,我们使用的是同一套代码,这叫同构。它使得客户端和服务器端代码一致,易于维护,减少了不同团队之间的沟通成本。

划重点:

严格说,以这种方式路径/products发布,不能算单页面应用。因为这里路由不是在#号做的,这个页面也不是逻辑页面。随着应用增大,会有多个这样的路径被加入。例如为了允许用户维护信息,加入 /user-info 。

但是,每个路径仍然可以通过在#后面加参数,做出它自己的许多逻辑页面,因此每个路径可以看作一个微型SPA,整个应用是把它们整合起来了。

c2d991890c0c83fe91014098814944d8.png

赢战新三年

600d77572cbfcedc4f2c197cbe29f409.png

8ed01a1e18dce36aa6ff14393c10e34f.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值