摘自:CSS Secrets
“If a color stop has a position that is less than the specied position of any color stop before it in the list, set its position to be equal to the largest speci ed position of any color stop before it.”
— CSS Images Level 3 (http://w3.org/TR/css3-images)
根据当前页面内容布局,把当前内容切换为骨架容器,生成对应的html。
👉🏻 直冲 代码实现demo or
👉🏻 继续查看以下分析文 👇🏻👇🏻👇🏻
前言
本文涉及到主要插件有 :
Puppeteer
vue-server-renderer
rollup
page-skeleton-webpack-plugin
vue-skeleton-webpack-plugin
CSR:客户端渲染。
📢📢📢:
本文涉及代码基于
page-skeleton-webpack-plugin
为前提所简化。 本文目的是:让有兴趣的读者能更快了解内部实现。 实现目的是:达到页面的简单骨架屏时无需再写额外的代码。构建思路基于 page-skeleton-webpack-plugin 和 vue-skeleton-webpack-plugin 两个框架所做的学习简化版。
如有意可以前往观看其源码。
大致目录
生成流程
骨架屏生成的流程如下:
最终效果
把当前布局的页面转化为对应布局的骨架屏: (页面一:)
(页面二:)
快速开始
该章节用于快速了解和尝试运行。
确保8080端口和8082端口闲置,否则需要改代码端口。 访问localhost:8082,使用开发者模式下Network 网络slow3G,刷新页面即可看到被渲染的骨架屏。 核心文件夹:
下面是该程序的分析与实现讨论。
使用场景
本章节介绍骨架屏使用的三个场景分析和实现。
首屏
对于首屏实现骨架屏,大致就是替换index.html页面的内容,在请求html的时候第一时间会渲染出骨架屏内容。
首屏生成大致流程:
生成页面对应的骨架屏,需要获取到这些页面的Dom结构,但在生成页面之前先偷偷获取并生成骨架屏。这里使用到Puppeteer(Headless Chrome Node.js API,模拟 Chrome 浏览器的运行)插件。
Puppeteer
👉🏻 Puppeteer知识点击就送
简单说一下 Puppeteer 执行与骨架屏生成大致流程:
Puppeteer需要能访问到的链接,也就是说,在生成骨架屏之前要先启动服务,例如本地开发访问本地localhost:8080/index.html,那么就得先起8080的服务。
👉🏻 点击查看 - 生成骨架屏主文件入口
👉🏻 点击查看 - 通过Puppteer生成Html并转化为骨架代码
在转换过程中,涉及到 Puppteer 里需要把执行的页面 Js 注入到页面中:
this.scriptContent 就是需要注入的js自运行代码,代码的地址为 skeletonjs/script/index.js,而这个文件是由下面目录通过 rollup 打包合成的一份文件:
page-skeleton-webpack-plugin是通过socket传动另一个端口生成骨架屏页面预览,这里把socket和生成页面预览去除,单纯用node跑生成骨架页面程序服务。
(也尝试像page-skelrton那样做成个plugin,在生成端口地址之后运行puppeteer。按预期走非常不错,但是有一个致命的问题,就是每次热跟新的时候都会执行这个周期,那就导致每次都运行puppeteer生成骨架,可以是可以但没必要,所以干脆单独成为一个node服务[/dog]。)
去缓存刷新,根据不同的页面地址展示不同骨架屏
很遗憾,在实现这个功能的时候发现
page-skeleton
目前只能生成首页的骨架屏,无法满足这个条件… 正苦恼的时候发现另一个骨架屏插件vue-skeleton-webpack-plugin
。vue-skeleton-webpack-plugin
简略vue-skeleton大致原理:
这个插件里了解到通过skeleton 的Vue实例构建服务端webpack渲染对象,把生成的skeleton页面注入到index.html里。
由于这个插件需要手动建立的是一个vue页面实例,那么页面内容是固定自然不适合于我们。但把替换处理交给在服务端渲染之后再返回的方法确实给了很好的思路。这时候我们把思路转到服务端渲染。
vue-server-renderer
这里尝试的是vue-server-renderer,因为nuxt要改代码所以先鸽一边。
我们快速了解下
vue-server-renderer
大致原理:说白了就是把原来访问的index.html,改为通过跑一个node应用监听这个端口,当用户访问这个端口的时候生成一个渲染好的html再返回内容。
这里有个问题,就是服务端渲染返回的是已经渲染好的html页面,那么再放上个骨架屏就有点画蛇添足的感觉了,不过先想实现这个功能那就忽略这个细节 T.T
续着vue-skeleton的服务端渲染想法,我可以把渲染的部分去掉,只是做服务端监听,不使用渲染。
直接浏览器请求的链接路由返回处理好index.html的spa应用方式。那么获取到的html由于要加载js生成vue实例,又会先渲染显示骨架屏,而且可以根据不同路径生成对应的骨架屏。
hhh, 真就脱裤放屁了…
这里注意一点。由于在服务端生成页面并解析成骨架屏会消耗大量时间,那么我们需要在打包的时候先有已经根据路由列表生成骨架屏的shell文件夹。
之后把生成的文件放在public文件夹打包即可。所以这里推荐的做法是在开发的时候就生成好对应文件。
组件局部loading
这部分只有一个思路,尚未实现。观察到antd vue的骨架屏是通过, 标签实现。
那么思路就是使用标签包裹。
通过获取包含内容的数据结构,解构生成骨架屏,在数据请求成功之后隐藏起来。
元素处理
本章节介绍骨架屏根据页面内容生成方案,很大程度依赖的是已经被渲染过的页面,居于这个页面上进行筛选元素。
筛选元素
元素解析
背景色
👉🏻 点击查看 - 背景色转换代码
svgs、buttons、image、inputs
👉🏻 点击查看 - svgs、button、image、inputs转换代码
以上元素不能在里面进行添加其他元素的操作,所以需要用其他元素今天替换,同时避免其他资源的加载。
伪类
👉🏻 点击查看 - 伪类转换代码 伪类的使用场景比较多而且复杂,在变换骨架的时候会较为有难度,但使用伪类多为脱离文档流,故想在生成骨架屏的时候选择屏蔽伪类。
字体处理
👉🏻 点击查看 - 字体转换代码
下面简述如何处理文本和字体条纹颜色转换。
在判断文本行数时,会先获取文字填充到新建的内联元素,并继承fontsize,获取到的内联元素width和height就是字体的宽和高,再对比原元素的宽高即可知道是否为多行文本。
对单行文本进行颜色填充:
我们可以根据文本块的lineHeight和fontsize得到文本距离块上下的距离,设置linear-gradient
loading
👉🏻 点击查看 - loading代码 👉🏻 点击查看 - loading动画代码
translate(-${{ 'left': 0, 'center': '50%', 'right':calc(100% - ${width})}[textAlign]}, 0)
c. top: 字体顶部离行高的距离比 d. left:{ 'left': 0, 'center': '50%', 'right':calc(100% - ${width})}[textAlign]
参考
ElemeFE/page-skeleton-webpack-plugin lavas-project/vue-skeleton-webpack-plugin https://github.com/Jocs/jocs.github.io/issues/22 - Connect to preview 第40题(2019-09-16):如何实现骨架屏,说说你的思路 · Issue #42 · qappleh/Interview 饿了么的 PWA 升级实践