iframe渲染pdf实验
实验一:iframe引入前端静态资源
<iframe src="./1.pdf"></iframe>
无脚手架,直接在html文件中引入iframe
| 使用方式 | 结果 | 结果演示 |
|---|---|---|
| 正常展示pdf |
使用脚手架如vite, 在vue文件中引入iframe
| 使用方式 | 结果 | 结果演示 |
|---|---|---|
| 不能展示pdf |
问题研究
src="./1.pdf"的相对路径找不到文件资源
当你yarn serve启动项目后,src相对路径是相对yarn serve执行的那层目录而非源码所在目录路径;
| 序号 | 解决方案 | 配图 |
|---|---|---|
| 方案一(bad) | 将pdf资源移动到根路径下 | |
| 方案二(bad) | 将路径修改为‘./src/1.pdf’ | |
| 方案三(规范) | 将静态资源放在根目录下的assets文件夹下,使用相对于服务器根路径的路径 | |
| 方案四(可靠) | 使用import.meta.url, 表明以当前文件目录所在位置为相对开始的位置创建一个绝对路径的URL |
找不到文件,为什么递归展示了三层iframe
不太清楚。下面是ChatGPT给我发HTML5规范,大体意思就是iframe设计就是src中指定资源找不到就尝试重新加载iframe所在的同一页面。
If the src attribute on the iframe element is set to the same URL as the document containing the iframe element, the user agent may instead navigate the nested browsing context to the top-level browsing context's active document.
小结
- 静态资源没有被打包编译,也没有被存储在特定的位置。只作为普通文件直接从源码中读取使用。// 除非你额外配置
- import.meta.url 指明当前文件所在目录位置,import.meta是这个模块的元信息
实验二:后端返回pdf文件流,前端iframe渲染
核心源码
前端
<template>
<iframe :src="url"></iframe>
</template>
<script>
import {ref} from 'vue'
export default {
setup() {
let url = ref('');
const getPdf = () => {
const xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
const blob = new Blob([xhr.response], {type: 'application/pdf'});
url.value = URL.createObjectURL(blob);
}
}
xhr.responseType = "blob"; // 不设置,无法正确解析pdf文件
xhr.open("GET", "http://localhost:8000/getPdf1", true);
xhr.send();
}
getPdf();
return {url}
}
}
</script>
后端
const express = require('express');
const app = express();
app.get('/getPdf1', (req, res) => {
res.set('Access-Control-Allow-Origin', '*');
res.sendFile(`${__dirname}/1.pdf`);
})
app.listen(8000, () => {
console.log("服务已启动,8000端口监听中...")
})
其他:隐藏toolbar
toolbar展示场景: 当iframe pdf宽高为100%时,会展示toolbar。
| 隐藏前演示 | 隐藏方式 | 隐藏后演示 |
|---|---|---|
| <iframe :src="url + '#toolbar=0'" ></iframe> |
iframe相关简介
HTML内联框架元素(<iframe>)表示嵌套的browsing context,它能将另一个html页面嵌入当前页面。
browsing context 浏览器上下文
- browsing context是浏览器展示Document的环境。在现代浏览器中,通常是一个标签页(tab),也可能是一个窗体(window)或者页面的一部分(iframe);
- 每个browsing context都有一个活动文档的源(origin)和一个记录所有展示文档的有序历史(history);
- browsing context之间的通讯被严格限制,只有两个同源的浏览器上下文才能打开和使用通讯接口(BroadcastChannel);
BroadcastChannel通讯接口
该接口可以实现同源的不同窗体、tab页、iframe下的不同Document之间相互通讯。通过触发message事件,消息可以广播到所有监听了该事件的BroadcastChannel对象。
// 事件发送者
const channel = new BroadcastChannel('expample1');
function handleClick() {
channel.postMessage('触发事件');
}
// 事件接收者
const channel = new BroadcastChannel('expample1');
channel.addEventListener('message', (event) => {
console.log("接收内容", event.data);
})
iframe废弃属性
- frameborder:设置边框,使用css border代替
- scrolling: 是否在框架内显示滚动条
iframe中样式修改实验
iframe是可替换元素,可替换元素的展示效果不由css控制。就是说它们的内容不受当前文档样式的影响,css可以影响可替换元素的位置(object-fit、object-position),但不会影响可替换元素自身的内容。iframe元素可以有自己的样式表,它们不会继承父文档的样式。
实验目标
修改滚动条样式,保证全局均使用期望样式滚动条
| 默认样式 | 期望样式 |
|---|---|
实验一:修改全局滚动条
| 核心源码 | 演示 |
|---|---|
| ::-webkit-scrollbar { width: 6px; height: 6px; } ::-webkit-scrollbar-thumb { background-color: #bfbfbf; -webkit-border-radius: 6px; } |
实验二:修改iframe中html的滚动条
| 修改前演示 | 核心源码 | 修改后演示 |
|---|---|---|
| const setIframeStyle = () => { const iframe = instance.refs.iframeHtml.contentDocument; const style = document.createElement('style'); iframe.head.appendChild(style); const sheet = style.sheet; sheet.insertRule("::-webkit-scrollbar {width: 6px;height: 6px;}"); sheet.insertRule("::-webkit-scrollbar-thumb {background-color: #bfbfbf;-webkit-border-radius: 6px;}"); } |
问题研究:onMounted及之前触发setIframeStyle方法修改滚动条不生效
iframe加载异步,当父组件onMounted后,iframe还没有加载完成。所以在onMounted中拿不到加载完成的iframe,触发setIframeStyle也不生效。
解决方案:iframe提供onload方法,可以在onload方法中setIframeStyle。
| 实验代码 | 演示 |
|---|---|
| onMounted(() => { // 设置无效,iframe加载完成晚于onMounted // setIframeStyle(); console.log("onMounted"); }); const handleLoad = () => { console.log("iframe加载完成"); setIframeStyle(); } |
实验三:修改iframe中pdf的滚动条
| 核心代码 | 演示 |
|---|---|
| <pdf :src="url" /> import pdf from 'vue-pdf-embed' export default { components: {pdf} } |
问题研究一:如实验二方式修改iframe中全局滚动条样式不生效原因
pdf中滚动条是自带的,不支持修改。
解决方案:考虑使用第三方库,将iframe pdf转换成canvas后,即可使用实验一提供的能力。相较于iframe引入pdf, 第三方库将pdf转换成canvas具有更好的跨浏览器的兼容性,也保证样式统一。