首页 > 解决方案 > Vue js 预取组件

问题描述

我最近了解了延迟加载组件并开始使用它。现在我正在尝试预取延迟加载的组件以及vue-router路由。但是使用 chrome devtools 我发现延迟加载的块仅在我们实际导航到延迟加载的路由(在 vue-router 路由的情况下)或在v-if评估到true并渲染组件时(在延迟加载的情况下)加载零件)。

我也尝试过webpackPrefetch: true在路由器中使用魔术字符串以及组件导入语句,但这样做似乎没有任何区别。

项目结构:
主从布局

路由器配置:

import Vue from "vue";
import Router from "vue-router";
Vue.use(Router);

var routes = [  
  {
    path: "/DetailPage",
    component: () => import(/* webpackChunkName: "Detail-chunk" */ "AppModules/views/MyModuleName/DetailPage.vue")
  },
  {
    path: "/MasterPage",
    component: () => import("AppModules/views/MyModuleName/MasterPage.vue")
  }
]

export const router = new Router({
  routes: routes,
  stringifyQuery(query) {
    // encrypt query string here
  }
});

export default router;

主视图:

<template> 
  <div @click="navigate">
    Some text
  </div>
</template>

<script>
export default {
  name: "MasterPage",
  methods: {
    navigate() {
      this.$router.push({
        path: "/DetailPage",
        query: {},
      });
    },
  },
};
</script>

详情页面:

<template>
  <div>    
    <my-component v-if="showComponent" />
    <div @click="showComponent = true">Show Component</div>
  </div>
</template>

<script>
const MyComponent = () => import(/* webpackChunkName: "MyComponent-chunk" */ "AppCore/components/AppElements/Helpers/MyComponent");
export default {
  name: "DetailPage",
  components: {
    MyComponent,
  },
  data() {
    return {
      showComponent: false
    }
  }
};
</script>

vue.js.config 文件:

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")
      }
    }
  }
};

异步路由和组件都被拆分为单独的块,但这些块不是预取的。
当我导航到主视图时,我没有Detail-chunk.[hash].js在网络选项卡中看到。只有在执行母版页中的方法时才会请求它navigate(这是正确的延迟加载行为,没有预取)。
现在,当我在详细信息页面上时,MyComponent-chunk.[hash].js仅在showComponent变为true(单击按钮时)
我还在一些地方读到vue-cliv3 确实默认启用了预取功能并且不需要 webpack 魔术字符串时才请求。我也通过删除webpackPrefetch评论来尝试过,但没有任何区别。

我做了vue-cli-service inspect,发现预取插件确实存在于 webpack 配置中:

 /* config.plugin('preload') */
    new PreloadPlugin(
      {
        rel: 'preload',
        include: 'initial',
        fileBlacklist: [
          /\.map$/,
          /hot-update\.js$/
        ]
      }
    ),
    /* config.plugin('prefetch') */
    new PreloadPlugin(
      {
        rel: 'prefetch',
        include: 'asyncChunks'
      }
    ),

更新:我尝试使用 webpack 魔术注释删除预取 webpack 插件config.plugins.delete('prefetch');,然后使用 webpack 魔术注释:/* webpackPrefetch: true */但没有任何区别。

如何实现预取功能?

标签: javascriptvue.jswebpackcode-splitting

解决方案


我通过创建一个在自定义时间后加载的简单预取组件解决了这个问题。

预取.vue

<script>
import LazyComp1 from "./LazyComp1.vue";
import LazyComp2 from "./LazyComp2.vue";
export default {
    components:{
        LazyComp1,
        LazyComp2,
    }
}
</script>

应用程序.vue

<template>
    <Prefech v-if="loadPrefetch"></Prefech>
</template>
<script>
export default {
    components: {
        Prefech: () => import("./Prefetch");
    },
    data() {
        return {
            loadPrefetch: false
        }
    },
    mounted() {
        setTimeout(() => {
            this.loadPrefetch = true;
        }, 1000);
    }
}
</script>

推荐阅读