Vue: bug: infinite loop async component crashes browser tab

Created on 13 May 2019  路  15Comments  路  Source: vuejs/vue

Version

2.6.4

Reproduction link

https://gist.github.com/DominusVilicus/f825d90575fccd6d437b02061c725a91 (code-sandbox doesn't allow import())

Steps to reproduce

export default {
  render(h){
    return h(import('./comp'))
  }
}

Results in an infinite loop that stops the browser tab from working (chrome). Just need to do vue create app, and create two components, and then use them in the App.vue

export default {
  computed:{
    component(){
      return () => import('./comp')
    }
  },
  render(h){
    return h(this.component)
  }
}

works fine however

comp.vue

<template>
    <pre>Hello World</pre>
</template>

What is expected?

Returning the imported component in h(import('./comp')) should render the async component

What is actually happening?

It's going in an infinite loop (try put console.log('test') in render()

Most helpful comment

I already told you that we need a variable in my first comment. I would appreciate if you stop spamming, it's not the first time you do this and it's not particularly nice.

If you want to, you can instead try to help improve the documentation page for render functions.

What you can do is this:

export default {
  data: () => ({ comp: 'comp' }),
    computed: {
    component(){
        const comp = this.comp
        return () => import('./blocks/' + comp) 
    },
}
    render(h) {
        return h(this.component);
    }
}

and this is the way you do with no modules:

var Comp = () => Promise.resolve({ template: `<div>Comp</div>` })

new Vue({
  el: '#app',
  data: {
    foo: 'bar'
  },
  render: h => h(Comp)
})

All 15 comments

put the component in a variable outside of the render function:

const Comp = () => import('./comp')

it's necessary for Vue to track whether the component has been fetched

what if I want a dynamic imported/lazy-loaded async component
eg:

export default {
  data(){
    return { block: 'test' }
  },
  render(h){
    return h(import('./blocks/${block}))
  }
}

@posva

can you provide an HTML repro? you can use Promise.resolve and even add a setTimeout to simulate the import If you do I can take a look

https://github.com/DominusVilicus/asyncdynamic

The use-case is building a block editor like gutenberg inside Vue. I need dynamic imports for components so as to not bloat the pages and editor when adding blocks to the page

My goal is to render a collection of blocks, dynamically and asynchronously

export default {
    data(){
        return { component: () => import('./blocks/comp') }
    },
    render(h) {
        return h(this.component);
    }
};

Works, but my goal is more like:

export default {
    data(){
        return { comp: 'comp' }
    },
    render(h) {
        return h(()=> import(`./blocks/${this.comp}`));
    }
};

@posva

can you provide an HTML repro? Thanks

Docs state (https://vuejs.org/v2/guide/render-function.html#createElement-Arguments)

createElement(
  // {String | Object | Function}
  // An HTML tag name, component options, or async
  // function resolving to one of these. Required.
  'div'
  //...
)

yet even this fails

render(h) {
   return h(async ()=>{
        return 'div'
    }, {}, ['child-text']);
}

There is either something wrong with the docs, or with vue itself

@posva, I can't provide a html repro, I don't know how import works in a pure html environment. CodeSandbox doesn't have webpack configured in a way where it allows for imports either.

The only way you can do this is using vue-cli as far as I am aware. I have provided the code for the minimal reproduction.

It turns out

createElement()

doesn't seem to know how to handle async functions, or functions for that matter, as the docs say they should

https://codesandbox.io/s/l7mo9o6j5q

here's a min repro of createElement() not working like they docs say it should

@posva

@posva should this issue be reopened, because createElement() isn't working as the docs say they are. It can't handle functions that return a string, nor objects, nor components that are promises

I already told you that we need a variable in my first comment. I would appreciate if you stop spamming, it's not the first time you do this and it's not particularly nice.

If you want to, you can instead try to help improve the documentation page for render functions.

What you can do is this:

export default {
  data: () => ({ comp: 'comp' }),
    computed: {
    component(){
        const comp = this.comp
        return () => import('./blocks/' + comp) 
    },
}
    render(h) {
        return h(this.component);
    }
}

and this is the way you do with no modules:

var Comp = () => Promise.resolve({ template: `<div>Comp</div>` })

new Vue({
  el: '#app',
  data: {
    foo: 'bar'
  },
  render: h => h(Comp)
})

I apologise, I am just trying to understand

That solves my original problem so thank you, I will look into why using a function in the first argument causes the browser to crash (and act unexpectedly according to the docs) and let you know what I find out. :)

Thanks! Yeah, the doc page for render functions is probably the one that needs the most care among all pages in vuejs docs. It's also a quite advanced topic so not everybody can contribute to that section, but since you have been experimenting so much with render functions, you should for sure be able to improve that page.

When using a variable like const Component = () => ..., Vue stores a resolved property in it Component.resolved === true (after resolving the promise)

Duplicate of #9943

Was this page helpful?
0 / 5 - 0 ratings

Related issues

aviggngyv picture aviggngyv  路  3Comments

robertleeplummerjr picture robertleeplummerjr  路  3Comments

seemsindie picture seemsindie  路  3Comments

bfis picture bfis  路  3Comments

paceband picture paceband  路  3Comments