admin管理员组文章数量:1277894
I recently learnt about lazy loading ponents and started using it. Now I am trying to prefetch the lazy loaded ponents as well as vue-router
routes. But using the chrome devtools I found that lazy loaded chunks are only loaded when we actually navigate to the lazy loaded route (in case of a vue-router route) or when the v-if
evaluates to true
and the ponent is rendered (in case of a lazy loaded ponent).
I have also tried using the webpackPrefetch: true
magic string in the router as well as ponent import statement but doing that does not seem to make any difference.
Project structure:
Master-Detail layout
router config:
import Vue from "vue";
import Router from "vue-router";
Vue.use(Router);
var routes = [
{
path: "/DetailPage",
ponent: () => import(/* webpackChunkName: "Detail-chunk" */ "AppModules/views/MyModuleName/DetailPage.vue")
},
{
path: "/MasterPage",
ponent: () => import("AppModules/views/MyModuleName/MasterPage.vue")
}
]
export const router = new Router({
routes: routes,
stringifyQuery(query) {
// encrypt query string here
}
});
export default router;
Master view:
<template>
<div @click="navigate">
Some text
</div>
</template>
<script>
export default {
name: "MasterPage",
methods: {
navigate() {
this.$router.push({
path: "/DetailPage",
query: {},
});
},
},
};
</script>
Details page:
<template>
<div>
<my-ponent v-if="showComponent" />
<div @click="showComponent = true">Show Component</div>
</div>
</template>
<script>
const MyComponent = () => import(/* webpackChunkName: "MyComponent-chunk" */ "AppCore/ponents/AppElements/Helpers/MyComponent");
export default {
name: "DetailPage",
ponents: {
MyComponent,
},
data() {
return {
showComponent: false
}
}
};
</script>
vue.js.config file:
const path = require("path");
const BundleAnalyzerPlugin = require("webpack-bundle-analyzer")
.BundleAnalyzerPlugin;
module.exports = {
publicPath: "some-url",
outputDir: "./some/path",
chainWebpack: webapckConfig => {
webapckConfig.plugin("html").tap(() => {
return [
{
inject: true,
filename: "index.html",
template: "./public/index.html"
}
];
});
},
productionSourceMap: true,
configureWebpack: {
plugins: [
new BundleAnalyzerPlugin({
analyzerMode: "server",
generateStatsFile: false,
statsOptions: {
excludeModules: "node_modules"
}
})
],
output: {
filename: "some file name",
libraryTarget: "window"
},
module: {
rules: [
{
test: /\.(woff|woff2)(\?v=\d+\.\d+\.\d+)?$/,
use: [
{
loader: "url-loader",
options: {
limit: 50000,
fallback: "file-loader",
outputPath: "/assets/fonts",
name: "[name].[ext]?hash=[hash]"
}
}
]
}
]
},
resolve: {
alias: {
vue$: process.env.NODE_ENV == 'production' ? 'vue/dist/vue.min.js' : 'vue/dist/vue.js',
AppCore: path.resolve(__dirname, "..", "..", "AppCoreLite"),
AppModules: path.resolve(__dirname, "..", "..", "AppModulesLite")
}
}
}
};
Both the async route and ponent do get split into separate chunks but these chunks are not prefetched.
When I navigate to the master view, I dont see Detail-chunk.[hash].js
in the network tab. It gets requested only when the navigate
method in the master page is executed (this the correct lazy load behaviour without prefetch).
Now when I am on the details page, MyComponent-chunk.[hash].js
is only requested when the showComponent
bees true
(on click of a button)
I've also read at a few places that vue-cli
v3 does has prefetch functionality enabled by default and webpack magic string is not needed. I also tried that by removing the webpackPrefetch
ment but it made no difference.
I did vue-cli-service inspect
and found that prefetch plugin is indeed present in the webpack config:
/* config.plugin('preload') */
new PreloadPlugin(
{
rel: 'preload',
include: 'initial',
fileBlacklist: [
/\.map$/,
/hot-update\.js$/
]
}
),
/* config.plugin('prefetch') */
new PreloadPlugin(
{
rel: 'prefetch',
include: 'asyncChunks'
}
),
UPDATE: I tried removing the prefetch webpack plugin using config.plugins.delete('prefetch');
and then using the webpack magic ment: /* webpackPrefetch: true */
but it made no difference.
How do I implement prefetch functionality?
I recently learnt about lazy loading ponents and started using it. Now I am trying to prefetch the lazy loaded ponents as well as vue-router
routes. But using the chrome devtools I found that lazy loaded chunks are only loaded when we actually navigate to the lazy loaded route (in case of a vue-router route) or when the v-if
evaluates to true
and the ponent is rendered (in case of a lazy loaded ponent).
I have also tried using the webpackPrefetch: true
magic string in the router as well as ponent import statement but doing that does not seem to make any difference.
Project structure:
Master-Detail layout
router config:
import Vue from "vue";
import Router from "vue-router";
Vue.use(Router);
var routes = [
{
path: "/DetailPage",
ponent: () => import(/* webpackChunkName: "Detail-chunk" */ "AppModules/views/MyModuleName/DetailPage.vue")
},
{
path: "/MasterPage",
ponent: () => import("AppModules/views/MyModuleName/MasterPage.vue")
}
]
export const router = new Router({
routes: routes,
stringifyQuery(query) {
// encrypt query string here
}
});
export default router;
Master view:
<template>
<div @click="navigate">
Some text
</div>
</template>
<script>
export default {
name: "MasterPage",
methods: {
navigate() {
this.$router.push({
path: "/DetailPage",
query: {},
});
},
},
};
</script>
Details page:
<template>
<div>
<my-ponent v-if="showComponent" />
<div @click="showComponent = true">Show Component</div>
</div>
</template>
<script>
const MyComponent = () => import(/* webpackChunkName: "MyComponent-chunk" */ "AppCore/ponents/AppElements/Helpers/MyComponent");
export default {
name: "DetailPage",
ponents: {
MyComponent,
},
data() {
return {
showComponent: false
}
}
};
</script>
vue.js.config file:
const path = require("path");
const BundleAnalyzerPlugin = require("webpack-bundle-analyzer")
.BundleAnalyzerPlugin;
module.exports = {
publicPath: "some-url",
outputDir: "./some/path",
chainWebpack: webapckConfig => {
webapckConfig.plugin("html").tap(() => {
return [
{
inject: true,
filename: "index.html",
template: "./public/index.html"
}
];
});
},
productionSourceMap: true,
configureWebpack: {
plugins: [
new BundleAnalyzerPlugin({
analyzerMode: "server",
generateStatsFile: false,
statsOptions: {
excludeModules: "node_modules"
}
})
],
output: {
filename: "some file name",
libraryTarget: "window"
},
module: {
rules: [
{
test: /\.(woff|woff2)(\?v=\d+\.\d+\.\d+)?$/,
use: [
{
loader: "url-loader",
options: {
limit: 50000,
fallback: "file-loader",
outputPath: "/assets/fonts",
name: "[name].[ext]?hash=[hash]"
}
}
]
}
]
},
resolve: {
alias: {
vue$: process.env.NODE_ENV == 'production' ? 'vue/dist/vue.min.js' : 'vue/dist/vue.js',
AppCore: path.resolve(__dirname, "..", "..", "AppCoreLite"),
AppModules: path.resolve(__dirname, "..", "..", "AppModulesLite")
}
}
}
};
Both the async route and ponent do get split into separate chunks but these chunks are not prefetched.
When I navigate to the master view, I dont see Detail-chunk.[hash].js
in the network tab. It gets requested only when the navigate
method in the master page is executed (this the correct lazy load behaviour without prefetch).
Now when I am on the details page, MyComponent-chunk.[hash].js
is only requested when the showComponent
bees true
(on click of a button)
I've also read at a few places that vue-cli
v3 does has prefetch functionality enabled by default and webpack magic string is not needed. I also tried that by removing the webpackPrefetch
ment but it made no difference.
I did vue-cli-service inspect
and found that prefetch plugin is indeed present in the webpack config:
/* config.plugin('preload') */
new PreloadPlugin(
{
rel: 'preload',
include: 'initial',
fileBlacklist: [
/\.map$/,
/hot-update\.js$/
]
}
),
/* config.plugin('prefetch') */
new PreloadPlugin(
{
rel: 'prefetch',
include: 'asyncChunks'
}
),
UPDATE: I tried removing the prefetch webpack plugin using config.plugins.delete('prefetch');
and then using the webpack magic ment: /* webpackPrefetch: true */
but it made no difference.
How do I implement prefetch functionality?
Share Improve this question edited Aug 26, 2020 at 14:40 ravi kumar asked Aug 21, 2020 at 8:27 ravi kumarravi kumar 1,6202 gold badges21 silver badges50 bronze badges 5- Is there any way you could recreate it inside code sandbox? – ColdHands Commented Aug 23, 2020 at 13:38
- @ColdHands I am not sure if I can do that. I was trying but could not get code-splitting to work in codesandbox. And I dont think this can be demonstrated without code splitting. – ravi kumar Commented Aug 26, 2020 at 14:22
- Have you tried to reproduce this on a fresh vue-cli project? I tried it with your examples and prefetch seems to be working correctly on the latest vue-cli. network tab with prefetch requests – Ricardo Tavares Commented Aug 29, 2020 at 18:35
- @RicardoTavares can you please share your vue.config.js, package.json and main.js? – ravi kumar Commented Aug 30, 2020 at 14:25
- 1 sure, here's a link to a repro github./rnunot/so-prefetch-demo – Ricardo Tavares Commented Aug 31, 2020 at 10:54
5 Answers
Reset to default 2I solved this by creating a simple prefetch ponent that loads after a custom amount of time.
Prefetch.vue
<script>
import LazyComp1 from "./LazyComp1.vue";
import LazyComp2 from "./LazyComp2.vue";
export default {
ponents:{
LazyComp1,
LazyComp2,
}
}
</script>
App.vue
<template>
<Prefech v-if="loadPrefetch"></Prefech>
</template>
<script>
export default {
ponents: {
Prefech: () => import("./Prefetch");
},
data() {
return {
loadPrefetch: false
}
},
mounted() {
setTimeout(() => {
this.loadPrefetch = true;
}, 1000);
}
}
</script>
since vue-router-prefetch
didn't work for me i ended up doing it manually.
Vue 3 Example - all routes are iterated on page load and async ponents are loaded
const router = createRouter({
history: createWebHistory(),
routes: [{
path: '/',
ponent: HomeView
}, {
path: '/about',
ponent: () => import('./views/AboutView.vue')
}]
});
async function preloadAsyncRoutes() {
// iterate all routes and if the ponent is async - prefetch it!
for (const route of router.getRoutes()) {
if (!route.ponents) continue;
// most routes have just a "default" ponent unless named views are used - iterate all entries just in case
for (const ponentOrImporter of Object.values(route.ponents)) {
if (typeof ponentOrImporter === 'function') {
try {
// prefetch the ponent and wait until it finishes before moving to the next one
await ponentOrImporter();
} catch (err) {
// ignore failing requests
}
}
}
}
}
window.addEventListener('load', preloadAsyncRoutes);
Lazy loaded ponents are meant to be loaded only when user clicks the route. If you want to load ponent before it, just don't use lazy loading.
vue-router
will load ponents to memory and swap the content of the tag dynamically even if you will use normally loaded ponent.
You need to implement vue-router-prefetch package for your need. Here is a working demo.
Note: From the working demo, you can notice from console.log
that only page 2
is prefetched by the QuickLink
ponent imported from vue-router-prefetch
Code :
import Vue from "vue";
import Router from "vue-router";
import RoutePrefetch from "vue-router-prefetch";
Vue.use(Router);
Vue.use(RoutePrefetch, {
ponentName: "QuickLink"
});
const SiteNav = {
template: `<div>
<ul>
<li>
<router-link to="/page/1">page 1</router-link>
</li>
<li>
<quick-link to="/page/2">page 2</quick-link>
</li>
<li>
<router-link to="/page/3">page 3</router-link>
</li>
</ul>
</div>`
};
const createPage = (id) => async() => {
console.log(`fetching page ${id}`);
return {
template: `<div>
<h1>page {id}</h1>
<SiteNav />
</div>`,
ponents: {
SiteNav
}
};
};
const routers = new Router({
mode: "history",
routes: [{
path: "/",
ponent: {
template: `<div>
<h1>hi</h1>
<SiteNav />
</div>`,
ponents: {
SiteNav
}
}
}]
});
for (let i = 1; i <= 3; i++) {
routers.addRoutes([{
path: `/page/${i + 1}`,
ponent: createPage(i + 1)
}]);
}
export default routers;
I'm working on a mobile app. and wanted to load some ponents dynamically while showing the splash screen.
@Thomas's answer is a good solution (a Prefetch ponent), but it doesn't load the ponent in the shadow dom, and Doesn't pass Vetur validation (each ponent must have its template)
Here's my code:
main.vue
<template>
<loader />
</template>
<script>
import Loader from './Loader'
const Prefetch = () => import('./Prefetch')
export default {
name: 'Main',
ponents: {
Loader,
Prefetch
}
}
</script>
Prevetch.vue
<template>
<div id="prefetch">
<lazy-p-a />
<lazy-p-b />
</div>
</template>
<script>
import Vue from 'vue'
import LazyCompA from './LazyCompA'
import LazyCompB from './LazyCompB'
Vue.use(LazyCompA)
Vue.use(LazyCompB)
export default {
ponents: {
LazyCompA,
LazyCompB
}
}
</script>
<style lang="scss" scoped>
#prefetch {
display: none !important;
}
</style>
The loader ponent is loaded & rendered, then the Prefetch ponent can load anything dynamically.
本文标签: javascriptVue js Prefetch componentsStack Overflow
版权声明:本文标题:javascript - Vue js Prefetch components - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1741231300a2362155.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论