vue服务端渲染同构渲染(代码片段)

shengxiaguangnian shengxiaguangnian     2022-12-14     610

关键词:

引言

自JavaScript诞生以来,前端技术发展非常迅速。移动端白屏优化是前端界面体验的一个重要优化方向,Web 前端诞生了 SSR 、CSR、预渲染等技术。

十年前,几乎所有网站都使用 ASP、Java、PHP 这类做后端渲染,但后来随着 jQuery、Angular、React、Vue 等 JS 框架的崛起,开始转向了前端渲染。2014年起又兴起了同构渲染,号称是未来,集成了前后端渲染的优点,当真如此?

我们先明确三个概念:

后端渲染:后端渲染指传统的 ASP、Java 或 PHP 的渲染机制;

CSR:前端渲染 指使用 JS 来渲染页面大部分内容,代表是现在流行的 SPA 单页面应用;

同构渲染:指前后端共用 JS,首次渲染时使用 Node.js 来直出 HTML。一般来说同构渲染是介于前后端中的共有部分

以及常用性能测试时间点

  • 首次内容绘制FCP(frist conntentful paint) 浏览器将第一个dom渲染到屏幕的时间,也就是通常所说的白屏时间
  • 文档加载时间DCL(domContentLoaded)
  • 首次有意义的绘制FMP(frist meaningful paint) ,就是说主要内容出现在页面上的时间,用户希望看到的主要内容出现在屏幕上的时间

在前端渲染领域,主要有以下几种方式可供选择:

   CSR  预渲染    SSR同构
优点
  • 不依赖数据
  • 局部刷新,无需每次都进行完整页面请求
  • FP 时间最快
  • 客户端用户体验好
  • 内存数据共享
  • 懒加载
  • 富交互,可使用JS实现各种炫酷效果
  • 节约服务器成本
  • 不依赖数据
  • FCP 时间比 CSR 快
  • 客户端用户体验好
  • 内存数据共享
  • SEO 友好
  • 首屏性能高,FMP 比 CSR 和预渲染快
  • 兼容性问题较优秀
  • SEO 友好
  • 首屏性能高,FMP 比 CSR 和预渲染快
  • 客户端用户体验好
  • 内存数据共享
  • 客户端与服务端代码公用,开发效率高
缺点
  • SEO 不友好
  • FCP 、FMP 慢
  • 首屏白屏问题
  • SEO 不友好
  • FMP 慢
  • 开发成本高(构建)
  • 客户端数据共享成本高
  • 模板维护成本高
  • Node 容易形成性能瓶颈
  • 实施难度大

 由上表格可得知,各种渲染模式优缺点各异,看起来并没有那么完美的解决方案,我们只能依据自身产品需求,选择最适合我们的渲染方案。

应用场景 

1、一个优秀的产品 ,离不开SEO,它包含了很多方面的知识,一般公司都会配置专门的SEO职位, 毕竟有太多事情要做了。而我们作为一个有追(破 )求(不)完(得)美(已)的前端,在开发的时候,需要做好页面结构科学、标签语义化、站点路由精简、提供必要的爬虫信息(如 Robot.txt,Sitemap,标签 role,ref 等等);然而在现实中CSR大行其道,SPA站点更是数不胜数,用户体验得到了质的提升,然而,然而,为了兼顾SEO,开发者不得不为爬虫提供专门的shadowsite,或者自己顶起下载自己的页面提供给爬虫,或者使用SSR,刀耕火种的年代。。。

2、首屏渲染速度

目前前后端的分离的前端项目需要先加载静态资源,再异步获取数据,最后渲染页面,在这个过程中的前两部页面都是没有数据的,影响了首屏的渲染速度,也就影响了用户的体验。 目前对于首屏渲染速度的提升有许多方案,在ssr之外还有龙骨,墓碑,数据直出。相比于这些方案ssr方案实现是最复杂的,但效果也是最好的。

3、方案选择

vue 中做同构,有两种,一种是基于官方Vue SSR指南推荐的SSR,一种是vueJS通用应用框架 NUXT.

官网方案,可以更直接的控制应用程序,更深入底层,也比较灵活。NUXT,你懂得,就是那种你拿来即用的效果,提供了部分额外功能,e.g. 静态站点,可用于快速实现Vue SSR

SSR和预渲染的使用场景还是有比较明显的区别的,预渲染的使用场景更多的是我们所说的静态页面的形式。而SSR适用于大型的。页面数据处理较多且较为复杂、与服务端有数据交互的功能性网站,一个明显的使用场景就是电商网站

今天为大家介绍的这个,是部分同构方案,开始。。。

使用简介

   技术图片

从官网借来的图可知,SSR有两个入口文件,entry-client和entry-server两个文件,都包含了应用代码,webpack会根据这两个入口文件分别打包成给服务端用的,Server Bundle及客户端使用的Client Bundle ,当服务器接收到了来自客户端的请求之后,会创建一个渲染器 bundleRenderer,这个 bundleRenderer 会读取上面生成的 server bundle 文件,并且执行它的代码, 然后发送一个生成好的 html 到浏览器,等到客户端加载了 client bundle 之后,会和服务端生成的DOM 进行 Hydration (判断这个 DOM 和自己即将生成的 DOM 是否相同,如果相同就将客户端的Vue实例挂载到这个 DOM 上, 否则会提示警告)。

   而在vue服务器端渲染时,就不能只是使用web-dev-server和web-hot-middle了,因为我们需要添加服务器渲染的node代码逻辑,这样,我们可以自己开一个node服务器,使用webpack-dev-middle中间件进行打包、使用webpack-hot-middle中间件进行热更新,并添加服务器端渲染逻辑,即node端通过引入vue-serverer-renderer插件来渲染服务器端打包的bundle文件到客户端。

如何用?

所以在项目开发期间,其实我们是运行着两个node服务,一个做代理服务,一个提供ssr和其他的一些服务。

 1、client-server的作用:

  • 客户端打包bundle,然后提供给浏览器进行混合静态标记,在默认情况下,可以在浏览器端输出vue组件,进而生成、操作DOM,然而,服务端渲染时将同一个组件渲染成浏览器端的Html字符串,但是这些个只是字符串,而非应用程序,比如没有CSS等;该html字符串然后发送给浏览器,然后结合客户端打包,最后将静态标记‘混合’为客户端上,生成完全交互的应用程序
  • 数据同步的问题,在挂在到客户端应用程序之前,需要获取到与服务端应用程序完全相同的数据,即为window.__INITIAL_STATE__,然后通过store.replaceState(window.__INITIAL_STATE__),这样,就会替代本地store中的state,否则,客户端会因为使用与服务器端不同的状态,导致混合失败。
  • 服务器端打包代码是为了提供html静态页面,客户端代码打包时为了后期的交互与数据处理,以及服务端打包失败的的异常处理

2、entry-server的作用:

  • 前端请求过来的时候,根据getMatchedComponents方法 服务端匹配对应的URL,然后会以此寻找对应的组件或者静态资源
  • 该配置主要用于生成 bundle   vue-ssr-server-bundle.json ,生成之后,传递给createBundleRenderer ,这里会检查组件是否有 asyncData 方法,然后下一步
  • pre-fetch,服务器端的数据预获取:如果组件需要预获取数据,我们就调用自己约定好的asyncData 方法,获取到数据,并存储到服务端的store中,然后将解析完成的状态附加给上下文,并且 `template` 选项用于 renderer 时,状态将自动序列化为 `window.__INITIAL_STATE__`,并注入 HTML。下面是一个页面简单请求的例子
  • 技术图片

     

  • 异常处理,如果在预获取数据过程中出错了,我们给一个标识  serverError,并注入HTML中,然后浏览器端可以根据此状态进行重新渲染,或者异常展示都可以。

2、由于在node环境中,无法用webpack进行CSS文件处理,所以在配置文件中我们需要对服务端渲染时,进行CSS白名单处理 

1 externals: TARGET_NODE? nodeExternals(whitelist: [/\\.css$/] ):undefined

3、我们从打包出来的代码中可以看到,是vue-ssr-server-bundle.json,而非常规的JS文件。这是因为node环境下,每次打包成js文件,你得考虑node的热部署,并且node环境也不支持sourcemap,所以引入了  vue-server-renderer 组件,这种方式类似于常规的render,支持热部署,支持source map ,在配置文件中,我们配置了该组件下的服务端渲染插件,可以使得服务端渲染输出的是json文件。

4、

在CSR模式下,我们开发过程中会进行热部署,这样会大大提高工作效率。 

 1 let bundle
 2 serverCompiler.watch(, (err, stats) => 
 3   if (err) 
 4     throw err
 5   
 6   stats = stats.toJson()
 7   stats.errors.forEach(error => console.error(error))
 8   stats.warnings.forEach(warn => console.warn(warn))
 9   const bundlePath = path.join(
10     webpackConfig.output.path,
11     ‘vue-ssr-server-bundle.json‘
12   )
13   bundle = JSON.parse(mfs.readFileSync(bundlePath, ‘utf-8‘))
14   console.log(‘new bundle generated‘)
15 )

 注意事项

  1.  代码执行环境不同引起的bug
    1. 服务端是没有window,document对象的,storage的实现类也甭想了。。。location更不用说了。。如果加了这些个对象的引用和操作,在服务器端会引起报错,node若报错,可想而知。。
    2. 生命周期的钩子函数  beforeCreate和created会在SSR过程中调用,但是也同时会在客户端执行。。。写代码前最好判断一下你的执行环境。
  2.   接口代理问题:可以采用DevServer进行代理转发请求,或者用axios也可以,不然存在跨域问题
  3. 服务端渲染 的时候必须路由必须使用history模式,处理好请求不在的回调
  4. cookie穿透:因为http请求是先到SSR服务器,再有SSR服务器去后端服务器器扭曲想要的数据接口,客户端请求的时候是带着cookie数据的,而SSR服务器请求后端接口的时候,却没有相应的cookie数据,因此在SSR服务器进行接口请求的时候,我们需要手动拿到客户端的cookie传给后端服务器。我们使用是axios,就可以手动设置axios请求的headers字段,达到cookie穿透的目的。
  5. 数据泄露:因为使用了vuex,如果不使用惰性加载,容易造成数据泄露的情况发生,任何需要登录获取数据的情况,建议在客户端进行。

异常处理

  1、服务器预获取数据的异常处理,参考上面的entry-server中所说,让客户端主动获取数据,再次尝试渲染

  2、在服务端数据预获取的生命周期结束后的渲染页面过程中出现的异常,包括各种操作数据的语法错误等,如对undefined取属性。编写过程要注意全局环境下的代码,是否试用,做环境判断。

缓存策略 

虽然 Vue 的服务器端渲染(SSR)相当快速,但是由于创建组件实例和虚拟 DOM 节点的开销,无法与纯基于字符串拼接(pure string-based)的模板的性能相当。在 SSR 性能至关重要的情况下,明智地利用缓存策略,可以极大改善响应时间并减少服务器负载,这是一个取舍的过程。vue服务区缓存分为页面缓存、组建缓存和接口缓存;

 页面缓存

将渲染完成的页面缓存到内存中,同时设置最大缓存数量和缓存时间。 优势:大幅度提高页面的访问速度 代价:增加服务器内存的使用

 1 const microCache = LRU(
 2   max: 100, // 最大缓存的数目
 3   maxAge: 1000 // 重要提示:条目在 1 秒后过期。
 4 )
 5 const isCacheable = req => 
 6   // 判断是否需要页面缓存
 7   if (req.url && req.url === ‘/‘) 
 8     return req.url
 9    else 
10     return false
11   
12 
13 const handleRequest = async (ctx, next) => 
14   const req = ctx.req
15   const res = ctx.res
16   const cacheable = isCacheable(req)
17   if (cacheable) 
18     const hit = microCache.get(res.url)
19     if (hit) 
20       return res.end(hit)
21     
22   
23 ...
24 ...
25 

 

组件缓存

使用较少,本次并未涉及

接口缓存

将通用的接口缓存到内存,减少服务端接口请求的时间

 get (url, params = ) 
        // url = baseUrl + ‘/sa‘ + url
        url = baseUrl + url
        const key = md5(url + JSON.stringify(params))
        // 判断是否有缓存,直接返回缓存结果
        if (params.cache && microCache.get(key)) 
          return Promise.resolve(microCache.get(key))
        
        let Cookie = ‘‘
        if (params.req && params.req.headers && params.req.headers.cookie) 
          Cookie = params.req.headers.cookie
        
        return new Promise((resolve, reject) => 
          axios(
            url,
            params,
            headers: 
              ‘X-Requested-With‘: ‘XMLHttpRequest‘,
              ‘Cookie‘: Cookie
            ,
            method: ‘get‘
          ).then(res => 
            // 判断是否需要缓存 如果需要缓存缓存数据
            if (params.cache && microCache) 
              microCache.set(key, res.data)
            
            resolve(res.data)
          ).catch(error => 
            reject(error)
          )
        )
      ,

 

  

结论

我们是否真正的需要做同构渲染,这取决因素有很多,主要有,SEO,首屏时间比较重要的产品可考虑。

运用该技术并没有想象中的轻松,对前端开发有一定的要求,编写过程要谨慎,涉及构建设置和部署的更多要求,还需要更多的服务器端负载,总之,我们可能需要的支持会更多一点。

代码呈现

 Demo已上传至github,以供参考,如若对您有帮助,给个小星星还是很开心的,传送门

 

react前后端如何同构,防止重复渲染

...后端同构、React首屏渲染的概念。然后通过这2个概念解决服务端渲染完成后浏览器端重复渲染的问题。什么叫前后端同构?为了解决某些问题(比如SEO、提升渲染速度等)react提供了2个方法在服务端生成一个HTML文本格式的字符... 查看详情

第三十四篇vue(代码片段)

总览什么是SSRSSR-服务端渲染Vue.js是一个用于构建客户端应用的框架。默认情况下,Vue组件的职责是在浏览器中生成和操作DOMVue也支持将组件在服务端直接渲染成HTML字符串,作为服务端响应返回给浏览器,最后在浏览器端将静态... 查看详情

使用vue2.0实现服务端渲染的hackernews

  Vue2.0支持服务端渲染(SSR),并且是流式的,可以做组件级的缓存,这使得极速渲染成为可能。同时,和2.0也都能够配合SSR提供同构路由和客户端statehydration。vue-hackernews-2.0是Vue作者在GitHub上面放的Vue2.0的一个示例项目,结合Ex... 查看详情

浅谈react前后端同构防止重复渲染

...些问题(比如SEO、提升渲染速度等)react提供了2个方法在服务端生成一个HTML文本格式的字符串。在得到了这个HTML格式的字符串之后,通常会将其组装成一个页面直接返回给用户的浏览器。到这里,服务端的活已经干完了,然后... 查看详情

React JS 同构渲染

...果我在浏览器中关闭javascript,则只会呈现静态代码。在服务器端渲染期间,ComponentWillMount和render方法被启动,但不是ComponentDidMount。即 查看详情

基于vue的nuxt框架cnode社区服务端渲染(代码片段)

nuxt-cnode基于vue的nuxt框架仿的cnode社区服务端渲染,主要是为了seo优化以及首屏加载速度线上地址http://nuxt-cnode.foreversnsd.cngithub地址https://github.com/Kim09AI/nu...技术栈vuevue-routervuexnuxtaxiossimplemdeES6/7stylus目录结构├─npm-shrinkwrap 查看详情

nuxtjs实现服务端渲染和静态化站点(代码片段)

...Nuxt.js是一个基于Vue的通用框架,主要用于解决Vue项目的服务端渲染(SSR)。它本质上是一个Vue框架,增加一层node服务,通过对客户端/服务端的抽象封装,使用Nuxt各种资源配置,处理服务端渲染。除了服务端渲染以外,Nuxtjs还提供... 查看详情

React Relay 和服务器端渲染

】ReactRelay和服务器端渲染【英文标题】:ReactRelayandserversiderendering【发布时间】:2016-10-1211:05:19【问题描述】:我一直在开发一个使用react构建的同构应用程序,并且我已经阅读了有关使用Relay.js和GraphQL进行客户端-服务器交互的... 查看详情

nuxt(代码片段)

...用框架SPA缺点不利于SEO首屏渲染时间长怎么解决呢?服务端渲染SSR-服务端渲染(SSR)SSR缺点动态渲染配置繁琐预渲染Prerendering预渲染:构建阶段生成匹配预渲染路径的html文件(注意:每个需要预渲染的路由都... 查看详情

vue服务端渲染和vue浏览器端渲染的性能对比

Vue2.0开始支持服务端渲染的功能,所以本文章也是基于vue2.0以上版本。网上对于服务端渲染的资料还是比较少,最经典的莫过于Vue作者尤雨溪大神的vue-hacker-news。本人在公司做Vue项目的时候,一直苦于产品、客户对首屏加载要求... 查看详情

javascriptinterceptors.jsaxios服务端渲染(代码片段)

查看详情

实例pk(vue服务端渲染vsvue浏览器端渲染)

Vue2.0开始支持服务端渲染的功能,所以本文章也是基于vue2.0以上版本。网上对于服务端渲染的资料还是比较少,最经典的莫过于Vue作者尤雨溪大神的vue-hacker-news。本人在公司做Vue项目的时候,一直苦于产品、客户对首屏加载要求... 查看详情

vue 服务器端渲染和数据填充

】vue服务器端渲染和数据填充【英文标题】:vueserversiderenderinganddatapopulation【发布时间】:2019-03-2710:40:55【问题描述】:我目前正在重构一个应用程序并将我的所有基本代码转换为vue。我的要求之一是进行服务器端渲染。我一直... 查看详情

使用react的static方法实现同构以及同构的常见问题(代码片段)

...内容。我会定时更新。也欢迎您star,issue,共同进步1.我们服务端渲染数据从何而来1.1怎样写出同构的组件服务端生成HTML结构有时候并不完好。有时候不借助js是不行的。比方当我们的组件须要轮询服务器的数据接口,实现数据与... 查看详情

rubyract的react服务器端渲染(代码片段)

查看详情

vue的两种服务器端渲染方案

作者:京东零售姜欣关于服务器端渲染方案,之前只接触了基于react的Next.js,最近业务开发vue用的比较多,所以调研了一下vue的服务器端渲染方案。首先:长文预警,下文包括了两种方案的实践,没有耐心的小伙伴可以直接跳到... 查看详情

vue服务端渲染提取css

vue服务端渲染,提取css单独打包的好处就不说了,在这里主要说的是抽取css的方法要从 *.vue 文件中提取CSS,可以使用 vue-loader 的 extractCSS 选项(需要 vue-loader12.0.0+)//webpack.config.jsconstExtractTextPlugin=requir... 查看详情

如何使用vue2做服务端渲染

...了一个月时间,终于在新养车之家项目中成功部署了vue2服务端渲染(SSR),并且使用上了Vuex负责状态管理,首屏加载时间从之前4G网络下的1000ms,提升到了现在500-700ms之间,SSR的优势有很多,现在让我来跟你细细道来。技术栈... 查看详情