Vue Router loads different component implementations based on background data

  • 2021-11-10 08:48:14
  • OfStack

Catalog requirements encountered in actual projects
There are 1 bad ways to implement it
A better way to realize it personally
The function has been realized, but I have started a new thinking
Final solution-higher-order components

Requirements encountered in actual projects

The same link needs to load different page components. According to the different services purchased by users, there are different pages.

There are 1 bad ways to implement it

Write these components directly under the same component, and judge by v-if. If you do this, you can even write all the components in one file without using vue-router, and all of them are judged by v-if, which is also feasible. (The premise is that tens of thousands of lines of code start from 1, if you don't bother) When rendering this link, directly request the background data and render different links through the data. (It works in theory, but if the user doesn't use this function, these links take the background data in advance every time; In addition, if the user knows the link and accesses the link directly, it still needs logic to judge which page the user should see.) By calling router. beforeEach, each route is intercepted. When the route is our specified route, the background data is requested and the page is dynamically jumped. (The function can be completed, but in fact, this is only a small function of the whole system, and should not invade the whole routing system. If every business page is written in the global routing system, the routing logic will be too complicated.)

A better way to realize it personally

Get the server data at the place where the route is configured and dynamically load the corresponding components


{
  path: 'shopKPI',
  //  If the background data is stored in advance store Inside, visit here store The data can be judged directly 
  //  However, the data of this specific business page is placed globally store I don't need it anywhere else. It's really unnecessary 
  component: () => import('@/views/store/dataVersion'),
  name: 'store_KPI',
  menuName: ' Shop staff ',
  meta: {
    codes: ['storeProduct.detail']
  }
}

Ideally, the reality is that this method received by component must return 1 promise synchronously.

At this time, I thought of the above bad implementation method 1, and slightly modified it


<!-- ChooseShopKPI.vue -->
<template>
  <dataVersion v-if="!useNewShopKPI" />
  <ShopKPI v-else />
</template>

<script>
import { get } from 'lodash';
import { getStoreReportFormVersion } from '@/api/store';
import dataVersion from './dataVersion';
import ShopKPI from './ShopKPI';

export default {
  name: 'ChooseShopKPI',

  components: {
    dataVersion,
    ShopKPI,
  },

  data() {
    return { useNewShopKPI: false };
  },

  created() {
    getStoreReportFormVersion().then((res) => {
      if (get(res, 'data.data.new')) {
        this.useNewShopKPI = true;
      }
    });
  },
};
</script>

<style lang="css" scoped></style>

Render the middle page ChooseShopKPI instead of rendering the corresponding page of the route


{
  path: 'shopKPI',
  //  If you get the background data in advance, access it here store The data can be judged directly 
  //  However, the data of this specific business page is placed globally store I don't need it anywhere else. It's really unnecessary 
-  component: () => import('@/views/store/dataVersion'),
+  component: () => import('@/views/store/ChooseShopKPI'),
  name: 'store_KPI',
  menuName: ' Shop staff ',
  meta: {
    codes: ['storeProduct.detail']
  }
}

In this way, we achieve the desired function.

The function has been realized, but I have started a new thinking

Although this method solves the problem of dynamically loading page components. But there are also some minor problems.

If such pages that load data through the server are added later, there will be multiple intermediate pages of ChooseXXX. This kind of intermediate page is actually routed twice. Developers who are not familiar with logic may not know the page jump logic inside, which increases the understanding cost.

Final solution-higher-order components

By abstracting ChooseXXX, it is transformed into DynamicLoadComponent


<!-- DynamicLoadComponent.vue -->
<template>
  <component :is="comp"  />
</template>

<script>
export default {
  name: 'DynamicLoadComponent',
  props: {
    renderComponent: {
      type: Promise,
    },
  },
  data() {
    return {
      comp: () => this.renderComponent
    }
  },
  mounted() {},
};
</script>

<style lang="css" scoped></style>

Get the background data directly in the configuration of the route and distribute the route. In this way, the routing logic is concentrated in the routing configuration file, and there is no secondary routing. Maintain it without headache and brain swelling.

The DynamicLoadComponent component can also be reused, and the routing configuration for judging the background data loading page can be directed to this intermediate component.


{
  path: 'shopKPI',
  component: () => import('@/views/store/components/DynamicLoadComponent'),
  name: 'store_KPI',
  menuName: ' Shop staff ',
  meta: {
    codes: ['storeProduct:detail'],
  },
  props: (route) => ({
    renderComponent: new Promise((resolve, reject) => {
      getStoreReportFormVersion()
        .then((responseData) => {
          const useNewShopKPI = get(responseData, 'data.data.shop_do');
          const useOldShopKPI = get(
            responseData,
            'data.data.store_data_show'
          );

          if (useNewShopKPI) {
            resolve(import('@/views/store/ShopKPI'));
          } else if (useOldShopKPI) {
            resolve(import('@/views/store/dataVersion'));
          } else {
            resolve(import('@/views/store/ShopKPI/NoKPIService'));
          }
        })
        .catch(reject);
    }),
  })
}

View online small examples (only chrome is supported)
https://stackblitz.com/edit/vuejs-starter-jsefwq?file=index.js


Related articles: