成功调用 axios 后,Vue.js 组件不会发送到父级

Posted

技术标签:

【中文标题】成功调用 axios 后,Vue.js 组件不会发送到父级【英文标题】:Vue.js component does not emit to parent after successful axios call 【发布时间】:2020-02-15 09:12:19 【问题描述】:

我正在使用

"axios": "^0.19.0",
"vue": "^2.6.10",
"vuex": "^3.1.1"

我的vuex 操作看起来像这样,并使用axios 调用远程接口。该请求确实有效并收到了有效的响应。

skipQuestion(commit, payload) 
    let params = 
        answer: 
            id: payload.id,
            skipped: true,
        
    ;

    return new Promise((resolve, reject) => 
        commit(UPDATE_LOADING, true);
        Remote.put(`/answer.json`, params)
            .then((response) => 
                commit(UPDATE_LOADING, false);
                commit(SKIP_QUESTION, payload.id);
                resolve();
            )
            .catch((error) => 
                commit(UPDATE_LOADING, false);
                reject(error);
            )
    )
,

组件Question 确实具有以下方法skip,它调用vuex 操作skipQuestion 并应该向父组件发出skip 事件。

...mapActions(['skipQuestion']),
skip(evt) 
    let payload =  id: this.question_id ;
    this.skipQuestion(payload).then( () => 
        this.$emit('skip', this.uuid);
    ).catch( (error) => 
        console.log(error);
    );
,

问题是,skip 事件在操作的 then 块中使用时不会发送给父级。 chrome 的 vue 开发者控制台也确认,skip 事件已被触发。如果我将发射放在块外,一切正常。有什么建议吗?

编辑 1

还尝试了以下代码,两个日志语句都打印到控制台。

skip(evt) 
    let payload =  id: this.question_id ;
    let vm = this; 
    this.skipQuestion(payload).then( () => 
        console.log('before skip emit');
        vm.$emit('skip', this.uuid);
        console.log('after skip emit');
    ).catch( (error) => 
        console.log(error);
    );
,

编辑 2

这是用于监听子事件的模板代码:

<question v-bind="question"
          :key="question.uuid"
          v-if="questionReady"
          v-on:skip="onSkipQuestion"
          v-on:answer="onAnswerQuestion">
</question>

如前所述,如果不使用由axios 请求返回的 Promise/then-block,则发出确实有效。您将在下面找到应该调用的方法:

methods: 
  onSkipQuestion(payload) 
    // this code is not executed
    console.log('onSkipQuestion')

    //....
  ,


  //....
      

【问题讨论】:

也许不是您问题的最终解决方案,但您绝对应该尝试切换到 await / async 而不是使用原始承诺 我假设你的父组件上有类似v-on:skip=somfunc 的东西来收听skip 事件。对吗? 是的,当然,我也试过@skip=somefunc。如前所述,如果我在 then 块之外发出它,一切正常。 你能添加你的模板代码吗? 也许尝试使用.bind()这个方法,那么也许你的父组件也会看到这个事件。或者尝试在data() return skip: () =&gt; .... 中创建skip 箭头功能 【参考方案1】:

总结您给出的信息:

用于 chrome 的 vue 开发者控制台也确认已触发跳过事件。


TL;DR

vm.$emit 基本上调用vm._events[eventName] 中列出的每个方法

v-oncreateElemet 中通过context.listeners 注册并通过updateListeners 注入


基本上可以使用debugger 语句进行调试:

skip(evt) 
    let payload =  id: this.question_id ;
    this.skipQuestion(payload).then( () => 
        debugger; // scope -> _events & scope -> $parent.componentInstance
        // or console.log(JSON.stringify(this._events))
        this.$emit('skip', this.uuid);
    ).catch( (error) => 
        console.log(error);
    );
,

那你就知道发生了什么了。


检查事项:

有效的this 范围

有效parent

实际触发为已解决

vm._events已注册

【讨论】:

【参考方案2】:

您在 then 块中丢失了对 this 的引用。引用现在是调用的回调函数。而是这样做

 ...mapActions(['skipQuestion']),
    skip(evt) 
        let payload =  id: this.question_id ;
        let vm = this; // Preserve Vue instance for use inside block
        this.skipQuestion(payload).then( () => 
            vm.$emit('skip', vm.uuid);
        ).catch( (error) => 
            console.log(error);
        );
    ,

【讨论】:

谢谢,我之前尝试过,但没有任何改变。该事件不会发送给父级。 奇怪....你确认then被阻止是通过添加console.log语句来调用的吗? 是的,我在emit 之前和之后添加了一个console.log,两者都打印在then 块中。确实更新了代码示例。 如果您将this 更改为vm,那么在emit 语句中this.uuid 也应该更改。【参考方案3】:

也许问题是你没有在skipQuestion() 中返回resolve() 或reject()。

无论如何,假设您的应用程序使用 ES2017(或更高版本),或者如果不是,则使用 Babel,我会改进函数以使用 async / await 结构,以便 @987654323 @ 不会有任何风险(参考Varun's answer),如果skipQuestion() 没有返回任何东西,也不会有任何风险,就像目前一样:

async skip(evt) 
    let payload =  id: this.question_id ;
    try 
      let result = await skipQuestion(payload)
      this.$emit('skip', this.uuid);
     catch(error)
      console.log(error);
    

Promise((resolve, reject) =&gt; ...) 语法的情况下,不返回 resolve / reject 是已知的错误来源

【讨论】:

感谢您的回答。目前我们正在使用Babel。我们尝试了您的更新代码,还删除了额外的 Promise 的返回,但仍然没有将 emit 提升到父组件。 你是什么意思“删除了额外的承诺的回报”?您可以编辑您的问题并添加您所做的更改吗? 商店操作现在返回return Remote.put(/answer.json, params) .then((response) =&gt; commit(UPDATE_LOADING, false); ) .catch((error) =&gt; commit(UPDATE_LOADING, false); ) 你不需要删除 promise 语法。无论如何,请在 thencatch 块中返回一些东西!您在启动 skipQuestion() 时使用 thencatch,我认为它不会被命中,因为没有返回任何内容 您能否举一个例子来说明您的陈述“不返回解决/拒绝是已知的错误来源” - 这不应该是 o0 的情况?至少对于承诺状态 - 除非你在承诺内解决/拒绝后运行阻塞代码......我很好奇【参考方案4】:

我也遇到了同样的问题,我终其一生都无法弄清楚发生了什么。我能想到的是,在等待承诺解决方案时,组件以某种方式失去了发出事件的能力。

无论如何,我的解决方案是自己发出承诺,如下所示:

skip(evt) 
    let payload =  id: this.question_id ;
    this.$emit('skip', skipQuestion(payload));

在父级上,你可以这样做

... @skip="receive_skip($event)" ...

...

methods: 
    receive_skip(skipped) 
        skipped
        .then((data) => 
            // do something on success
        )
        .catch((err) => 
            // do something on fail
        );
    

它没有那么干净和优雅,但它完成了工作。

【讨论】:

以上是关于成功调用 axios 后,Vue.js 组件不会发送到父级的主要内容,如果未能解决你的问题,请参考以下文章

Axios Vue.js 将对象数组从 store.js 传递给组件

Axios-一次发出多个请求(vue.js)

Vue.js 模板循环 - 异步 axios 调用

axios请求后的VUE JS动态背景图片

Vue Js - 文件输入更改后发送axios请求

在 axios 调用时,DOM 不显示 vue.js 和 laravel 中数组的更新数据