前言:前段时间学习了vue的服务端渲染,对一个不知名的项目进行了SSR改造。我遇到了很多陷阱。我写这篇文章是为了纪念它。希望大家以后遇到类似的问题,不再迷茫。!成长的路上,不再悲伤!一、SSR简单介绍SSR,全称server-side-render,即服务端渲染。顾名思义,就是在服务端直接“直接”渲染我们页面的数据,直接在html中填充数据,吐回给前端。其实类似于改革开放时期的jsp渲染,以及后来的node端模板引擎jade、handlerbars等,都是将数据填充到页面中。不同的是,以前我们要在服务端维护一个代码。以往有以下两个缺点:1、不利于前后分离。2、前端修改后,需要同步后台,带来额外的工作量。后来到了21世纪,大家开始使用vue、react等应用框架来开发页面,也就没有了上面说的“直出”,但这毕竟也带来了其他的问题。一种是爬虫爬取页面不再那么方便了,因为拉回来的是一个空的html页面,里面没有任何数据。其次,取数据要等到资源加载完成,框架初始化完成。在要求高的业务场景下,会给用户带来不好的使用体验。所以vue和react都实现了SSR的方案来满足这个需求,让你的框架代码可以在服务端运行,同构直出,方便SEO,也缩短首屏显示时间页面的。服务端渲染和客户端渲染的时序图如下:引用自:https://www.jianshu.com/p/10b...2.学习前的提问在做SSR之前,希望各位读者尝试想一想。如果需要实现一套SSR方案,需要做哪些工作,会遇到哪些问题,比如如何让服务器运行相同的代码?需要考虑哪些细节?笔者个人问题如下:1、SSR方案是否只优化首屏渲染?2、SSR在服务端运行客户端代码。它需要初始化vue框架,生成虚拟DOM,最后输出HTML。这个过程会影响服务器的性能吗?3、客户端代码开发完成后,是否需要在服务端保留一份完整的代码供服务端运行?如果服务端和客户端是两个独立的项目,那岂不是要在服务端安装客户端需要的npm依赖?4、根据每个页面不同的路径,是否要做相应的直出?5、前后端请求必须一致。如果前端请求携带session和后端请求必须一致,则需要解决session的问题。6、后台直接输出HTML后,JS和CSS依赖谁来填??7.后台本质只有HTML字符串,所以前端Vue代码在初始化时,需要接管后台吐出的HTML,重新生成虚拟DOM,接管吐回的store数据从背景。想知道以上问题的答案,最后揭晓吧!三、SSR的原理之前问了那么多问题,是时候进入正题了。从官方提供的示意图可以看出,我们的客户端代码、store、组件等,通过两个入口文件分别打包生成两个bundle,server-bundle和client-bundle,server-bundle负责用于在服务器端运行,生成直接输出的HTML,在前端渲染,而client-bundle将混合成HTML,最后由节点服务器HTML处理。由于您希望您的客户端代码在服务器上运行,因此您需要执行以下操作:1.编写通用代码。服务器生成虚拟dom时,会执行created和beforeCreate函数。当然这里也可以拉取请求渲染数据,但是这里不建议这样做,后面会说明。因此,在初始化时,从入口文件开始的每一步,都必须格外小心,避免访问window、document等只在客户端可用的全局对象。仍然是服务器端避免代码执行报错。2.改造原有的store、router、app。从官网也可以看到我们需要避免单例模式。由于webpack打包的最终模块是以module.exports的形式暴露给其他文件引用的,如果你暴露的是一个对象,这意味着后面的每一次引用都会返回一个对象,只是不同的用户渲染不同的页面。您如何让他们共享相同的应用程序、商店和路由器?显然不合理,所以需要将原来的store、router等改造封装成工厂函数,避免单例模式。(注:router需要改成history模式,让请求去后台,而不是使用默认的hash)3、将组件的请求逻辑抽取出来放到asyncdata函数中,为什么呢?为什么我不在创建时这样做?原因很简单。服务器直接输出页面是一个同步过程。中间渲染到某个组件后,发起异步请求,最后直接输出html。请求没有得到响应,数据也没有填入html。返回给用户,那么它根本就没有起到服务端渲染数据的作用?所以提取asyncdata函数的本质目的是管理请求的统一入口。通过手动触发asyncdata函数并返回promise对象,您的应用程序将知道数据已返回并填充,然后将HTML吐出给用户。从官网的例子就可以看出为什么要传入一个store,而不是通过this.$store来访问,小子,你还是太年轻了,因为那个时候this.$store并没有挂载storeobject,你就可以这样传入函数调用了~不行~再看看上面的代码,通过在entry-server中调用命中路由组件的asyncdata方法,然后解析app去出去。在此之前,将store的state赋值给context的state,目的是为了在输出HTML的时候将store的state序列化,填充到全局的__INITAIL_STATE__变量中。所以对应的,需要在entry-client文件中加入如下代码,来判断是否让vue直接从原来的服务器端接管store数据。说完以上3点,接下来就是修改配置文件和后台路由了:1.配置webpack文件,分别打包后台bundle和前端bundle,两者配置不同,入口文件不同,如前所述,有两个入口文件。2、写一个通用的路由中间件,用于命中非API接口以外的非静态资源和页面接口。在这个中间件中编写你的直出逻辑,包括调用相应的API并传入相应的bundle文件。初始化fetch,让它承载client带来的session。看到这里,是的,这篇文章不是配置教程,如果想看配置教程,就去官网看看吧~4.你可能会遇到的坑在这里,笔者整理了所有在配置过程中遇到的问题访问SSR来解决问题。1.无法读取undefinedglobal.navigator={userAgent:'node',}2.打包后台bundle时,一直报错,ERROR:Server-sidebundleshouldhaveonesingleentryfile。避免在服务器配置中使用CommonsChunkPlugin。但是我没有使用多入口文件?也没有使用CommonChunk吗?最后对比配置文件,发现使用了splitchunk,于是修改代码如下:3、后来发现本地devserver无法运行,页面空白?发生了什么?原来是配置文件有问题。配置好nodeExternal后,本地并没有打包依赖包,前端页面自然是一片空白。4、由于服务端代码和客户端代码分离到两个项目中,分别维护,导致服务端代码找不到依赖。所以要么把服务端和客户端的代码放在一起,要么去掉nodeexternal的配置,打包依赖。5.Headerisnotdefined解决方法:引入node-fetch提供Header接口6.DocumentisNOTDEFINED首先检查你的客户端代码是否在服务端执行,在执行的时候引入一个document对象。其次,cssplugin插件也可能导致webpack打包过程中引入document对象。7.一件事是要保证当你的服务器代码允许的时候,窗口是未定义的,否则vue代码会进入客户端逻辑,调用document方法。因为作者使用的是内部服务器框架,所以引入了window对象,并报错。8.笔者这里也遇到了一个问题。直接输出html时,vue-server-render源码中执行错误undefined。除了分析文件依赖关系的问题外,数组中多了一个undefined。作者手动修改了源码去掉undefined,很粗鲁。9.修改服务器端的fetch。因为客户端发送请求要用fetch,用axios是没用的。为了使请求保持一致,需要添加url前缀和cookie。10.取数据需要放在asyncdata函数中,返回promise对象。其次,存储对象需要注入到函数中。只有在promiseresolve之后才能返回HTML,否则返回一个空页面。11.后来,观察者函数的回调错误()...不要在变异处理程序之外改变vuex存储状态。其实在creatingStore的时候,我在工厂函数外面抽取了一个对象,导致这个对象是共享的,因为模块只会初始化一次,这个对象由于闭包而被保留,被所有用户请求共享,所以要避免这个问题,就要让所有store对象涉及的state,老老实实的放到factory函数里,大家不共享。5.故事大纲至此,你可能对SSR有了更深的思考或理解,或者变得更加迷茫,但没关系。下面我来回答一下之前提出的问题:1、SSR方案是否只优化了首屏的渲染?答:是的。2、SSR在服务端运行客户端代码。它需要初始化vue框架,生成虚拟DOM,最后输出HTML。这个过程会影响服务器的性能吗?答:由于vue代码需要在服务端执行,而node本身是单线程的,所以请求还是会对性能造成影响,所以要好好选择。3、客户端代码开发完成后,是否需要在服务端保留一份完整的代码供服务端运行?如果服务端和客户端是两个独立的项目,那岂不是要在服务端安装客户端需要的npm依赖?答:生成不同的捆绑文件。生成服务端bundle文件时,无需配置nodeExternal,即可将所有依赖包打入。4、根据每个页面不同的路径,是否要做相应的直出?答:可以,需要给路由器设置history模式,这样请求才能到后台。5、前后端请求必须一致。当前端请求携带session和后端请求必须一致时,需要解决session问题。答:是的,你需要重写fetch,在处理请求之前添加urlprefix和session。6、html直接后台输出后,js和css的依赖呢,谁填?答:前端代码打包的时候会生成一个json文件提供给服务端,所以在服务端直接导出的时候会填写相应的依赖。7.后台本质只有HTML字符串,所以前端Vue代码在初始化时,需要接管后台吐出的HTML,重新生成虚拟DOM,接管吐回的store数据从背景。答:是的。本次讲座到此结束,请大家踊跃发言!欢迎讨论!谢谢!温馨提示:评论区将抽取幸运儿!奖励价值不超过1元的红包!
