Skip to content

Why should warn and hint the coder to not use defineAsyncComponent ? #1469

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Yiximail opened this issue Jul 11, 2022 · 18 comments
Closed

Why should warn and hint the coder to not use defineAsyncComponent ? #1469

Yiximail opened this issue Jul 11, 2022 · 18 comments

Comments

@Yiximail
Copy link

What problem does this feature solve?

我阅读过 #627#682 问题。
I've read read about issues #627 and #682.

问题 #627 是关于:
The issue #627 is about:

createRouter({
  routes: [{
    path: '/',
    component: defineAsyncComponent(() => import('...'))
  }]
})

它没必要使用到 defineAsyncComponent, 所以它应该写成
It is not necessary to use defineAsyncComponent, so it should become

createRouter({
  routes: [{
    path: '/',
    component: () => import('...')
  }]
})

我理解并且十分同意这样改变
I understand and fully agree with this change.

但是根据 defineasynccomponent 的文档 和 Async Components,它其实是可以接收其他参数。
However, according to defineasynccomponent API and Async Components. Actually, it can receive other parameters.

interface AsyncComponentOptions {
  loader: AsyncComponentLoader
  loadingComponent?: Component
  errorComponent?: Component
  delay?: number
  timeout?: number
  suspensible?: boolean
  onError?: (
    error: Error,
    retry: () => void,
    fail: () => void,
    attempts: number
  ) => any
}

所以我认为不应该只要使用了 defineAsyncComponent 就弹出警告。
So I don't think it should get a warning when use defineAsyncComponent by receive an object.

在我使用场景中,我需要一个加载页来等待下一个页面准备好或者等待依赖的chunk下载好,所以我这样写了。
In my usage scenario. I need a loading page to wait for the next page to be ready. Or just wait for the dependent chunks to download. So I write it like this.

import LoadingPage from "..."
createRouter({
  routes: [{
    path: '/',
    component: defineAsyncComponent({
      loader: () => import('...'),
      loadingComponent: LoadingPage
    })
  }]
})

它能够实现我的需求,但是收到了一个来自 #682 修改的警告。
And it works for my needs. But I received a warning from #682 as well.

[Vue Router warn]:
Component "default" in record with path "/" is defined using "defineAsyncComponent()".
Write "() => import('./MyPage.vue')" instead of "defineAsyncComponent(() => import('./MyPage.vue'))".

我不太确定为什么不应该使用 defineAsyncComponent,是我忽略了什么吗?或者使用它会产生什么问题?
I'm not sure why should not use defineAsyncComponent. Is it something I ignored? Or what problems will arise from using it.

@posva
Copy link
Member

posva commented Jul 11, 2022

Because the router needs to load the component during the navigation (lazy loading), so everything would be ignored anyway. If you use a defineAsyncComponent, then the loading isn't part of the navigation.


Please, next time consider using the forum, the Discord server or StackOverflow for questions first. But feel free to come back and open an issue if it turns out to be a bug 🙂

@posva posva closed this as completed Jul 11, 2022
@Yiximail
Copy link
Author

Thank you for your reply

@Autumn-one
Copy link

这确实是一个问题,defineAsyncComponent 的定义并不是徒劳的,我测试了一下,在不使用 defineAsyncComponent 的情况下,无法很好的添加异步组件加载时候的loading状态,这样我们导航的时候可能会在没有任何交互反馈的情况下让用户不知所措,我也认为这个warning是不应该存在的,看了上面的回答我目前一头雾水,不知道这个解释与问题有什么关联?

@Autumn-one
Copy link

I don't understand your answer to this question. After thinking about it, I find another question is how to add a Loading component to the loading process of asynchronous components without using defineAsyncComponent.

@Autumn-one
Copy link

Thank you for your reply↳

我看了很多相关的解释,理由都很奇怪,实际也没有解决存在的问题,请问你明白他回复的意思了么?后面是怎么解决这个问题的呢?

@Yiximail
Copy link
Author

Thank you for your reply↳

我看了很多相关的解释,理由都很奇怪,实际也没有解决存在的问题,请问你明白他回复的意思了么?后面是怎么解决这个问题的呢?

好问题,其实我至今也不懂

直接用其实我没发现任何问题包括守卫都是正常的

其实他的说法也没说错
我强调了“loading”页,所以他认为我把另一页加载到了 loadingComponent 中,而这里是完全脱离整个vue-router的控制,这是不符合他们预期的

事实上这并不是我们想要的真正的页面,只是一个loading的图标或者组件而已,认为它不是一个页面完全没问题(只是一个大一点的组件而已)。

我出现这个问题的原因在于,我开发的时候,异步加载新的页面(也就是今天刚打开这个项目),它需要编译,得等一会儿我很不爽。或者是build上去服务器之后,服务器被第一次访问,访问人感觉点了没反应很不爽。

解决方法一:下狠手,直接无视警告

解决方法一:用 beforeEachafterEach 记住你正在加载的页面,好,例如说 beforeEach 准备跳转 main-page,迟迟等不到 afterEach 触发,好这时候用 pinia 或者 vuex 去触发这个全局的loading,然后等加载好了再取消loading

解决方法二:我曾经看过一个人用一个vue页面,用选项式写法,把 defineAsyncComponent 写在 script 中,也就是说,将loading页和实际页面,都当成这个空白页的组件(能理解我意思吗?),空白页面是一瞬间加载完的,所以通过了vue-router的检测

泪目~~ 有人和我想法一样

@Autumn-one
Copy link

Thank you for your reply↳↳

我看了很多相关的解释,理由都很奇怪,实际也没有解决存在的问题,请问你明白他回复的意思了么?后面是怎么解决这个问题的呢?

好问题,其实我至今也不懂

直接用其实我没发现任何问题包括守卫都是正常的

其实他的说法也没说错 我强调了“loading”页,所以他认为我把另一页加载到了 loadingComponent 中,而这里是完全脱离整个vue-router的控制,这是不符合他们预期的

事实上这并不是我们想要的真正的页面,只是一个loading的图标或者组件而已,认为它不是一个页面完全没问题(只是一个大一点的组件而已)。

我出现这个问题的原因在于,我开发的时候,异步加载新的页面(也就是今天刚打开这个项目),它需要编译,得等一会儿我很不爽。或者是build上去服务器之后,服务器被第一次访问,访问人感觉点了没反应很不爽。

解决方法一:下狠手,直接无视警告

解决方法一:用 beforeEachafterEach 记住你正在加载的页面,好,例如说 beforeEach 准备跳转 main-page,迟迟等不到 afterEach 触发,好这时候用 pinia 或者 vuex 去触发这个全局的loading,然后等加载好了再取消loading

解决方法二:我曾经看过一个人用一个vue页面,用选项式写法,把 defineAsyncComponent 写在 script 中,也就是说,将loading页和实际页面,都当成这个空白页的组件(能理解我意思吗?),空白页面是一瞬间加载完的,所以通过了vue-router的检测

泪目~~ 有人和我想法一样

我认为 defineAsyncComponent 是一个很好的方法,还能设置超时和错误页面,是一个低成本的实现,所以我浪费了几个小时搜索相关问题之后还是觉得继续使用这个方法。
但是我不得不说一下这个vue-router的开发者,不加掩饰的说这人多少有点毛病,我在提出这个问题之前充分搜索了相关的内容,overflow 论坛 issues 文档等相关的回答都看了一遍,看到基本都是对这个所谓官方人员话语的引用,没有任何比较符合问题的解释,当然我也在discord中提出了自己的疑问,当时他看见之后并没有理会,然后我提了一个相关的issues,他直接给关了!???我还顺便看了一下其他的issues,看到的回复基本就是一句话,这还能在敷衍一点么?真TM SB。这个货在官方文档只写了一句话,这不等于在说我不推荐你这样用!至于为什么你的自己猜!我也不告诉你们是不是有更好的实现方式。WTF!还有vue-router的官方文档中对 router-view添加动画的示例代码在最新的vue项目里面竟然也是不能使用的,这个货再用脚做项目么?因为这个LG项目的存在我想我都要考虑其他的技术选型了。

@Yiximail
Copy link
Author

Thank you for your reply↳↳

我看了很多相关的解释,理由都很奇怪,实际也没有解决存在的问题,请问你明白他回复的意思了么?后面是怎么解决这个问题的呢?

好问题,其实我至今也不懂
直接用其实我没发现任何问题包括守卫都是正常的
其实他的说法也没说错 我强调了“loading”页,所以他认为我把另一页加载到了 loadingComponent 中,而这里是完全脱离整个vue-router的控制,这是不符合他们预期的
事实上这并不是我们想要的真正的页面,只是一个loading的图标或者组件而已,认为它不是一个页面完全没问题(只是一个大一点的组件而已)。
我出现这个问题的原因在于,我开发的时候,异步加载新的页面(也就是今天刚打开这个项目),它需要编译,得等一会儿我很不爽。或者是build上去服务器之后,服务器被第一次访问,访问人感觉点了没反应很不爽。
解决方法一:下狠手,直接无视警告
解决方法一:用 beforeEachafterEach 记住你正在加载的页面,好,例如说 beforeEach 准备跳转 main-page,迟迟等不到 afterEach 触发,好这时候用 pinia 或者 vuex 去触发这个全局的loading,然后等加载好了再取消loading
解决方法二:我曾经看过一个人用一个vue页面,用选项式写法,把 defineAsyncComponent 写在 script 中,也就是说,将loading页和实际页面,都当成这个空白页的组件(能理解我意思吗?),空白页面是一瞬间加载完的,所以通过了vue-router的检测
泪目~~ 有人和我想法一样

我认为 defineAsyncComponent 是一个很好的方法,还能设置超时和错误页面,是一个低成本的实现,所以我浪费了几个小时搜索相关问题之后还是觉得继续使用这个方法。 但是我不得不说一下这个vue-router的开发者,不加掩饰的说这人多少有点毛病,我在提出这个问题之前充分搜索了相关的内容,overflow 论坛 issues 文档等相关的回答都看了一遍,看到基本都是对这个所谓官方人员话语的引用,没有任何比较符合问题的解释,当然我也在discord中提出了自己的疑问,当时他看见之后并没有理会,然后我提了一个相关的issues,他直接给关了!???我还顺便看了一下其他的issues,看到的回复基本就是一句话,这还能在敷衍一点么?真TM SB。这个货在官方文档只写了一句话,这不等于在说我不推荐你这样用!至于为什么你的自己猜!我也不告诉你们是不是有更好的实现方式。WTF!还有vue-router的官方文档中对 router-view添加动画的示例代码在最新的vue项目里面竟然也是不能使用的,这个货再用脚做项目么?因为这个LG项目的存在我想我都要考虑其他的技术选型了。

没必要生气,你看我就回了一句话,实际上我那时候花了两天去找,实在找不到才来github的。

@Autumn-one
Copy link

Thank you for your reply↳↳

我看了很多相关的解释,理由都很奇怪,实际也没有解决存在的问题,请问你明白他回复的意思了么?后面是怎么解决这个问题的呢?

好问题,其实我至今也不懂
直接用其实我没发现任何问题包括守卫都是正常的
其实他的说法也没说错 我强调了“loading”页,所以他认为我把另一页加载到了 loadingComponent 中,而这里是完全脱离整个vue-router的控制,这是不符合他们预期的
事实上这并不是我们想要的真正的页面,只是一个loading的图标或者组件而已,认为它不是一个页面完全没问题(只是一个大一点的组件而已)。
我出现这个问题的原因在于,我开发的时候,异步加载新的页面(也就是今天刚打开这个项目),它需要编译,得等一会儿我很不爽。或者是build上去服务器之后,服务器被第一次访问,访问人感觉点了没反应很不爽。
解决方法一:下狠手,直接无视警告
解决方法一:用 beforeEachafterEach 记住你正在加载的页面,好,例如说 beforeEach 准备跳转 main-page,迟迟等不到 afterEach 触发,好这时候用 pinia 或者 vuex 去触发这个全局的loading,然后等加载好了再取消loading
解决方法二:我曾经看过一个人用一个vue页面,用选项式写法,把 defineAsyncComponent 写在 script 中,也就是说,将loading页和实际页面,都当成这个空白页的组件(能理解我意思吗?),空白页面是一瞬间加载完的,所以通过了vue-router的检测
泪目~~ 有人和我想法一样

我认为 defineAsyncComponent 是一个很好的方法,还能设置超时和错误页面,是一个低成本的实现,所以我浪费了几个小时搜索相关问题之后还是觉得继续使用这个方法。 但是我不得不说一下这个vue-router的开发者,不加掩饰的说这人多少有点毛病,我在提出这个问题之前充分搜索了相关的内容,overflow 论坛 issues 文档等相关的回答都看了一遍,看到基本都是对这个所谓官方人员话语的引用,没有任何比较符合问题的解释,当然我也在discord中提出了自己的疑问,当时他看见之后并没有理会,然后我提了一个相关的issues,他直接给关了!???我还顺便看了一下其他的issues,看到的回复基本就是一句话,这还能在敷衍一点么?真TM SB。这个货在官方文档只写了一句话,这不等于在说我不推荐你这样用!至于为什么你的自己猜!我也不告诉你们是不是有更好的实现方式。WTF!还有vue-router的官方文档中对 router-view添加动画的示例代码在最新的vue项目里面竟然也是不能使用的,这个货再用脚做项目么?因为这个LG项目的存在我想我都要考虑其他的技术选型了。

没必要生气,你看我就回了一句话,实际上我那时候花了两天去找,实在找不到才来github的。

今天看了一下源码,发现使用defineAsyncComponent警告的时候会根据一个属性判断是否重复打印,通过这个点可以取消warn的打印,代码如下:

function transform_routes_async ( routes: any[] )
{
    routes.forEach( item =>
    {
        if ( item.component ) {
            item.component = defineAsyncComponent( {
                loader: item.component,
                loadingComponent: PageLoading,
                delay: 100,
                errorComponent: PageError,
                timeout: 30000
            } );
            // 加上这一句就好了
            item.component.__warnedDefineAsync = true
        }
        if ( item.children ) {
            transform_routes_async( item.children );
        }
    } );
}

不过在深入使用 vue3 之后我还发现了其他很多的问题,我有一些完美主义的想法,而vue3的某些设计还是欠考虑,对vue官方提的一些 issues 之后得到的反馈也非常失望,后面如果有公司级新项目可能对vue3的倾向就不大了

@lb1129
Copy link

lb1129 commented Jun 14, 2023

define your lazyLoad:

import { defineComponent, ref, h, markRaw, type VNode } from 'vue'
export const lazyLoad = (directory: string, fileName: string) =>
  defineComponent({
    name: fileName,
    setup(props) {
      const comp = ref<VNode>()
      import(`@/views/${directory}/${fileName}.vue`).then((res) => {
        comp.value = markRaw<VNode>(res.default)
      })
      return () => (comp.value ? h(comp.value, props) : h(PageLoading))
    }
  })

in route :

import { type RouteRecordRaw } from 'vue-router'
const routers: RouteRecordRaw[] = [{
    path: '/login',
    name: 'Login',
    component: lazyLoad('authenticate', 'Login')
  }]

@catpigdog
Copy link

What problem does this feature solve?

我阅读过 #627#682 问题。 I've read read about issues #627 and #682.

问题 #627 是关于: The issue #627 is about:

createRouter({
  routes: [{
    path: '/',
    component: defineAsyncComponent(() => import('...'))
  }]
})

它没必要使用到 defineAsyncComponent, 所以它应该写成 It is not necessary to use defineAsyncComponent, so it should become

createRouter({
  routes: [{
    path: '/',
    component: () => import('...')
  }]
})

我理解并且十分同意这样改变 I understand and fully agree with this change.

但是根据 defineasynccomponent 的文档 和 Async Components,它其实是可以接收其他参数。 However, according to defineasynccomponent API and Async Components. Actually, it can receive other parameters.

interface AsyncComponentOptions {
  loader: AsyncComponentLoader
  loadingComponent?: Component
  errorComponent?: Component
  delay?: number
  timeout?: number
  suspensible?: boolean
  onError?: (
    error: Error,
    retry: () => void,
    fail: () => void,
    attempts: number
  ) => any
}

所以我认为不应该只要使用了 defineAsyncComponent 就弹出警告。 So I don't think it should get a warning when use defineAsyncComponent by receive an object.

在我使用场景中,我需要一个加载页来等待下一个页面准备好或者等待依赖的chunk下载好,所以我这样写了。 In my usage scenario. I need a loading page to wait for the next page to be ready. Or just wait for the dependent chunks to download. So I write it like this.

import LoadingPage from "..."
createRouter({
  routes: [{
    path: '/',
    component: defineAsyncComponent({
      loader: () => import('...'),
      loadingComponent: LoadingPage
    })
  }]
})

它能够实现我的需求,但是收到了一个来自 #682 修改的警告。 And it works for my needs. But I received a warning from #682 as well.

[Vue Router warn]:
Component "default" in record with path "/" is defined using "defineAsyncComponent()".
Write "() => import('./MyPage.vue')" instead of "defineAsyncComponent(() => import('./MyPage.vue'))".

我不太确定为什么不应该使用 defineAsyncComponent,是我忽略了什么吗?或者使用它会产生什么问题? I'm not sure why should not use defineAsyncComponent. Is it something I ignored? Or what problems will arise from using it.

哈哈,没想到也有和我一模一样的疑问出现,一直在寻找路由懒加载时展示Loading状态,最后自己琢磨了下使用defineAsyncComponent{ loadingComponent } 但又苦于会出现警告😅

@catpigdog
Copy link

之前的版本我是用的beforeEach和afterEach组合来显示全局Loading状态

@hviet17
Copy link

hviet17 commented Feb 2, 2024

After further digging to understand this "issue", I have found some old docs that shed more light to his answer.

Vue Router supports a similar mechanism for asynchronously loading route components, known as lazy loading. Despite the similarities, this feature is distinct from Vue's support for async components. You should not use defineAsyncComponent when configuring route components with Vue Router. You can read more about this in the Lazy Loading Routes section of the Vue Router documentation.

Note that when used as a route component in vue-router, these properties will be ignored because async components are resolved upfront before the route navigation happens. You also need to use vue-router 2.4.0+ if you wish to use the above syntax for route components.

Essentially, vue-router already support async component (they call it lazy loading) with dynamic import so defineAsyncComponent is redundant . Furthermore, vue-router will wait for the promise of component to resolve before navigating to the new route, so any loadingComponent or errorComponent that you defined in defineAsyncComponent will be ignored.
Hope someone found it helpful.

@Yiximail
Copy link
Author

Yiximail commented Feb 3, 2024

After further digging to understand this "issue", I have found some old docs that shed more light to his answer.

Vue Router supports a similar mechanism for asynchronously loading route components, known as lazy loading. Despite the similarities, this feature is distinct from Vue's support for async components. You should not use defineAsyncComponent when configuring route components with Vue Router. You can read more about this in the Lazy Loading Routes section of the Vue Router documentation.

Note that when used as a route component in vue-router, these properties will be ignored because async components are resolved upfront before the route navigation happens. You also need to use vue-router 2.4.0+ if you wish to use the above syntax for route components.

Essentially, vue-router already support async component (they call it lazy loading) with dynamic import so defineAsyncComponent is redundant . Furthermore, vue-router will wait for the promise of component to resolve before navigating to the new route, so any loadingComponent or errorComponent that you defined in defineAsyncComponent will be ignored. Hope someone found it helpful.

It makes sense, I always use dynamic import.
However, I want to show a loading component or icon while the vue-router waits for the new route instead of staying in the old route and not giving the user any feedback.
This waiting process is very fast when there is a browser cache, but the first time a user enters a new page, there will be some delay ( Like clicking on the link and nothing happening for a few seconds before the navigation ).

I know this mostly happens when the vue page is too large, but we can't ignore the fact that sometimes the users have a poor network.

@hviet17
Copy link

hviet17 commented Feb 3, 2024

@Yiximail yes, it'd be nice if vue-router support it natively. Right now you can achieve the same thing with a combination of beforeEach/beforeResolve navigation guards & global event emitter / EventBus.
Doing it this way can be more flexible with meta tag & also you don't have to repeat defining loadingComponent in each route.

router.beforeEach((to) => {
    emitter.emit('asyncRouteLoading')
    return true
})

router.beforeResolve((to) => {
    emitter.emit('asyncRouteLoaded') //stop loading
    return true
})

& in loadingComponent

onMounted(() => {
    emitter.on("asyncRouteLoading", startLoading)
    emitter.on("asyncRouteLoaded", stopLoading)
})

@ny-fenitra
Copy link

ny-fenitra commented Feb 7, 2024

@Yiximail I have the same issue as you i.e. I want to show a loading component while the vue-router waits for the new lazy loaded route.

Actually, with the combination of defineasynccomponent and the loader of suspense it works well.

@hviet17 @posva So if it is just a matter of redundancy, is it ok to use defineasynccomponent with route components with the benefit of having a loading state already in place with suspense ?

@anthonyboutinov
Copy link

Turns out, there's a very easy way to have a loading screen while different route components are being downloaded:

// App.vue

<script setup>
// imports...

const router = useRouter();
const isLoading =ref(false);
router.beforeEach(() => {
  isLoading.value = true;
});
router.afterEach(() => {
  isLoading.value = false;
});
</script>

<template>
...
    <!-- Main content -->
    <LoadingScreen v-if="isLoading" /><!-- LoadingScreen is just another component that you define -->
    <router-view v-else v-slot="{ Component }">
      <component :is="Component" />
    </router-view>
...
</template>

@mys1024
Copy link

mys1024 commented Jan 11, 2025

Thanks to anthonyboutinov's comment, I found a good way to add transitions to route components and the loading screen:

<script setup>
// imports...

const router = useRouter();
const isLoading =ref(false);

router.beforeEach(() => {
  isLoading.value = true;
});

router.afterEach(() => {
  isLoading.value = false;
});
</script>

<template>
  <RouterView v-slot="{ Component }">
    <Transition name="fade" mode="out-in">
      <KeepAlive>
        <component :is="isLoading ? LoadingScreen : Component" />
      </KeepAlive>
    </Transition>
  </RouterView>
</template>

<style scoped>
.fade-enter-active,
.fade-leave-active {
  transition: opacity 0.2s ease;
}

.fade-enter-from,
.fade-leave-to {
  opacity: 0;
}
</style>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants