客户端渲染, 服务端渲染和同构

3,348 阅读6分钟

三者的区别和联系

  • 服务端渲染, 服务端直接生成html,返回给浏览器,但是交互能力有限, 可以联想到node应用的时候,我们使用模板语言比如ejs,把数据和模板绑定, node端直接返回html代码, 我们平常所说的node中间层,BFF,如果提供的数据请求以及模板渲染, 那就是后端渲染, 如果只是提供数据层的处理, 而对于静态资源只是做一次转发, 那就不是服务端渲染,适用于任何的后端语言, 比如php,java执行吐出html代码
  • 客户端渲染, spa应用基本都是,浏览器首先渲染的是一套空的html,然后请求js,ajax请求, 最后返回完整的html
  • 同构, 是客户端渲染和服务端渲染的整合,代码总共执行2次, 在服务端执行一次,用于服务端渲染,在客户端执行一次,用于接管页面的交互,可以在服务端和客户端都可以运行的程序;

为什么需要SSR

  • seo,以及首屏渲染时间
  • spa为什么对seo不友好,那就要说到seo的原理了, seo不只是html页面中head头里面的内容,还包括页面的内容,搜索引擎会抓取网站的内容, 然后对比排名,spa第一次http请求的时候, 根本没有html内容,所以对seo来说就是白纸
  • 搜索引擎要根据网页的内容才能给网页权重
  • 前后端分离是seo的一大罪魁祸首, html页面不会立即渲染出来,而是等待ajax请求完成之后才渲染完整的html页面

单页面or多页面

  • 无论是单页面还是多页面,只要是实现了前后端分离, 对seo都不友好;
  • 单页和多页的区别
    • spa: 单页好管理, 组件公用, 需要框架搭建;减少二级页面的资源请求, 提升二级页面的打开速度
    • 多页是多个html, 页面之间的跳转会出现卡顿现象,
    • 多页可以分为2种形式,一种是传统的不使用react,vue框架的,直接手写html,这种相对来说友好, 访问页面, 经历一次http请求, 请求响应回来, 页面就被加载进来,
    • 另外一种就是用react,vue之类的框架,html 全部是js生成, 这样首屏渲染时间就会变长
  • 多页只是为了首屏渲染时间, 其实用骨架图就可以解决这个问题,让用户有感就可以
  • 比如很多电商网站, 是多页的, 但是基本一个大的模块是一个单页, 这样就做到了相对隔离

为什么又流行服务端渲染了

  • 二句话总结:
    • 当初是你要分开,分开就分开
    • 现在又要用kpi,把我唤回来
  • ajax出现, 前后端分离成为趋势, spa流行, seo解决不行, 等技术成熟,nodejs出现, 开始搞同构

实现SSR很难吗?

  • node向前是同构, 向后是API包装BFF(服务于前端的后端)
  • VUE, Nuxt.js
  • React, Nextjs
  • 自己手写同构

分析对比

  • 前端无论采用什么样的渲染方式,都是为了增强用户体验,按照目前的发展趋势,同构和spa会是目前的主流
  • 首屏渲染,代码分割和骨架图可以解决
  • SEO只能是同构和服务端渲染:
    • 通过meta的元属性进行关键字的SEO
    • 提供一套静态HTML模版供浏览器抓取用以SEO(比如prerender.io)
  • 同构的缺点是除了增加代码的复杂度,还会增加服务器的负载, 一般推荐首屏或者个别页面进行ssr,其他页面仍然是spa

原理

  • 之所以能够实现同构是因为虚拟dom的存在, 如果在实现SSR的代码中有操作dom的存在, ssr就会报错, 因为node端是不存在dom的

  • 同构的过程是, 代码首先在服务端运行一次, 然后在客户端运行一次. 难点主要在于我们要开发2个入口文件, 服务端入口需要考虑异步请求, 静态资源的处理(css), 模板生成等,还要维护一个node服务器, 需要进行服务器的日志记录,报警服务.
  • 服务端运行
    • 开启node服务, 根据路由渲染出html代码,React提供了StaticRouter, renderToString的方法, 直接把html作为字符串输出到页面
    • 需要提供2个入口页面,通过webpack配置打包成入口文件,一个是server.js, 我们需要在加载路由的时候, 把数据请求出来, 并且挂载到store上, 然后通过renderToString渲染出来
  • 客户端运行
    • 需要提供一个client.js,然后浏览器开始解析了, 只需要按照正常的spa应用的开发即可

NEXT.js 实现SSR

  • next.js也是一个大而全的框架,提供了各种能力, 无论是路由文件化,less,模块化,代码自动分割, 部署等,它把复杂的webpack配置隐藏, 接管路由, 遵守约定大于配置的思想, umi也是借鉴了它的思路
  • 官方的api文档上手起来非常容易

记录下next.js开发项目部署上线的问题:

  • 实现部署到服务器, 服务端渲染, 肯定要有一个服务来直出html, 用node做的话肯定要有一个端口来监听, 所以我们首先要开启一个node服务, 而Next.js生成的项目本身就是一个node服务, 我们只需要npm run build之后然后npm run start 就可以开启整个服务端渲染;

  • 所以在服务器上要做的就是执行2条命令,

    • npm run build
    • npm run start
    • 我们在package.json中把build命令修改为: next build && PORT=3001 npm start
    • 然后我们要使用pm2来守护进程, 执行build命令: pm2 start npm --name "next" -- run build, 其中的name是指package.json中的name名字;
    • 我们开启的是3001端口, 所以必须要要在nginx配置下反向代理, 把3001的端口的请求转发下,
    • nginx配置如下, 静态资源也要配置; server_name是你配置的域名, nginx配置完成之后然后重启下nginx, 就可以访问了.
        upstream next {
             server 127.0.0.1:3001;
        }
        server {
        listen 80;
        server_name next.******;
        access_log /usr/local/nginx/logs/access_nginx.log combined;
        #root /www/NEXT;
        #index index.php index.html index.htm;
        location / {
           proxy_pass http://127.0.0.1:3001;
        }
    
    
       location ~ .*\.(html|htm|gif|jpg|jpeg|bmp|png|ico|txt|js|css|map)$ {
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_pass http://127.0.0.1:3001;
            proxy_redirect off;
       }
    
  • 其实next就是开启了一个node服务, 然后用pm2 run build就可以了, 开启进程守护, 之前一直纠结的是以为next.js是一个类似于生成单页面应用,必须还要有一个服务器去渲染路由, 其实一个next.js应用就是一个node服务, 编码完成之后,只需要把代码提交到服务器,和nginx绑定,然后开启进程,

  • 当然本地只需要npm run build就已经开启服务了;

3张图让你了解服务端渲染

为什么服务端渲染会重新流行起来了

next.js实现服务端渲染, 用next.js实现的例子

umi开始支持ssr了

umi-ssr例子:

同构和服务端渲染

next.js 服务器部署

pm2使用手册

React同构1

React同构2