You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
也就是说,我们想要看到具体的视图,需要等待react or vue执行了挂载到app节点的操作才可以,那么从url输入到执行app挂载这个阶段都是白屏时间;这种场景还是路由没有进行懒加载的情况,如果路由进行了懒加载,那么执行app挂载的时候,因为没有对应的组件进行渲染,我们看到的页面实质上还是空白页面,我们可以通过chrome浏览器的performance来看下整个页面渲染的过程,如下图所示,图中这个例子采用的是react+react-router路由懒加载的方式,所以导致白屏时间相对长
前言
目前spa大行其道的时候,首屏白屏问题越来越受到关注,那么目前行业内针对首屏白屏的问题有了多种解决方案,比如ssr、预渲染等
这里要说的就是预渲染,因为相对于ssr,前端项目接入预渲染的成本相对低一些,那么我们先来看下什么是预渲染
在说预渲染之前我们需要先了解客户端渲染
访问url -> 服务端返回index.html -> 浏览器解析index.html -> 浏览器加载静态js、css文件 -> 生成renderTree -> 呈现页面 -> 执行前端框架react or vue的代码构建dom树 -> 将dom树挂载到app节点上 -> 呈现页面
那么我们看下一般的spa项目服务端返回的index.html,如下所示
也就是说,我们想要看到具体的视图,需要等待react or vue执行了挂载到app节点的操作才可以,那么从url输入到执行app挂载这个阶段都是白屏时间;这种场景还是路由没有进行懒加载的情况,如果路由进行了懒加载,那么执行app挂载的时候,因为没有对应的组件进行渲染,我们看到的页面实质上还是空白页面,我们可以通过chrome浏览器的performance来看下整个页面渲染的过程,如下图所示,图中这个例子采用的是react+react-router路由懒加载的方式,所以导致白屏时间相对长
那么导致长时间白屏的原因主要如下
那么预渲染又是什么呢?
上面我已经知道了白屏时间主要由两部分组成,理论白屏时间+实际白屏时间;理论白屏时间需要我们通过网络及服务器来进行相关的优化,并且这个优化一般也很难有提升;那么针对这个实际白屏时间,我们是不是有优化空间呢?是的,这一部分有很大的优化空间,如果我们直接返回的index.html内就包含了首页需要渲染的dom结构,那么是不是当index.html加载完成之后,我们就可以直接看到返回的dom结构呢?我们还是通过chrome浏览器的performance来看下整个页面渲染的过程
我们从图上可以看出来,实际白屏时间已经几乎没有了,这种先将对应的dom结构提前生成到index.html内的方式就叫预渲染
那么我们在实际项目中怎么去做预渲染,我们不可能动过手动的方式去把dom结构写到到index.html内去,那我们实现自动插入的思路是怎样的呢?
以webpack插件为例
通过谷歌发布的无头浏览器库puppeteer启动一个无头浏览器 -> 打开指定路由的页面(可以是本地,也可以是线上url)-> 通过无头浏览器提供的api拿到某个时间节点对应的dom(需要对数据脱敏,或者做些修正处理) -> 通过处理html的插件,将生成的dom节点插入到对应的index.html内去
这样我们的预渲染dom结构就可以自动插入到对应的index.html内去了
这种预渲染又带来什么问题呢?
针对动态展示数据的页面不好处理什么意思,也就是当我们的页面是通过接口返回的数据做不同的渲染,那么我们则无法通过预渲染直接拿某个状态的dom结构了,因为不同的用户看到的内容可能不一至;如下所示
不同的用户看到的列表内容可能是不一样的
首页预渲染出来的dom,不适配其它路由页面什么意思,也就是说,我们只根据我们的首页的来生成对应的dom结构,但是我们切换到其它路由,然后重新刷新的时候,因为预渲染出来的dom还是首页的dom,但是当前的路径已经不是首页路径的话,那么这里页面会有一个严重的页面过度问题;怎么解决,有两个思路
第一个思路:默认插入的预渲染dom display:none;然后在预渲染dom的下面插入一段script脚本,判断当前path是首页的时候才展示预渲染的dom,如果不是则还是不展示;这样子的话其实相当于做了部分场景下的首屏白屏优化
第二个思路:每个页面都生成一份对应有预渲染dom的html,除了预渲染dom不一样,其它引入的js、css都一样,如下所示
然后我们需要修改我们的nginx配置,当匹配到不同的路径时,要返回对应的那个html文件,这样就可以保证每个路由页面单独刷新的时候,不会展示其它页面的预渲染dom;但是这种方式成本过高
看下常用的用来生成预渲染页面的插件
更多使用方式直接参考文档
除了上面预渲染思路还有没有其它的预渲染思路,有骨架屏
通过上面已经知道,有些动态数据的场景不方便直接生成对应的预渲染dom,那么我可不可使用一种更抽象一点的方式来表示预渲染dom呢?答案就是骨架屏
我们通过一些灰色的占位div,大致描述出一个页面的内容,然后用来过度展示,这样即不会看到的是白屏,也不用担心数据问题,如下所示
骨架屏的实现思路
1、通过谷歌发布的无头浏览器库puppeteer启动一个无头浏览器 -> 打开指定路由的页面(可以是本地,也可以是线上url)-> 通过无头浏览器提供的api拿到某个时间节点对应的dom -> 去掉dom节点的内容及背景色等等,维持原本的dom结构 -> 通过处理html的插件,将生成的dom节点插入到对应的index.html内去
2、通过谷歌发布的无头浏览器库puppeteer启动一个无头浏览器 -> 打开指定路由的页面(可以是本地,也可以是线上url)-> 通过无头浏览器提供的api拿到某个时间节点对应的dom -> 遍历所有的dom,使用定位的方式重新组织dom结构 -> 通过处理html的插件,将生成的dom节点插入到对应的index.html内去
当然骨架屏的使用也会碰到首页预渲染出来的dom,不适配其它路由页面的问题,这个需要我们在实际项目中灵活对待
常用的生成骨架屏的插件
饿了么的骨架屏插件page-skeleton-webpack-plugin
更多使用方式参数文档
auto-skeleton-cli
最后如果即不想使用预渲染的dom也不想要骨架屏,同时不想白屏时间过长,那么也可以直接使用一些loading或者动态的logo之类的gif图or svg来作为预渲染的内容,如下所示
总结
我们首先要知道白屏时间主要由两部分组成理论白屏实际+实际白屏时间,我们可以通过预渲染的方式优化我们的实际白屏时间;需要注意的是如果我们的路由不是通过懒加载的方式进行加载的,我们的预渲染的dom结构可以直接生成到id=app的节点内,等待框架自动挂载替换app节点内的内容;如果我们的路由是通过懒加载的方式实现的,有两个思路,第一个预渲染的dom还是放到id=app内的节点内,但是这里还要处理子路由对应的js及框架render的这个时间;第二个思路就是将预渲染的dom放到id=app同级的一个div内,然后在路由加载完成之后在隐藏这个div
The text was updated successfully, but these errors were encountered: