如何在Vue应用外部的JavaScript中访问Vue组件数据
问题:如何从Vue应用外部访问组件数据实现Razor表单同步提交
我们应该如何从Vue应用外部访问Vue组件的数据?例如,当触发位于Vue应用外部DOM中的按钮的普通JavaScript onClick事件时,我们该如何获取对应的数据?
在我目前的实现方案中,我设置了一个隐藏字段,每次Vue应用内执行操作时都会更新该字段的值,这样JS点击事件就能获取到所需的数据,但我认为肯定有更优的实现方式。
我当前的配置如下:
VehicleCertificates.js
import { createApp, Vue } from 'vue' import VehicleCertificates from './VehicleCertificates.vue'; const mountEl = document.querySelector("#certificates"); const app = createApp(VehicleCertificates, { ...mountEl.dataset }) const vm = app.mount("#certificates");
VehicleCertificates.vue
<template> <div style="background-color: red;"> <h3>Certificates</h3> <div> <table class="table table-striped table-hover table-condensed2" style="clear: both;"> <thead> <tr> <th><b>Type</b></th> <th><b>Valid From</b></th> <th><b>Valid Till</b></th> <th style="text-align: right;"> <a href="#" @click='addCertificate'> <i class="fa fa-plus-square"></i> Add </a> </th> </tr> </thead> <tbody> <tr v-for="(certificate, index) in certificates" :key="index"> <td>{{ certificate.CertificateTypeDescription }}</td> <td> {{ certificate.ValidFrom }} </td> <td> {{ certificate.ValidTo }} </td> <td> <a href='#' @click="removeCertificate(index)" title="Delete" style="float: right;" class="btn btn-default"> <i class="fa fa-trash"></i> </a> </td> </tr> <tr v-show="certificates.length == 0"> <td colspan="4"> No certificates added </td> </tr> </tbody> </table> </div> </div> </template> <script> import axios from 'axios'; import { onMounted, ref } from "vue"; export default { props: { vehicleId: String }, data() { return { count: 0, certificates: ref([]), types: [] } }, created() { onMounted(async () => { let result = await axios.get("/api/v1.0/vehicle/GetCertificates", { params: { vehicleId: this.vehicleId } }); this.certificates.splice(0, 0, ...result.data); this.certificatesUpdated(); }); }, methods: { removeCertificate(index) { this.certificates.splice(index, 1); this.certificatesUpdated(); }, addCertificate() { this.certificates.push({ CertificateTypeDescription: 'ADR', ValidFrom: 1, ValidTo: 2 }); this.certificatesUpdated(); }, certificatesUpdated() { $("#VehicleCertificatesJson").val(JSON.stringify(this.certificates)); } } } </script>
我的最终需求是:在提交ASP.NET Core Razor页面的表单时,能够将Vue应用中的数据和其他非Vue数据一同提交。该Vue应用仅作为Razor视图的特定部分存在,并非单页应用(SPA)。
解答
结合你非SPA嵌入Razor页面的场景,有两种成熟的优化方案可选:
方案1:全局挂载组件实例(最简单,适配所有外部调用场景)
你当前代码中已经获取到了挂载后的组件实例vm,只需要将其挂载到全局window对象上,外部JS就可以直接访问组件内的所有数据和方法:
// 修改VehicleCertificates.js import { createApp } from 'vue' import VehicleCertificates from './VehicleCertificates.vue'; const mountEl = document.querySelector("#certificates"); const app = createApp(VehicleCertificates, { ...mountEl.dataset }) const vm = app.mount("#certificates"); // 新增:挂载到全局window window.vehicleCertificatesVM = vm;
之后不管是外部按钮点击事件还是表单提交逻辑,都可以直接获取数据:
// 外部JS直接访问组件数据 const certificates = window.vehicleCertificatesVM.certificates; // 提交前一次性同步到隐藏域即可,无需每次修改组件数据都更新 document.querySelector("#VehicleCertificatesJson").value = JSON.stringify(certificates);
附带优化点
你当前代码中存在两个不规范写法,可一并修改:
- 选项式API的
data返回值本身就是响应式的,不需要用ref包裹,把certificates: ref([])改为certificates: []即可 onMounted是组合式API的钩子,选项式API直接使用mounted配置项即可,不需要单独导入,也不要写在created钩子内。
方案2:组件内部监听表单提交事件(无全局污染,和原有逻辑完全兼容)
如果不想污染全局作用域,可以在组件内部监听Razor表单的提交事件,提交前自动同步数据到隐藏域,外部完全不需要感知Vue的存在:
// 修改VehicleCertificates.vue的script部分 <script> import axios from 'axios'; export default { props: { vehicleId: String }, data() { return { count: 0, certificates: [], types: [] } }, // 替换原有的created配置 async mounted() { const result = await axios.get("/api/v1.0/vehicle/GetCertificates", { params: { vehicleId: this.vehicleId } }); this.certificates.splice(0, 0, ...result.data); // 监听表单提交事件,替换为你实际的表单ID document.querySelector("#yourFormId").addEventListener("submit", this.syncDataToHiddenField); }, beforeUnmount() { // 组件销毁前移除监听,避免内存泄漏 document.querySelector("#yourFormId")?.removeEventListener("submit", this.syncDataToHiddenField); }, methods: { syncDataToHiddenField() { document.querySelector("#VehicleCertificatesJson").value = JSON.stringify(this.certificates); }, removeCertificate(index) { this.certificates.splice(index, 1); }, addCertificate() { this.certificates.push({ CertificateTypeDescription: 'ADR', ValidFrom: 1, ValidTo: 2 }); } // 原有的certificatesUpdated方法可以直接删除,无需每次修改都同步 } } </script>
这种方案和你原有的Razor表单提交逻辑完全兼容,后端模型绑定不需要做任何修改。
内容的提问来源于stack exchange,提问作者Yan
相关产品推荐
相关产品推荐

