跳转到内容
关系图谱

web-frame:浏览器窗口样式的网页嵌入

1,384 字 5 分钟

属性 1
series
VaultKit 包指南

@vaultkit/vitepress-plugin-web-frame 为 VitePress / markdown-it 提供 ::: web-frame 容器,把任意网页(或任意 Markdown 内容)装进一个「浏览器窗口」视觉里:三色圆点栏 + 圆角边框 + 柔和阴影,颜色取 VitePress --vp-c-* 语义变量,明暗模式自动翻转。视觉参考 taicode-labs/clarify 的 WebFrame 组件语义。

与系列里的 bilibili 容器 同款设计哲学:markdown-it-container 构建期直接产出 HTML + CSS,客户端零 JS,未知属性构建期抛错。本文覆盖全部 6 个容器属性、3 个插件 options,以及双内容形态与 deferSrc 的设计考量。

为什么需要它

写作中经常要「给读者看一眼某个网页长什么样」——展示官网、演示 demo 页、对比竞品界面。裸 <iframe> 有老三样问题:不自适应、无懒加载、无视觉包装。而截图又是死的:上游一改版就过时。

web-frame 容器给出第三条路:构建期产出带浏览器窗口 chrome 的响应式 iframe,loading="lazy" 懒加载;更妙的是它还有纯内容形态——不给 src 时容器正文本身就是窗口内容,适合手绘示意、富文本演示,让一段普通 Markdown 看起来像一个应用窗口。

安装

sh
pnpm add -D @vaultkit/vitepress-plugin-web-frame
ts
// .vitepress/config.ts
import { webFrame } from '@vaultkit/vitepress-plugin-web-frame'
import { defineConfig } from 'vitepress'

export default defineConfig({
  markdown: {
    config(md) {
      md.use(webFrame)
    },
  },
})
ts
// .vitepress/theme/index.ts
import '@vaultkit/vitepress-plugin-web-frame/styles/index.css'

使用 @vaultkit/vitepress-theme-vault 主题则开箱即带,容器直接可用。

快速上手

语法:

md
::: web-frame {src="https://vitepress.dev" title="VitePress 官网" height=360}
:::

效果——下面是本页构建期真实渲染的窗口,内嵌 VitePress 官网:

不给 src 的纯窗口形态,正文即窗口内容:

没有 src 时,容器正文作为窗口内容渲染 —— 支持加粗行内代码链接 等任意 Markdown。

容器属性逐项详解

全部 6 个属性:

属性类型缺省说明
srcstringiframe 地址;缺省时正文即窗口内容
titlestringEmbedded contentiframe title + 窗口栏 sr-only 文案
heightnumber | string420数字(px)或任意 CSS 长度
showWindowbooleantrue是否渲染三色圆点窗口栏
classstring透传外层容器类名
idstring透传外层容器 id

src

要嵌入的网页地址。有 src 时渲染 iframe(正文通常留空,样式以 :empty 自动收起);没有 src 时进入纯内容形态:

md
::: web-frame {src="https://example.com"}
:::

::: web-frame {title="无 src 形态"}
这段 Markdown 就是窗口内容。
:::

注意被嵌网页的 X-Frame-Options / CSP frame-ancestors——拒绝被嵌的站点(如 GitHub)iframe 会一片空白,这是浏览器安全策略,插件无法绕过。

title

一石二鸟的无障碍文案:写进 iframe 的 title 属性,同时作为窗口栏的屏幕阅读器文案。缺省 Embedded content,中文站建议显式给出:

md
::: web-frame {src="https://vitepress.dev" title="VitePress 官网首页"}
:::

height

窗口内容区高度。裸数字按 px 处理,字符串支持 60vh24rem 等任意 CSS 长度;缺省 420

md
::: web-frame {src="https://vitepress.dev" height=360}
:::

::: web-frame {src="https://vitepress.dev" height="60vh"}
:::

纯内容形态下 height 作用于内容框(超出滚动);iframe 形态下作用于 iframe 本体。

showWindow

是否渲染窗口 chrome(三色圆点栏)。showWindow=false 得到纯 iframe / 纯内容框,适合不想要「拟窗」隐喻的场合。kebab-case 别名 show-window 同义:

md
::: web-frame {src="https://vitepress.dev" showWindow=false height=300}
:::

class 与 id

透传到外层 .web-frame 容器,分别用于挂自定义样式与页内锚点:

md
::: web-frame {src="https://vitepress.dev" class="full-bleed" id="site-demo"}
:::

插件 Options 全览

md.use(webFrame, options)WebFrameOptions

名称类型默认值说明
defaultTitlestring'Embedded content'title 缺省文案
defaultHeightnumber | string420height 缺省值
deferSrcbooleanfalse输出 data-src,由宿主 IntersectionObserver 注入
ts
md.use(webFrame, {
  defaultTitle: '嵌入内容',
  defaultHeight: '50vh',
  deferSrc: true,
})

设计机制

双内容形态:一个容器两种用法

渲染分支只看 src 有无:有则输出 iframe + 空内容槽;无则内容槽即正文(并把 height 移到内容槽上)。这让「嵌网页」和「拟窗展示 Markdown」共享同一套窗口视觉与属性词汇,不需要第二个容器。

deferSrc 与 bfcache

与 bilibili 容器同源的机理:浏览器原生 loading="lazy" 预载距离过大,慢网下 iframe 仍会抢首屏带宽;而导航中的子帧会让页面失去 bfcache 资格deferSrc: true 改输出 data-src,由宿主 IntersectionObserver 在进入视口附近时注入真实 src

该模式产物刻意不带 loading="lazy"——无 src 的 lazy iframe 会让 Chromium 挂起子帧 about:blank 导航(SubframeIsNavigating),反而破坏 bfcache。宿主须自备注入脚本(@vaultkit/vitepress-theme-vault 已内置)。

iframe 底色为什么固定白

窗口栏、边框颜色都取 --vp-c-* 变量随明暗翻转,唯独 iframe 底色固定 #fff:被嵌网页绝大多数是浅色底,若 iframe 透明,暗色模式下会透出宿主深色背景,加载闪烁非常难看。固定白底是对「大多数被嵌页」的最优先验。

构建期 fail-fast

属性白名单只有 6 个键,出现未知键(比如把 showWindow 拼成 showWindows)构建期直接抛错——问题止步于构建,不产出坏页面。

常见问题

嵌入的页面一片空白? 九成是对方站点禁止被 iframe(X-Frame-Options: DENY 或 CSP)。换目标页,或退回截图。

能嵌本站页面吗? 可以,src 写相对路径也行,但小心「页面嵌自己」的递归。

窗口栏能自定义颜色吗? 三色圆点是写死的红黄绿(data-dot 属性挂钩),可用 class 属性 + 自定义 CSS 覆盖。

相关链接