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

如何多次使用VueJS组件但仅执行一次created/mounted函数?

同一页面多次复用Vue组件时,如何仅发起一次AJAX请求获取数据?

我想要创建一个VueJS组件,实现以下功能:

  • 在mounted/created钩子中下载数据(选项列表)
  • 在Multiselect组件中展示下载的数据
  • 用户完成选择后将选中数据传回父组件

初始示例代码如下:

<template>
    <div>
        <multiselect v-model="value" :options="options"></multiselect>
    </div>
</template>

<script>
import Multiselect from 'vue-multiselect'
export default {
    components: { Multiselect },
    mounted() {
        this.getOptions();
    },
    methods:{
        getOptions() {
            // 执行AJAX请求
            // 将响应结果赋值给options
        }
    },
    data () {
        return {
            value: null,
            options: []
        }
    }
}
</script>
<style src="vue-multiselect/dist/vue-multiselect.min.css"></style>

如果页面中仅调用一次该组件,实现起来较为简单。但问题在于我可能需要在同一页面中多次使用该组件,有时甚至达数十次,我不希望多次调用this.getOptions();方法。

有没有办法实现该组件,使其无论在页面中被使用多少次,AJAX请求仅执行一次?

补充说明:我知道可以在父组件中下载数据,再通过props传递给子组件,示例代码如下,但这种方式违背了组件的设计初衷:

props: {
    optionsPassedByParents: Array
},
mounted() {
    if(this.optionsPassedByParents.length == 0)
        this.getOptions();
    else
        this.options = this.optionsPassedByParents;
},

解决方案

1. 用Promise缓存请求结果

在组件外部维护一个全局Promise缓存,第一次请求发起后,后续组件实例直接复用这个Promise的结果,避免重复请求:

<template>
    <div>
        <multiselect v-model="value" :options="options"></multiselect>
    </div>
</template>

<script>
import Multiselect from 'vue-multiselect'

// 全局缓存请求Promise,所有组件实例共享
let optionsPromise = null

export default {
    components: { Multiselect },
    data () {
        return {
            value: null,
            options: []
        }
    },
    async mounted() {
        await this.getOptions()
    },
    methods:{
        async getOptions() {
            if (!optionsPromise) {
                // 替换为你的实际AJAX请求(比如axios.get)
                optionsPromise = new Promise((resolve) => {
                    // 模拟AJAX请求延迟
                    setTimeout(() => {
                        const mockOptions = [
                            { label: '选项1', value: 1 },
                            { label: '选项2', value: 2 },
                            { label: '选项3', value: 3 }
                        ]
                        resolve(mockOptions)
                    }, 1000)
                })
            }
            // 等待Promise完成,直接复用结果
            this.options = await optionsPromise
        }
    },
    watch: {
        value(newVal) {
            // 将选中值传回父组件
            this.$emit('update:modelValue', newVal)
        }
    }
}
</script>
<style src="vue-multiselect/dist/vue-multiselect.min.css"></style>

2. 使用Vuex管理全局数据

如果项目已使用Vuex,可将数据存入全局状态,组件从Vuex获取数据,仅在数据为空时发起请求:

// store/modules/options.js
const state = {
    options: [],
    isLoading: false
}

const mutations = {
    SET_OPTIONS(state, data) {
        state.options = data
    },
    SET_LOADING(state, status) {
        state.isLoading = status
    }
}

const actions = {
    async fetchOptions({ commit, state }) {
        // 已有数据或正在加载时直接返回
        if (state.options.length || state.isLoading) return
        commit('SET_LOADING', true)
        // 替换为实际AJAX请求
        const response = await fetch('/api/options')
        const data = await response.json()
        commit('SET_OPTIONS', data)
        commit('SET_LOADING', false)
    }
}

export default {
    namespaced: true,
    state,
    mutations,
    actions
}

组件中使用:

<template>
    <div>
        <multiselect v-model="value" :options="options" :loading="isLoading"></multiselect>
    </div>
</template>

<script>
import Multiselect from 'vue-multiselect'
import { mapState, mapActions } from 'vuex'

export default {
    components: { Multiselect },
    data () {
        return {
            value: null
        }
    },
    computed: {
        ...mapState('options', ['options', 'isLoading'])
    },
    methods: {
        ...mapActions('options', ['fetchOptions'])
    },
    async mounted() {
        await this.fetchOptions()
    },
    watch: {
        value(newVal) {
            this.$emit('update:modelValue', newVal)
        }
    }
}
</script>
<style src="vue-multiselect/dist/vue-multiselect.min.css"></style>

3. 使用provide/inject跨组件共享数据

若不想用Vuex,可在根组件或公共父组件中发起请求,通过provide提供数据,子组件用inject接收,同时保留兜底请求逻辑:

// 根组件或公共父组件
export default {
    data() {
        return {
            globalOptions: [],
            isOptionsLoading: false
        }
    },
    async created() {
        this.isOptionsLoading = true
        // 实际AJAX请求
        const response = await fetch('/api/options')
        this.globalOptions = await response.json()
        this.isOptionsLoading = false
    },
    provide() {
        return {
            getGlobalOptions: () => ({
                options: this.globalOptions,
                isLoading: this.isOptionsLoading
            })
        }
    }
}

组件中使用:

<template>
    <div>
        <multiselect v-model="value" :options="options" :loading="isLoading"></multiselect>
    </div>
</template>

<script>
import Multiselect from 'vue-multiselect'

export default {
    components: { Multiselect },
    inject: ['getGlobalOptions'],
    data () {
        return {
            value: null,
            localOptions: [],
            localLoading: false
        }
    },
    computed: {
        options() {
            const globalData = this.getGlobalOptions()
            return globalData.options.length ? globalData.options : this.localOptions
        },
        isLoading() {
            const globalData = this.getGlobalOptions()
            return globalData.isLoading || this.localLoading
        }
    },
    async mounted() {
        const globalData = this.getGlobalOptions()
        // 全局无数据且未加载时,自己发起请求兜底
        if (!globalData.options.length && !globalData.isLoading) {
            this.localLoading = true
            const response = await fetch('/api/options')
            this.localOptions = await response.json()
            this.localLoading = false
        }
    },
    watch: {
        value(newVal) {
            this.$emit('update:modelValue', newVal)
        }
    }
}
</script>
<style src="vue-multiselect/dist/vue-multiselect.min.css"></style>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 02:55:30