如何让loadPyodide()在Vue+Pinia的任意组件中可用?
Vue + Pinia 全局共享Pyodide实例(仅加载一次)
完全可以通过Pinia实现Pyodide实例的全局共享,且保证仅加载一次。你遇到的Uncaught TypeError: pyodide.runPython is not a function错误,核心原因是没有正确等待Pyodide加载完成,或者直接把未resolve的Promise赋值给了变量,导致调用时拿到的不是真正的Pyodide实例。
完整实现方案
1. 创建Pinia Store管理Pyodide实例
新建stores/pyodideStore.js,封装加载逻辑和实例状态:
import { defineStore } from 'pinia' export const usePyodideStore = defineStore('pyodide', { state: () => ({ // 存储加载完成的Pyodide实例 pyodideInstance: null, // 标记加载状态,避免重复触发加载 isLoading: false, // 存储加载错误信息 loadError: null }), actions: { async loadPyodide() { // 已加载或正在加载时直接返回 if (this.pyodideInstance || this.isLoading) return this.isLoading = true this.loadError = null try { // 等待Pyodide加载完成,可根据需求配置indexURL const pyodide = await window.loadPyodide({ indexURL: "https://cdn.jsdelivr.net/pyodide/v0.25.0/full/" }) // 将实例存入状态 this.pyodideInstance = pyodide } catch (err) { this.loadError = err.message console.error('Pyodide加载失败:', err) } finally { this.isLoading = false } }, async runPythonCode(code) { if (!this.pyodideInstance) { throw new Error('Pyodide实例未加载,请先执行loadPyodide') } try { // 优先使用runPythonAsync避免阻塞主线程 return await this.pyodideInstance.runPythonAsync(code) } catch (err) { console.error('Python代码执行失败:', err) throw err } } } })
2. 组件中调用示例
在任意Vue组件中,通过Pinia获取实例并调用方法:
<template> <div> <button @click="initPyodide" :disabled="isLoading">加载Pyodide</button> <textarea v-model="pythonCode" placeholder="输入Python代码"></textarea> <button @click="executeCode" :disabled="!pyodideInstance || isLoading">执行代码</button> <div v-if="loadError" class="text-red-500">加载错误: {{ loadError }}</div> <div v-if="executionResult">执行结果: {{ executionResult }}</div> </div> </template> <script setup> import { usePyodideStore } from '@/stores/pyodideStore' import { storeToRefs } from 'pinia' import { ref } from 'vue' const pyodideStore = usePyodideStore() // 解构响应式状态 const { pyodideInstance, isLoading, loadError } = storeToRefs(pyodideStore) const pythonCode = ref('print("Hello from Pyodide!")') const executionResult = ref('') // 初始化Pyodide const initPyodide = async () => { await pyodideStore.loadPyodide() } // 执行Python代码 const executeCode = async () => { try { executionResult.value = await pyodideStore.runPythonCode(pythonCode.value) } catch (err) { executionResult.value = '执行出错: ' + err.message } } </script>
关键注意事项
- 确保Pyodide的全局引入:在项目入口
index.html中添加<script src="https://cdn.jsdelivr.net/pyodide/v0.25.0/full/pyodide.js"></script>,保证window.loadPyodide可用。 - 提前预加载:可以在
App.vue的onMounted钩子中调用loadPyodide,让Pyodide在应用启动时就开始加载,避免用户等待。 - 包安装扩展:如果需要安装Python第三方包,可在
loadPyodide方法中添加await this.pyodideInstance.installPackages(['numpy']),封装成独立的Pinia action即可。
内容的提问来源于stack exchange,提问作者rafaelcb21
相关产品推荐
相关产品推荐

