引入骨架屏生成工具实践

1,842 阅读4分钟

前言

最近又在搞优化(永远绕不开的优化。。。),在项目中尝试引入了骨架屏,加载体验还是比较好的,就搞了个简单的脚本,支持当前工程模版自动替换,加速开发效率。

骨架屏生成工具使用了 awesome-skeleton ,很强很赞!个人结合项目和使用做了修改和扩展。

其原理是利用 Puppeteer (Chrome 推出的无界面浏览器,一个 node 库)打开页面,注入一个 script 将页面 DOM 元素进行背景灰化、去除文字等处理,最后截屏得到 png 图片及其 base64 。

问题 & 解决

1. 背景色处理

在起初尝试的时候发现生成的骨架屏在商品列表里的红色按钮依旧是红色的,没有做灰化处理。这样看起来相当突兀:

突兀

查看代码发现对于 div 这样的容器元素,并没有修改它的背景色。我感到疑惑,经过一顿调试分析,发现一个结论:

  • 对于图片、文本这样的元素我们可以认为它们是内容元素,对其进行灰色处理;
  • 而对于容器元素,在对节点树递归处理时无法知道该元素是作为一个容器(白色)还是一个内容(灰色),所以未对其进行处理,仍保持原来的背景色。

很显然这样会出现不符合预期的骨架屏,不过工具提供了 DOM 节点属性来指定该元素要处理成什么颜色:

<div data-skeleton-bgcolor="#EE00EE"></div>

不过我觉得骨架屏应简洁大方,白色和灰色两种颜色足矣,所以拓展了两个节点属性,来指定元素为容器或是内容:

<div data-skeleton-container>该元素会被处理为白色,作为容器</div>
<div data-skeleton-content>该元素会被处理为灰色,作为内容块</div>

由此,在上边的按钮元素加上 data-skeleton-content 属性即可:

正常

2. 线上故障

将几个频道页引入骨架屏之后,感觉效果不错,开开心心上线了。没想到几分钟后报错邮件和短信提醒开始狂轰乱炸。。。惊!我的代码怎么可能有 bug ?看了下报错:Array.from is not a function ,原来是这个工具生成的 html 片段里的 script 会在 window 上挂一个销毁骨架屏的方法,而这个方法用 Array.from 将 NodeList 转化为数组遍历并删除。

可是这个 html 片段是压缩过的,我没有仔细看~事实证明我们仍不能随意用 ES6 的 API 。。。

解决:Array.from -> [].slice.call

3. 自动销毁

工具生成的 html 片段默认会在 script 里监听 load 事件,自动销毁骨架屏。实际体验来看,在骨架屏消失后页面并未渲染 OK ,所以还是推荐首屏数据拿到后手动销毁。

故增加了一个配置 autoDestroy: false ,默认不自动销毁。

4. 移除元素

在开发环境,页面引入了一个第三方工具 vConsole ,这个东西会在 body 后面插入一个元素,如图右下角。而工具是以 body 为根节点开始处理,自然无法处理这个元素。

移除示例

解决:增加配置项 removeIds ,将 vConsole 元素 id 放入 removeIds 数组,在处理时找到这些元素并移除。

5. 视图模版更新

工具的输出是一个 html 片段,通过定义一个模板用注释作为 placeholder 来生成新的模板即可:

<body>

<!-- SKELETON -->

<div id="app"></div>
<script type="text/javascript" src="xxx"></script>
</body>

// 替换注释
const targetTmpl = tmpl.replace('<!-- SKELETON -->', res.minHtml);
// 写入
fs.writeFile(targetPath, targetTmpl, (e) => {
    if (e) throw e;
    loading.succeed();
    success(`模版已更新:${path.relative(projectRootPath, targetPath)}`);
});

6. 构建部署问题

看起来一切都 OK 了,但是部署的时候构建报错:

ERROR: Failed to download Chromium r515411! Set "PUPPETEER_SKIP_CHROMIUM_DOWNLOAD" env variable to skip download.

puppeteer 会下载 Chromium,由于公司镜像源的问题无法下载,但是 Chromium 我们也只需要在开发环境使用而已,测试和生产环境没必要安装。

解决:在构建脚本中设置环境变量 PUPPETEER_SKIP_CHROMIUM_DOWNLOAD

// build.sh
export PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true

THANKS

再次感谢 awesome-skeleton