三者的区别和联系
- 服务端渲染, 服务端直接生成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就已经开启服务了;