获取中...

-

Just a minute...

本文将简要介绍几个概念,如SPA, CSR, SSR, 前端路由。适合小白,我将用浅显又生动的话语来介绍这些概念。

什么是SPA

SPA(single page application)单页应用,占了现如今web应用的绝大多数。其意思就如字面所言,但是或许小白们不能真正理解何为单页,且让我讲给你听。我们先说,CSR(客户端渲染)和SSR(服务端渲染)都是SPA,我们先讲CSR的情况

从一个很经典的问题讲起,“当你在浏览器地址栏里输入了一个url,并且敲了回车之后发生了什么”。你只需要知道前端的代码需要部署在服务器上面(注意与作为ajax请求目标的后端接口服务器不同,本文的服务器均指代部署着前端代码的服务器),浏览器会向服务器发送一个请求,而服务器会返回给你html,css和js这些静态资源。多说一嘴为什么叫静态资源,就是因为它是不变的(每个人拿到的都是相同的),它就是你写的前端代码打包出来的产物。试想如果你完的代码会自己变化就太恐怖了。而所谓的动态通常指依赖接口返回的数据(通常不同的用户拿到的也不同)。

而对于SPA来说,相信无论是用vue或者react的各位都还记得,项目的入口html文件里面有个div,id可能是root或者app,它就是页面的根节点。而vue或者react框架,就是写一堆js去生成dom元素,然后把生成的dom元素塞到根节点里面(挂载到根节点下)。如果你打开一个CSR(客户端渲染)的SPA应用,去查看控制台,network里面的Doc下面,有个很简洁的html文件,这个就是服务器传给浏览器的html文档。这个文档,也就是单页应用中的那个单页。

而单页,单就单在,这个网站的各个界面,点击跳转之后的界面,都是渲染在最初的这一份html文档上面的。打个比方,把网站当成一幅画,最初的那份html就是画布。你先在上面画了一个myWebSite.com/home,然后你要跳转到myWebSite.com/about,不是再拿一幅画布,而是把原来的画布擦干净,画一幅新的上去。这也就是前端路由的概念。在SPA中,当你去做一些跳转的操作时,不会再向服务器发送请求新的html文档,而是通过js的操作,去更新html上的内容。

关于前端路由的具体实现,本文不具体讲解,我们只需要知道前端路由是在不重新向服务器请求页面的前提下。改变了url。然后通过一些api,根据url的变化,去渲染不同的组件,从而使得我们能看到不同的页面。当然需要注意,虽然url被改变了,但是浏览器没有向服务器发送请求。如果你这时候刷新页面,则浏览器会试图向服务器发送请求,某些情况下会拿到404响应码,比如vue的history模式路由。

我们回顾SPA的渲染过程,它只在你第一次输入URL并且敲回车的时候请求一次服务器,拿到所有的html,css,js之后,浏览器开始完全接管页面的渲染和交互。通过执行js,将你写的组件挂载到根节点当中。当你试图在应用内(也就是你写的项目里)跳转时,你写的js会在原有html上渲染出新的组件,而你的交互也都是通过浏览器去执行js来实现。这就是CSR(客户端渲染),页面的渲染都是在浏览器上完成的。

什么是SSR

大部分小白们可能刚接触vue和react的时候写的都是CSR,也就是上面提到的内容。CSR有一些缺点,比如FCP(First Contentful Paint)首次内容绘制时间长,说白了就是首屏的白屏时间长(相对较长)。因为一开始浏览器拿到的只有一份空的html,所以用户看到的是白屏。你写的组件要等到浏览器去执行js去渲染到html上。假设浏览器执行js要10秒钟,那用户就会看到10秒的白屏,认为你这个网站好卡啊。

当然这是CSR的缺点之一,其次还有不利于SEO,说白了就是不容易被搜索引擎给抓取到,因为搜索引擎也会认为你这个网站是个空的html(搜索引擎也有做优化,稍有改善)。

为了解决这些问题,人们采用了SSR(服务端渲染)的解决方案。其实最早前后端没分离的时候,就是由后端服务器将动态的数据塞到html模板里面,返回给前端的。当你输入url按下回车之后,浏览器拿到的就是一份完整的有内容的html,而不是空的html。当然这是古早时期的行为了,不在本文讨论范围内。

为了解决CSR由浏览器绘制页面导致的白屏时间长的问题,SSR直接由服务端将你的组件渲染好塞到html文档里面,一起返回给你。说白了,和客户端渲染的区别就是,第一次拿到的是带内容的html文档,css和js。浏览器拿到带内容的html文档,就可以直接展示给用户看了。然后浏览器再执行js,将点击事件之类的添加到页面上,不然你的页面就只能看,没有任何交互了。

举个服务端渲染的例子,我想访问myWebSite.com/home,,服务器会渲染好一个home的html(html当然只能看不能点),连带着css,js返回给浏览器,之后浏览器在执行js。我们以最流行的React框架Next为例讲解,对于静态的页面,那可能如果myWebSite.com/home依赖动态数据,那么你可以在getServerSideProps方法里面去获取,然后将数据作为方法的返回值,这样一来你的页面就能从props拿到动态的数据。比如/home页面可能需要从接口拿用户信息,你可以选择普通的将调用接口写在组件的生命周期里(由浏览器执行js的时候调用),也可以将调接口写在getServerSideProps里(由服务端调用),再讲接口的数据传给页面组件。

那么服务端渲染的前端路由切换是怎么样的呢,还是以next为例,我们这里只讲解getServerSideProps的情况,这种最复杂有趣,其他情况请参见next文档。当你在next中使用前端路由跳转到一个通过getServerSideProps来进行服务端渲染的页面时,你要记得你依然是一个单页应用,next会支持你在原有的html文档上面切换要渲染的组件。但是你的页面是依赖getServerSideProps来给你提供数据的,而getServerSideProps不能在浏览器调用。这下困难了,不能像CSR那样不借助服务器就完成页面的切换了。这个时候next框架会替你向服务器发送一份请求,求助服务器帮忙执行一下新页面的getServerSideProps方法,并且将结果以json格式返回给浏览器。这个请求的url是形如myWebSite.com/{配置的basepath}/_next/data/{generateBuildId}/{query}/{页面的名字}.json。如果这个请求挂掉了(不常见的原因,比如我司配的软路由不恰当,将这个请求导向了错误的地址),那么next的补救措施是,让浏览器重新向服务器发送请求,有服务器渲染一个新的要跳转到的页面,再返回给浏览器,相当于重新执行了一遍最初的工作。

SSR也有缺点,比如每当浏览器重新向服务器请求的时候,都会请求很多的数据,包括当前页面的带内容的html,所有页面的css,所有页面的js(不考虑代码分割,懒加载的情况)。每当SSR的前端路由请求挂掉时,SSR都要重新请求大量的数据。而相对的,CSR只有首次渲染依赖前端服务器,而且这一次请求的内容是不带内容的html,所有页面的css,所有页面的js。这样看来哪怕是单次比较,CSR请求的东西也是比SSR少的,也就是说CSR虽然首次绘制出内容要晚于CSR,但是因为网络传输的内容少,所以会更早拿到请求的内容,从而更早执行完js,使得用户可以交互。而SSR虽然一开始拿到了有内容的html,让用户更早看到页面,但是要传输的内容多,从而晚执行完js,使得用户更晚才能交互。这个指标叫TTI(Time To Interactive),首次可交互时间。

所以SSR和CSR其实各有优缺点,使用的时候需要综合考量。

相关文章
评论
分享
  • 简单通俗解释HTTP缓存

    开始前的碎碎念 最近加班真的很严重,组里同事们都已经苦不堪言。业务交付压力非常大,根本没时间去磨练本领,学习技术。真的很讨厌这种没有成长的日子,每天被业务需求赶着跑。每天下班之后,本来应该用于提升自我的时间也被压榨用来写业务代码。 ...

    简单通俗解释HTTP缓存
  • 发布一个npm包的踩坑

    试着自己发布了一个npm包,记录一下自己踩的坑 package.json首先npm init,然后添加type:module字段,将其定义为一个es6模块。然后在files字段设置要发布上去的内容,比如dist文件夹。 接下来将开发用...

    发布一个npm包的踩坑
  • 前端知识随手记录

    最近几个月被业务需求搞的要死,已经到了周末两天都要去加班的地步了。也没机会像之前那样,晚上下班之后到家学习一两个小时了。最近被逼急了,想着这样加班岂不是永远没有学习的时间,没有学习怎么能提升自己,于是晚上决定十一点到家也要翻翻书,哪...

    前端知识随手记录
  • 动手实现react-ssr 2

    仓库链接:本篇代码 运行方法参考readme,请使用pnpm,因为本次使用的rspack尚未推出1.0版本,预计后续可能会有很多breakchange,所以lock文件至关重要,而我只上传了pnpm的lock,非常抱歉。 环境:no...

    动手实现react-ssr 2
  • 动手实现react ssr

    关于ssr相关的介绍,可以移步之前的一篇博文,本文不再赘述 日常工作中,我们可能都接触过ssr,不过我们可能通常是借助于框架(例如Next.js)的支持来完成ssr。今天我想尝试,摆脱高度封装的前端框架,只依赖react相关的库,来实...

    动手实现react ssr
  • 基于tapable来模拟webpack插件机制

    前两天看到ByteFE公众号发了篇文章,从源码去讲webpack。我之前也从很多方面去学习webpack,只是源码一直没太看得进去,这一次我想仔细地读一读。 简要介绍tapable说实话,之前看webpack源码一直没看的太懂,...

    基于tapable来模拟webpack插件机制
  • 深入js——作用域,作用域链,执行栈(一)

    最近在补js基础。当初自学js的时候,总是感觉学的不扎实。只能说不去上手,再怎么死读书,也理解不了。积累了经验,然后自己去思考,这个时候再去看理论,就容易理解了。 感觉工作一年以来,确实学到了很多,但是过于专注于应用方面的知识,反倒...

    深入js——作用域,作用域链,执行栈(一)
  • webpack运行时代码简要分析

    开个坑,主要也是怕自己后面忘了 简单写写,有空了补上 __webpack_modules是一个数组,每个元素都是一个函数,函数接受三个参数:第一个参数module好像没用到,第二个是一个对象(会把本模块要导出的属性,赋值给这个对象),...

    webpack运行时代码简要分析
  • webpack动态加载原理简述

    为什么需要动态加载上一篇讲我写webpack针对markdown的loader的时候,提到了希望能动态加载以优化性能。篇幅原因没有展开说,打算单独写一篇文章讲一下。 当我们访问网站的时候,浏览器会向服务器请求页面资源。首先请求的当然是...

    webpack动态加载原理简述
  • 编写一个简单的webpack loader的踩坑

    因为觉得hexo框架搭的博客功能一般,就有了想要自己写一个博客的想法。然后就想着如果把markdown文件也放到项目里的话,会比较方便,发现需要编写一个加载markdown的loader,于是就有了这篇博客,来记录一下踩过的坑和收获...

    编写一个简单的webpack loader的踩坑
Please check the parameter of comment in config.yml of hexo-theme-Annie!