You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

VS Code扩展Webview中tsvscode未定义问题排查与解决求助

VS Code扩展开发:Webview中tsvscode未定义错误排查与解决

问题场景

使用TypeScript+Svelte开发VS Code扩展,点击Webview内按钮时触发两类问题:

  • Webview控制台错误:
Uncaught ReferenceError: tsvscode is not defined
    at HTMLButtonElement.click_handler (sidebar.js:619:4)
  • 编译时Svelte插件警告:
Plugin svelte: 'tsvscode' is not defined
webviews/components/Sidebar.svelte
35:       <input bind:value={text} placeholder="Enter text" />
36:       <button on:click={()=>{
37:         tsvscode.postMessage({type:"onInfo",value:"ok"});
38:       }}>Show info</button>

相关代码片段

Sidebar.svelte

<script lang="ts">
    let text='';
</script>

<style>
    .main-container{
        height: 98vh;
        width: 100%;
        display: flex;
        flex-direction: column;
    }
    .output-container{
        border: 1px solid black;
        border-radius: 10px;
        margin-bottom: 10px;
        padding: 10px;
        height: 100%;
    }
    .input-container{
        bottom: 0;
        width: 100%;
    }
    input{
        margin-bottom: 10px;
    }
</style>

<div class="main-container">
    <div class="output-container">
        Your text will appear here:
      {text}
    </div>
  
    <div class="input-container">
      <input bind:value={text} placeholder="Enter text" />
      <button on:click={()=>{
        tsvscode.postMessage({type:"onInfo",value:"ok"});
      }}>Show info</button>
    </div>
</div>

SidebarProvider.ts

import * as vscode from "vscode";
import { getNonce } from "./getNonce";
import { apiUrl } from "./constants";

export class SidebarProvider implements vscode.WebviewViewProvider {
  _view?: vscode.WebviewView;
  _doc?: vscode.TextDocument;

  constructor(private readonly _extensionUri: vscode.Uri) {}

  public resolveWebviewView(webviewView: vscode.WebviewView) {
    this._view = webviewView;

    webviewView.webview.options = {
      enableScripts: true,
      localResourceRoots: [this._extensionUri],
    };

    webviewView.webview.html = this._getHtmlForWebview(webviewView.webview);

    webviewView.webview.onDidReceiveMessage(async (data) => {
      switch (data.type) {
        
        case "onInfo": {
          if (!data.value) {
            return;
          }
          vscode.window.showInformationMessage(data.value);
          break;
        }
        case "onError": {
          if (!data.value) {
            return;
          }
          vscode.window.showErrorMessage(data.value);
          break;
        }
      }
    });
  }

  public revive(panel: vscode.WebviewView) {
    this._view = panel;
  }

  private _getHtmlForWebview(webview: vscode.Webview) {
    const styleResetUri = webview.asWebviewUri(
      vscode.Uri.joinPath(this._extensionUri, "media", "reset.css")
    );
    const scriptUri = webview.asWebviewUri(
      vscode.Uri.joinPath(this._extensionUri, "out", "compiled/sidebar.js")
    );
    const styleMainUri = webview.asWebviewUri(
      vscode.Uri.joinPath(this._extensionUri, "out", "compiled/bundle.css")
    );
    const styleVSCodeUri = webview.asWebviewUri(
      vscode.Uri.joinPath(this._extensionUri, "media", "vscode.css")
    );

    const nonce = getNonce();

    return `<!DOCTYPE html>
            <html lang="en">
                <head>
                    <meta charset="UTF-8">
                    <meta http-equiv="Content-Security-Policy" content="img-src https: data:; style-src 'unsafe-inline' ${webview.cspSource}; script-src 'nonce-${nonce}';">
                    <meta name="viewport" content="width=device-width, initial-scale=1.0">
                    <link href="${styleResetUri}" rel="stylesheet">
                    <link href="${styleVSCodeUri}" rel="stylesheet">
                    <link href="${styleMainUri}" rel="stylesheet">
                    <script nonce ="${getNonce}"> const tsvscode = acquireVsCodeApi(); </script>
                </head>
                <body>
                    <script nonce="${nonce}" src="${scriptUri}"></script>
                </body>
            </html>`;
  }
}

globals.d.ts

import * as _vscode from "vscode";

declare global {
    const tsvscode:{
        postMessage: ({type: string, value:any})=>void;
    };
}

错误原因分析

  1. CSP脚本阻止:SidebarProvider.ts中创建tsvscode的脚本标签使用了${getNonce}(仅函数引用,未执行获取值),导致脚本的nonce值与CSP声明的nonce-${nonce}不匹配,浏览器直接阻止该脚本执行,tsvscode全局变量未被初始化。
  2. 类型定义未被识别:globals.d.ts的全局变量声明未被TypeScript编译器纳入编译范围,导致Svelte组件无法识别tsvscode的类型,触发未定义警告。

解决方法

1. 修正HTML中的nonce值

在SidebarProvider.ts的_getHtmlForWebview方法里,将创建tsvscode的脚本标签修改为:

<script nonce="${nonce}"> const tsvscode = acquireVsCodeApi(); </script>

替换后脚本使用已生成的nonce变量,与CSP策略中的值一致,浏览器允许脚本执行,tsvscode变量正常初始化。

2. 确保全局类型定义被识别

修改项目根目录的tsconfig.json,将globals.d.ts加入include数组:

{
  "compilerOptions": {
    // 原有配置
  },
  "include": [
    "src/**/*",
    "webviews/**/*",
    "globals.d.ts" // 添加该行,路径根据实际位置调整
  ]
}

3. 临时方案(可选)

如果类型定义仍未生效,可在Sidebar.svelte的script标签中显式声明:

<script lang="ts">
    let text='';
    declare const tsvscode: {
        postMessage: ({type: string, value: any}) => void;
    };
</script>

推荐优先使用tsconfig.json的方式,保证全局类型统一。

验证

重新编译并启动扩展,点击Webview内的「Show info」按钮,VS Code会弹出信息提示,Webview控制台无报错,编译时也不会出现Svelte插件警告。

内容的提问来源于stack exchange,提问作者Ppp

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 18:42:02