-
Notifications
You must be signed in to change notification settings - Fork 0
Description
对于我个人而言,平时很少使用 SSR ,当然了解新技术肯定是在之前学习过一点 SSR 相关,也自己粗略的玩过 Nuxt.js , Next.js,但是都是属于纸上得来终觉浅的阶段吧,有点不知东西的感觉 23333,最近公司有相关项目,所以趁着这个机会边踩坑,边学习 SSR,顺路去看了 vue-server-render,虽然还没完全看完,但是感觉到了已经可以记录的程度了。
Why SSR ?
随着时代的发展,我们从纯服务器端渲染来到了,前后端完全分离的客户端渲染,到了现在较多的使用 SSR。作为见证者,谈谈我的经历吧,在两年前,我在搭建校科协官网的时候,当时其实是有使用 SSR (魔改难看版)的,技术栈是 Thinkphp , Vue,jQuery,当时使用的情形是,后端同学和我都是初出茅庐的小二郎,后端同学不是很想写读取用户数据的接口,于是乎,我们用了Thinkphp的模板字符串去将用户数据渲染到 js 代码的某个函数里,前端再通过这个函数去 return 出来用户数据,其他部分的数据还是照常,用 ajax 去请求。啊,回想起来,年轻的我好像,不小心触碰了什么不得了的东西,将服务器端渲染和客户端渲染结合了起来。
一个技术流行,必然有它的原因,用 SSR 好处有:
- SEO 优化,更方便被搜索引擎爬取到网站的有效信息
- SSR 可以 减少 客户端请求的资源消耗
- 减少首屏渲染时间
那么当年为什么放弃纯服务器端渲染呢?
- 当年没有实现前后端分离,现在这部分工作完全交给前端来完成
- 完全依赖服务器端渲染会过多浪费服务器端的资源,一定程度上反而影响渲染效果
What I met ?
目前在使用过程中我遇到的问题主要是:
- 滚动条的默认行为
- 资源加载的不确定性
问题一:滚动条的默认行为
情景重现 :
需求:列表第一页用服务器端渲染,后面上拉加载的更多页通过 http 接口请求。啪啪啪啪(诡异的声音),我一顿操作完成了需求,心想,诶这么简单的任务(大话说的太早),满意的提交之后,可爱的测试小姐姐三下五除二就给我扔了个 Bug:上拉加载更多页后刷新,视窗在第一页和第二页数据之间。理想情况:刷新后视窗显示在第一页,不会发送请求去 get 第二页的数据。
分析问题:经过复现,找到了根本原因,滚动条默认会记录刷新前的滚动距离,并且在页面 load 之后恢复到刷新前的地方,从而中间存在一个滚动条的滚动事件,并且滚动条在发生滚动的时候由于记录的滚动距离远大于页面高度,所以会滚到页面底部,同时发送请求。
解决办法一:
在mounted 钩子(我们使用的是 Vue)中延时 300ms 监听滚动事件,从而保证了页面刷新 load ,滚动条到页面底部之后不会触发 http 请求,代码如下:
mounted () {
setTimeout(() => {
window.addEventListener('scroll', this.listenScroll);
}, 300);
}效果:刷新后页面没有再进行 http 请求,并且滚动条在第一页的底部。
这样的效果真的让人满意吗?
显然不是的,因为按照需求来说之所以刷新后只显示第一页的数据,就是为了保证和初始进入页面达到一样的视觉效果,而不是视窗在第一页的底部,而且这种方案中的延时 300ms 并不精准。
解决方法二:
在页面离开时,触发 unLoad 事件,将滚动条移动到页面顶部,代码如下:
beforeDestory () {
window.addEventListener('unLoad', () => {
window.scrollTo(0, 0)
})
}写完这段代码,我感觉思路很对23333,强行在组件被销毁之前,页面卸载的时候将滚动条重置,然而,实际效果并不成功,经过我几番折腾,首先确定了一点,要去监听页面刷新事件,其次为什么在beforeDestory 钩子中的事件监听没有生效。
刷新事件对于浏览器而言真正触发的事件顺序为: beforeunload -> unload -> load,beforeunload 的触发条件为页面即将前往下一个页面的时候,如果只是监听 unLoad 事件,此时页面已经被卸载,改动 滚动条位置毫无意义。
对于beforeDestory 钩子,在vue-server-render文档中提到了
重点来了,在服务器端渲染的时候并不会触发 mount 相关钩子以及 destroy 相关钩子,所以带来的问题就是并没有真正监听到。
经过修改之后的解决方案是:
mounted() {
window.addEventListener('beforeunload', () => {
window.scrollTo(0, 0)
})
}在组件创建的时候就开始监听 beforeunload 事件,从而将滚动条重置。
总结:所以在做同构项目的时候要时刻记住浏览器端的逻辑要放在组件渲染后的生命周期中,node 端的逻辑要放在组件还只是虚拟 dom 时的地方,顺便推一手我司大手子对外开源的 ssr 框架 billund ,它在里面做了默认降级处理,也就是无需注意生命周期的问题,开发感觉无限接近于框架原生开发(好生硬的广告)。
问题二:资源加载的不确定性
情景重现 :
需求:一个通用的 NavBar 组件,有一个返回按钮,需要一个“箭头”图片。
自测产生的 Bug:部分页面图片被正常加载,并被解析成 base64 链接,部分页面图片加载失败,图片路径为"[object Object]",但是我在mounted钩子中打印图片路径,可以发现是正常的 base64 字符串,type 是 string ,这就比较棘手了,因为是偶发状况。
问题分析:从根本的地方思考,通过 Webpack 打包代码,一般使用 url-loader 去处理图片加载,这部分的渲染是在服务器端进行的。经过分析后在 vue-server-render官方文档找到了答案。
解决方案:在mounted阶段是正常处理的,其原因就是钩子是在浏览器端完成的,所以能够正常加载图片,生成正确的图片链接,那么代码如下:
mounted() {
this.backImg = require('./img/back.png');
}测试之后一切顺利,解决了问题。

