跳到主要内容

微信小程序 WebView 接入

tuo-visual-viewer 依赖浏览器 DOM、Canvas 和 WebGL,不能直接作为原生小程序组件运行。小程序第一版推荐使用 web-view 打开业务域名下的 H5 Viewer 页面,由 H5 页面加载 UMD Viewer 并访问业务 API。

推荐架构​

小程序不保存 Project Key,也不直接调用需要 Project Key 的接口。Project Key、Access Token 和模型权限都由业务后端管理。

小程序页面​

pages/viewer/viewer.wxml:

<web-view src="{{viewerUrl}}" bindmessage="onViewerMessage" />

pages/viewer/viewer.js:

Page({
data: { viewerUrl: '' },

onLoad(options) {
// modelId 只作为业务查询标识;不要把 Project Key 放入 URL。
const modelId = options.modelId;
if (!modelId) return;

// 向业务后端申请一次性、短时浏览会话。
wx.request({
url: 'https://your-business.example.com/api/viewer-session',
method: 'POST',
data: { modelId },
success: ({ data }) => {
// sessionId 有效期短,并且只允许访问当前用户有权限的模型。
const query = encodeURIComponent(data.sessionId);
this.setData({
viewerUrl: `https://your-business.example.com/viewer/index.html?session=${query}`,
});
},
});
},

onViewerMessage(event) {
// H5 页面通过 wx.miniProgram.postMessage 回传事件。
const messages = event.detail?.data || [];
console.log('viewer event', messages[messages.length - 1]);
},
});

H5 Viewer 页面​

viewer/index.html:

<script src="https://resource.api.tiangongtuxue.com/tuo-visual-viewer/1.0.0/tuo-visual-viewer.umd.js"></script>
<div id="viewer" style="height:100vh"></div>
<script>
const params = new URLSearchParams(location.search);
const sessionId = params.get('session');
const container = document.querySelector('#viewer');

async function loadViewer() {
if (!sessionId) throw new Error('缺少浏览会话');

// H5 页面使用一次性 session 换取短时结果地址。
const response = await fetch('/api/viewer-session/result', {
headers: { 'X-Viewer-Session': sessionId },
});
if (!response.ok) throw new Error(`结果获取失败:${response.status}`);
const result = await response.json();

const viewer = new window.TuoVisualViewer.TuoVisualViewer(container);
await viewer.load(result.url);

// 将 Viewer 事件通知给小程序页面。
if (window.wx?.miniProgram) {
viewer.on('selectionchange', (event) => {
window.wx.miniProgram.postMessage({
data: [{ type: 'selectionchange', payload: event }],
});
});
}
}

loadViewer().catch((error) => {
document.body.textContent = error.message;
});
</script>

后端一次性会话接口​

业务后端可以使用短期随机 sessionId 保存以下信息:

字段说明
sessionId高熵随机值,建议只使用一次
userId当前小程序用户
projectId工程归属
modelId可访问的模型
expiresAt短期过期时间,例如 5 分钟

后端返回:

{
"sessionId": "<one-time-session>",
"expiresIn": 300,
"viewerUrl": "https://your-business.example.com/viewer/index.html"
}

后端换取结果时应自行完成:

  1. 校验小程序登录态和用户身份;
  2. 校验 modelId 属于当前用户可访问的工程;
  3. 校验 sessionId 未过期且未使用;
  4. 代表业务方调用天工图学 API;
  5. 只返回业务结果地址或短时结果数据;
  6. 不把 Project Key 或长期 Access Token 返回给 H5 页面。

参数传递方式​

推荐:一次性 session 参数​

https://your-business.example.com/viewer/index.html?session=<短期一次性值>

优点是小程序只传递临时标识,H5 页面无法据此获得工程长期权限。

不推荐:直接传 Project Key​

viewer.html?projectKey=...

这种方式会把 Project Key 暴露在 URL、WebView 历史、代理日志和错误监控中,禁止用于生产环境。

传递展示配置​

可以传递不敏感的 UI 配置,例如:

viewer.html?session=...&theme=light&toolbar=measure,section

配置项必须经过白名单校验,不能把任意 URL、脚本或凭据作为参数传入。

WebView 域名和发布配置​

  • 在小程序后台配置 H5 业务域名,必须使用 HTTPS;
  • H5 页面加载的 Viewer CDN 地址应具备 HTTPS 和稳定缓存策略;
  • 业务 API 域名、H5 页面域名和图片/模型结果域名都应加入允许列表;
  • 不要使用 localhost、内网 IP 或 HTTP 地址进行正式验收;
  • 检查 iOS 和 Android WebView 的 WebGL、文件下载和返回行为;
  • 页面销毁时调用 viewer.destroy(),避免切换页面后继续占用 WebGL 上下文。

最佳实践​

  • Project Key 只保存在业务后端密钥管理系统;
  • Access Token 只在后端短期缓存;
  • 小程序只携带业务模型 ID 或一次性 sessionId;
  • sessionId 有效期建议 1~5 分钟,使用后立即失效;
  • 每次打开 Viewer 前重新获取结果地址,不缓存永久地址;
  • H5 页面只展示当前用户有权访问的模型;
  • 不在小程序日志、H5 控制台和 URL 中打印 Key、Token 或临时签名地址;
  • 结果未完成时展示状态页,成功后再初始化 Viewer;
  • 监听 ready、progress、error、selectionchange,并将必要事件通过 postMessage 回传小程序;
  • 页面退出时清理 session、Object URL 和 Viewer 实例;
  • 对弱网环境设置超时、重试和友好错误提示。

与原生小程序能力的边界​

WebView 方案适合浏览、选择、隐藏、聚焦、测量和视图控制。如果业务需要原生小程序的 3D 渲染、离线模型或深度系统集成,需要另行开发原生渲染适配层,不能直接复用浏览器 UMD Viewer。