现在有一个报告页面,需要前端直接将报告内容下载为一个html文件,并且可以脱机打开,样式与页面保持一致。
1.首先,单独写一个.css文件,让页面与下载的html文件都使用的思路肯定是可行的;但是我的页面使用VUE+less开发,同时有大量的公共样式,剥离出一个.CSS文件的难度极大。
2.一个页面无非由 .html .css .js三部分构成;我们只需要将这个页面用到的三部分都下载下来,并保持正确的路径与依赖关系,就能够将一个页面完整复刻。
对于笔者来说,方案2可行性更高且通用性更高;当然,笔者的需求只需要下载一个可以观看的报告,对与JS文件没有需求,所以实现代码会跳过.JS文件。不过有需要的话可以以相同思路实现即可。
downloadHtml(){
//初始化css
let css = '';
//笔者因为页面使用rem为单位,所以需要一个初始化html元素font-size的方法;因为这个方法没有单独抽离文件且较为简单,笔者选择在下载时手动添加;
let js = `
function setFontSize() {
const designWidth = 1920; //设计稿的宽度,根据实际项目调整
const fontSize = document.documentElement.clientWidth / designWidth * 100;
document.querySelector('html').style.fontSize = fontSize + 'px';
}
function debounce(func, wait) {
let timeout;
return function() {
const context = this;
const args = arguments;
clearTimeout(timeout);
timeout = setTimeout(() => {
func.apply(context, args);
}, wait);
};
}
window.onresize = function () {
setFontSize()
};
setFontSize();`;
//直接写入script这样的字符串会有问题,将它从中间拆分再拼接可以避免
//将要执行的js通过<script>标签包裹,准备插入html,如果需要下载页面的js也可以使用该思路;
js = '<scr' + 'ipt>' + js + '</scr' + 'ipt>';
//获取该页面用到的.css静态资源地址
const stylesheets = Array.from(document.getElementsByTagName('link'))
.filter((link) => link.rel === 'stylesheet')
.map((link) => link.href);
//下载所有的.css静态资源
Promise.all(
stylesheets.map((href) => {
return fetch(href).then((response) => response.text());
})
).then((cssTexts) => {
//当.css静态资源下载完成,将其通过<style>标签包裹的方式插入html文件
css += cssTexts.join('\n');
css = '<style>' + css + '</style>';
// 获取Vue实例的HTML内容
const html = document.getElementById('htmlDetail').outerHTML;
const blob = new Blob([html, '\n', css, '\n', js], {
type: 'text/html'
});
// 创建一个a标签用于下载
const a = document.createElement('a');
a.href = URL.createObjectURL(blob);
a.download = 'vue-element.html';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
});
}
因为.css存在优先级问题,写在后面的样式会覆盖前面的样式,所以下载静态资源是最好是保证先后关系;笔者使用了Promise.all的方法一次性下载所有.CSS静态资源,并不能保证样式插入的先后顺序与原页面相同;