因为自己最近在做骨架屏生成脚本,也参考了业界的一些方案,总结并优化了一下,并加入到项目开发流程中。 接下来先给一张图,让实现思路清晰明了。
1、打开url页面
对于无头浏览器打开url页面有两种方案:
- 直接安装puppeteer包,因其内置自带谷歌浏览器,体积大。
- 安装puppeteer-core核心包,无内置浏览器,需求手动输入浏览器应用位置。
这里根据自己的业务去适用方案,比如是后台管理系统,可以使用第一种比较简单方便。 h5建议采用第二种,但前提是有安装谷歌浏览器,相信大部分开发人员已经装了, 对于需要手动输入浏览器位置的问题我们使用carlo包的findChrome去自动寻找帮我们解决。
2、生成骨架屏代码
那么首先我们要知道生成规则,什么是生成规则呢,简单来讲就是要依据页面哪些元素去生成骨架屏样式盒子。
比如根据某些元素标签例如img, video等之类的比较特殊的标签。
编写js生成脚本,在页面的控制台下运行,根据自己所配置的规则去生成样式代码,使用puppeteer中page实例的,evaluate 方法。贴一下puppeteer文档链接 puppeteer.bootcss.com。
根据适合项目首页的规则去生成适合我们项目的骨架屏代码,比如页面的上文本元素、媒体元素等,或者一些特殊的元素自定义处理等,提供配置化的方式给使用者去灵活使用。
详细的代码处理过程就不一一赘述了,大概的处理流程如下:
-
遍历页面所有元素需要生成骨架屏代码的元素进行处理
-
获取元素位置,大小信息
-
判断元素是否处于可视区域内(超出屏幕?被遮挡?样式性隐藏?)
-
根据元素信息生成元素及样式代码
3、代码注入html
将样式代码注入到index.html:这里一开始考虑用的是html-webpack-plugin的hooks去实现,但没实践出来,目前用的是cheerio,后续实践出来再做更新。
有了骨架屏代码后,我们就可以将代码注入到html里啦,这里我们使用cheerio包来操作html文件,将生成的骨架屏代码注入到html对应的元素下面,如vue项目中html下的#app元素。
4、包装成plugin
我们将骨架屏生成脚本包装成webpack plugin,在webpack.dev.config中使用,建议输入url参数时,带上页面所需地址参数,项目启动后可利用开启的服务去异步生成骨架屏代码并注入index.html,可正常访问项目,无须等待。
5、伪代码实现
咱们来简单的写下伪代码把主要流程给串起来:
// 首先打开URL页面
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto(url);
// 此时我们就拿到了url的页面实例,调用evaluate执行我们的js脚本
const html = await page.evaluate(handle, options);
// handle为处理页面的js方法(下面介绍),this指向window,返回生成的骨架屏html代码。
const fs = require('fs');
const cheerio = require('cheerio');
const fileHTML = fs.readFileSync('./index.html');
const $ = cheerio.load(fileHTML);
// 将html插入
$(#app).html(html);
// 写回index.html
fs.writeFileSync(this.filepath, $.html());
// handle
function handle() {
...遍历document.body下的元素
...拿到node.tagName === 'IMG' 标签元素
...通过node.getBoundingClientRect()获取坐标位置以及大小信息
...判断元素是否在可视范围
...生成对应元素及样式代码
return `
<style>
.sk_box {
position: absolute;
background-color: #EEEEEE;
}
</style>
<div class="sk_box" style="width: 100px;height: 100px;top: 100px;left: 100px;"></div>
`;
}
大概的流程代码就是这样啦,当然实际实现的话那就要复杂的多了,还需要考虑配置化的问题,给使用者能自定义一些配置,毕竟有的页面元素布局比较特殊。
把上面代码包成一个方法后带上配置参数,就可以提供给外部使用啦,顺便贴下webpackplugin代码:
const Skeleton = require('./index');
const once = (fn) => {
let called = false;
return () => {
if (called) return;
called = true;
fn();
};
};
class skeletonPlugin {
constructor(options = {}) {
this.options = options;
}
apply(compiler) {
const that = this;
compiler.hooks.done.tap('skeletonPlugin', once(() => {
setTimeout(() => {
new Skeleton(that.options).start();
});
}));
}
}
module.exports = skeletonPlugin;
webpack.dev使用
new SkeletonPlugin({ url: 'http://localhost:8080' }),
plugin是在开发模式下去使用,启动项目的时候去自动生成骨架屏,但是理想情况下是cicd的时候在打包后去自动生成。不过前提是服务器安装了谷歌浏览器,但是卡在了linux服务器打不开无头浏览器puppeteer,貌似版本不太一样; T_T