SwAsyncText
异步加载远程文本组件。URL 含 .bnc 时走 AES 解密路径,否则直接当 UTF-8 文本读取,业务方传 url 即可,加密分流由组件内部自动处理。fileId = md5(url.pathname) 写入 IndexedDB text 表持久化缓存,命中即返回;监听 url 变化自动重新加载,天然适配章节切换等场景。
基础用法
使用作用域插槽接收加载结果。text 为 null 时表示加载中,string 时表示已就绪(含缓存命中)。
基础用法(非加密)
加载中...
<template>
<SwAsyncText :url="textUrl" :img-key="aesKey">
<template #default="{ text }">
<div v-if="text === null" class="loading">加载中…</div>
<article v-else>{{ text }}</article>
</template>
</SwAsyncText>
</template>
<script setup lang="ts">
const textUrl = '/articles/welcome.txt'
const aesKey = useRuntimeConfig().public.cacheAesKey
</script>
加密内容(.bnc 分流)
下面是真实加密的演示资源(chapter-secret.bnc 用与 demo imgKey 配套的密钥加密):异步取回密文 → 客户端 AES 解密 → 展示明文。
加密内容:异步解密成功展示
加载中...
URL 含 .bnc 后缀时自动走 AES 解密路径。imgKey 沿用 SwImage 同名 prop,与 useSystemStore().info.img_key 字段对齐;非加密路径下 imgKey 仍需传值(满足 prop 必填),但内部不会被使用。
<template>
<SwAsyncText :url="chapter.url" :img-key="aesKey">
<template #default="{ text }">
<div v-if="text === null" class="skeleton" />
<article v-else v-html="text" />
</template>
</SwAsyncText>
</template>
<script setup lang="ts">
// chapter.url 例如:https://cdn.example.com/novel/chap-001.bnc
const aesKey = useRuntimeConfig().public.cacheAesKey
</script>
章节切换自动重新加载
url prop 变化时组件自动重新请求并解密,无需手动重置状态,适合翻页阅读器场景。
章节切换
加载中...
监听加载完成事件
@update:resp 在文本加载完成后触发(含缓存命中),用于字数统计、阅读进度上报等后续动作。
监听加载事件 + 字数统计
加载中...
Props
| 名称 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
imgKey | string | ✓ | — | AES 解密密钥。沿用 SwImage 同名 prop,与 useSystemStore().info.img_key 对齐;非加密路径下不会被使用,但仍需传值 |
url | string | '' | 文本 URL。含 .bnc 走解密分支,否则直接当 UTF-8 文本读取 |
Events
| 事件名 | 载荷 | 说明 |
|---|---|---|
update:resp | resp: string | 文本加载完成(含缓存命中)后触发。从 null 变为 string 时只触发一次,再次切换 url 重新加载完成又会触发 |
Slots
| 插槽名 | 作用域参数 | 说明 |
|---|---|---|
default | { text: string | null } | 作用域插槽。text === null 表示加载中(或 SSR 阶段),string 表示已就绪 |
工作原理
- SSR 短路:
import.meta.client为false时直接返回null,不发起任何网络请求与解密 - 缓存查询:
fileId = md5(url.pathname),先查 IndexedDBtext表;命中直接TextDecoder().decode(bytes)返回 - 远程加载 + 条件解密:未命中则
fetch(url)拿到ArrayBuffer;URL 含.bnc走aes.decrypt(buf, imgKey)→ 否则直接new Uint8Array(buf) - 写缓存 + 通知:解密后的字节写入 IndexedDB(下次秒开),同时
result从null→string,触发update:resp事件 - 响应式重载:
watchEffect监听props.url,变化时清状态重新走流程
底层通用逻辑封装在 useDecryptedAsset composable,SwAsyncText 与 SwImage 共享同一套 fetch + 条件解密 + 缓存 + 状态机。
注意事项
imgKey建议通过useRuntimeConfig().public.cacheAesKey传入,不要硬编码密钥- 作用域插槽中
text === null是加载中状态,text === ''是"加载完成但内容为空字符串",注意区分 - 组件仅在客户端执行,SSR 阶段插槽收到的
text始终为null,请提供骨架占位 update:resp事件缓存命中也会触发——对调用方更一致(任何"加载完成"都通知),但首屏统计 PV 时要去重