我使用 webpack 的代码分割功能将我的应用程序分割成多个块,这样当用户访问我的网页时就不会下载整个应用程序包。
某些路由所需的块可能相当大,并且可能需要花费大量时间来下载。这很好,除了用户在单击内部链接时不知道页面实际上正在加载,因此我需要以某种方式显示加载动画或其他内容。
我的路由器配置如下:
[
{
path: '/',
component: () => import(/* webpackChunkName: 'landing' */ './landing.vue'),
},
{
path: '/foo',
component: () => import(/* webpackChunkName: 'main' */ './foo.vue'),
},
{
path: '/bar',
component: () => import(/* webpackChunkName: 'main' */ './bar.vue'),
},
]
Vue.js 指南中的高级异步组件展示了如何在解析组件时显示特定的“正在加载”组件——这正是我所需要的,但它也说:
请注意,当在 vue-router 中用作路由组件时,这些属性将被忽略,因为异步组件在路由导航发生之前就已预先解析。
如何在 vue-router 中实现这一目标?如果这是不可能的,那么延迟加载的组件对我来说几乎毫无用处,因为它会给用户提供糟糕的体验。
您可以使用导航防护来激活/停用显示/隐藏加载组件的加载状态:
如果你想使用“nprogress”之类的东西,你可以这样做:
http://jsfiddle.net/xgrjzsup/2669/
const router = new VueRouter({
routes
})
router.beforeEach((to, from, next) => {
NProgress.start()
next()
})
router.afterEach(() => {
NProgress.done()
})
或者,如果您想就地显示某些内容:
http://jsfiddle.net/h4x8ebye/1/
Vue.component('loading',{ template: '<div>Loading!</div>'})
const router = new VueRouter({
routes
})
const app = new Vue({
data: { loading: false },
router
}).$mount('#app')
router.beforeEach((to, from, next) => {
app.loading = true
next()
})
router.afterEach(() => {
setTimeout(() => app.loading = false, 1500) // timeout for demo purposes
})
然后在模板中:
<loading v-if="$root.loading"></loading>
<router-view v-else></router-view>
这也可以很容易地封装在一个非常小的组件中,而不是使用
$root
组件来表示加载状态。
无论如何,我将分享我最终针对我的情况所做的事情。
我使用的是 Vuex,因此很容易创建任何组件都可以访问的应用程序范围的“加载”状态,但您可以使用任何想要共享此状态的机制。
简单来说,它的工作原理如下:
function componentLoader(store, fn) {
return () => {
// (Vuex) Loading begins now
store.commit('LOADING_BAR_TASK_BEGIN');
// (Vuex) Call when loading is done
const done = () => store.commit('LOADING_BAR_TASK_END');
const promise = fn();
promise.then(done, done);
return promise;
};
}
function createRoutes(store) {
const load = fn => componentLoader(store, fn);
return [
{
path: '/foo',
component: load(() => import('./components/foo.vue')),
},
{
path: '/bar',
component: load(() => import('./components/bar.vue')),
},
];
}
所以我所要做的就是用我的
() => import()
函数包装每个 load()
,该函数负责设置加载状态。加载是通过直接观察 Promise 来确定的,而不是依赖于特定于路由器的 before/after hooks。
您可以在 vuejs 的一个项目中使用此代码进行 gobel 路由
const router = new Router({
routes: [
{ path: '/', name: 'home', component: Home },
{ path: '/about', name: 'about', component: About }
]
})
router.beforeResolve((to, from, next) => {
// If this isn't an initial page load.
if (to.name) {
// Start the route progress bar.
NProgress.start()
}
next()
})
router.afterEach((to, from) => {
// Complete the animation of the route progress bar.
NProgress.done()
})
如果您在任何页面(甚至是嵌套页面)的一个位置显示加载指示器,则接受的答案是一个很好的解决方案。
但是,如果有人正在寻找一种方法来等待
RouterView
完成延迟加载组件并显示一些加载指示器,这里是您可以使用的代码。
<script setup lang="ts">
import Spinner from '@lib/components/spinner/Spinner.vue';
import { routerViewWithSuspenseService } from './router-view-with-suspense';
routerViewWithSuspenseService.register();
const { isCurrentIndexLoading } = routerViewWithSuspenseService.use();
</script>
<template>
<template v-if="isCurrentIndexLoading">
<slot>
<div class="grid size-full place-items-center">
<Spinner class="size-10"></Spinner>
</div>
</slot>
</template>
<RouterView v-else></RouterView>
</template>
import { useTimeoutFn } from '@vueuse/core';
import { computed, shallowRef } from 'vue';
import { useRouter } from 'vue-router';
function createRouterViewWithSuspenseService() {
const registered = shallowRef(false);
const index = shallowRef(0);
const isLoading = shallowRef(false);
const { start, stop } = useTimeoutFn(() => (isLoading.value = true), 100, { immediate: false });
function register() {
if (registered.value) {
return;
}
registered.value = true;
const router = useRouter();
router.beforeEach(() => {
start();
});
router.afterEach(() => {
stop();
isLoading.value = false;
});
}
function use() {
index.value += 1;
const currentIndex = index.value;
return {
isCurrentIndexLoading: computed(() => currentIndex === index.value && isLoading.value),
};
}
return {
register,
use,
};
}
export const routerViewWithSuspenseService = createRouterViewWithSuspenseService();
基本上我用
RouterView
替换了所有 RouterViewWithSuspense
代码,你就可以显示加载指示器了。
希望我能帮别人解决很多头痛问题:D
附注我仍然不确定为什么原生
Suspense
不能很好地与 vue-router 一起使用的动态组件配合使用。