全动态VUE路由器

时间:2019-01-10 17:01:47

标签: vue.js vue-router nuxt.js

我们正在基于Vue和Nuxt构建一个巨大的网站,具有超过25种不同的页面类型,这些页面类型无法与Vue Router随附的标准/:id或/ overview /:slug逻辑匹配。

由于不能选择塞子匹配,因此我们正在考虑以下解决方案:

  1. 用户访问页面“ / this-is-a-topic-page”
  2. 服务器调用返回pageType topicPage
  3. 的API
  4. topicPage与nuxt页面WpTopicPage
  5. 相关
  6. 我们将WpTopicPage设置为Vue Router通配符实例中的组件

这类似于代码中的以下内容:

export function createRouter() {
  return new Router({
    mode: 'history',
    routes: [
      // 1. User visits page "/this-is-a-topic-page"
      {
        name: 'wildcard',
        path: '*',
        component: *, // this should be dynamic
        beforeEnter: (to, from, next) => {
          // 2. Server calls API that returns the pageType `topicPage`
          this.$axios.get(`/call-to-the-api?slug=${to.params.slug}`)
            .then((res) => {
              // 3. `topicPage` relates to the nuxt page `WpTopicPage`
              if(res.data.pageType === 'topicPage') {
                // 4. Set `WpTopicPage` as our Page component
                return WpTopicPage;
              }
            })
        },
      },
    ],
  });
}

以上内容显然无效。是否可以在beforeEnter函数中动态设置路由内的component

5 个答案:

答案 0 :(得分:2)

前段时间我一直在为类似的任务而苦苦挣扎。我还需要一个完全动态的路由器,但我的应用程序初始化顺序有点不同。

  1. 在 Vue 实例化时 (@main.js new Vue({...})),我没有路由,也没有相关组件。
  2. 同时,我异步请求来自服务器的初始数据(并显示加载动画)
  3. 它一到我就映射我的路由器

很酷的是,可以在任何时间点映射和重新映射路由器。
我认为即使使用您的初始化序列,您也可以使用我的实现。

这是我的router.js

import Vue from 'vue';
import Router from 'vue-router';

Vue.use(Router);

const createRouter = () =>
  new Router({
    mode: 'history',
    linkActiveClass: 'active',
    base: __dirname,
    routes: []
  });

const router = createRouter();

export function resetRouter() {
  const newRouter = createRouter();
  router.matcher = newRouter.matcher;
}

export default router;

注意有 resetRouter 函数。我认为它的作用是不言自明的。

一旦您的应用知道需要映射/使用什么样的路由和组件,您就可以创建路由集合并映射它们。像这样:

import { default as router, resetRouter } from '@/router';

// ...

let routes = [];

// some magic to fill the routes array
// I.e. items coming from API
items.forEach(item => {
  if (item.view) {
    const component = () => import(`@/views/${item.view}.vue`);

    const route = {
      name: null, // prevent duplicate named routes warning
      path: item.path,
      component,
      meta: {
        title: item.title
      }
    };
    routes.push(route);
  }
});

resetRouter();
router.addRoutes(routes);

答案 1 :(得分:0)

有可能做。我创建了一个codepen供您测试:

这里是:

Vue.use(VueRouter);

let A = {
  mounted() {
    console.log('Mouted component A');
  },
};
let B = {
  mounted() {
    console.log('Mouted component B');
  },
};
let C = {
  mounted() {
    console.log('Mouted component C');
  },
};

const router = new VueRouter({
  mode: "hash",
  routes: [
    {
      path: '*',
      beforeEnter(to, from, next) {
        let components = {
          default: [A, B, C][Math.floor(Math.random() * 100) % 3],
        };
        to.matched[0].components = components;
        
        next();
      }
    },
  ]
});

app = new Vue({
  router,
  el: '#app',
  components: { A, B, C }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue-router/3.0.2/vue-router.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  <router-link :to="'/' + Math.random()">anything</router-link>
  <router-view></router-view>
</div>

这是输出:

enter image description here

您可以在控制台日志中看到-每次更改时,我们都会随机加载和安装组件。

答案 2 :(得分:0)

这是一个解决方案。

  • Home.vueNotFound.vue的路由是手动设置的
  • *.vue目录中的其他视图(views)动态创建路由
  • 路由的路径是.vue文件的小写字母,除了Home.vue设置为/之外。例如)FileName.vue的路径为/filename

项目结构

app
|__router
   |__index.js
|__views
   |__Home.vue
   |__NotFound.vue
   |__*.vue

index.js

import Vue from "vue";
import VueRouter from "vue-router";

// list of files in the `views` directory
const views = require.context(
  `../views`,
  true,
  /^.*\.vue$/
)

Vue.use(VueRouter);

// routes
const routes = [{
  path: "/",
  name: "Home",
  component: import("../views/Home.vue")
}]

// dynamically add all routes in views directory
for (var i = 0; i < views.keys().length; i++) {
  // skip home and notfound since it is defined above
  if (
    views.keys()[i].slice(2, -4) !== 'Home' &&
    views.keys()[i].slice(2, -4) !== 'NotFound' 
    ) {
    // file path for the route
    let filePath = `views/${views.keys()[i].slice(2, -4)}.vue`
    // add routes
    routes.push({
      path: views.keys()[i].slice(1, -4).toLowerCase(),
      name: views.keys()[i].slice(2, -4),
      component: () =>
        import("../" + filePath)
    })
  }
}

// directs any undefined route to not found 
routes.push({
  path: "*",
  name: "NotFound",
  component: () => import("../views/NotFound.vue")
})

const router = new VueRouter({
  routes: routes
});

export default router

答案 3 :(得分:0)

@AndrewShmig 和@fjemi 的两个回答都是很好的想法,唯一的问题是他们不符合 Vue Router 的预期设计。似乎 Vue 开发人员希望“to”是静态的,除了元字段,组件中的函数应该是一个 promise,在第一次使用路由后会被缓存。

我认为同意文档的唯一方法是使用 BeforeEnter 钩子将新组件推送到“/*”路由中,标记元字段,然后返回重定向。这将导致重新进入钩子,这次使用正确的组件。不断用每个条目替换路由有点令人不安,但希望它不会导致内存泄漏。由于我们正在替换一个预先存在的路由,我们可以期望它在路由器的一侧进行优化 - 但我没有检查 -。

答案 4 :(得分:0)

我们当前的解决方案没有违反守卫中“只读”tofrom 参数的要求,但是它需要访问路由的内部来安装代理,所以推荐的方法“替换路由并重定向到替换的路由”不太标准。然而,如果您不想每次都重写路由树,它会很有用。

首先我们安装一个守卫来将新路由保存在一个全局变量中。对于路由器的上下文来说足够全局。

router.beforeEach( (to,from) => {nuevaRuta = to })

我们保存了 to,但它仍然是“只读的”。看看如何:我们使用代理定义组件和道具:

    var nuevaRuta = 'None'
    const proxyComponentes = {
        get: function (target, prop, receiver) {
          return FriendlyIframe;
        },
        ownKeys: (oTarget, sKey) => { 
          if (nuevaRuta == 'None' || typeof nuevaRuta.params.panes == 'undefined')
             return Reflect.ownKeys(oTarget,sKey)
          else {
             return nuevaRuta.params.panes
          } 
        },
        getOwnPropertyDescriptor(target, prop) { // called for every property
          return {
            enumerable: true , configurable: true  /* ...other flags, probable "value:..." */
          };
        }
      };
   const proxyPropiedades = {
       get: function (target, prop, receiver) {
         console.log(prop)
         if (prop.startsWith('user_')){
           var id = prop.replace('user_','')
           return {src: `/u/${id}?noheader=true`}
         }else{
           return {src: `/p/${window.PRID}/${prop}?noheader=true`}
         }
        }};
    var myComponents = {};  
    var myProps= {}; 

我们在构建路由匹配时安装它们。更准确地说,对于版本 4 的路由器,可以在路由定义期间安装组件的代理:

routes:[
    {
      name: "generica",
      path: '/:panes+',
      components: new Proxy ( myComponents, proxyComponentes),
      props: false

但是 props 是通过规范化“缓存”的,所以我们需要在之后安装它。在我看来,这是最令人不安的细节

router.getRoutes().find( (e) => e.name=="generica").props = new Proxy (myProps, proxyPropiedades)

当然,如果路由器在组件处理中实现缓存和进一步优化,该方法可能会停止工作。如果您想要一个长期安全的解决方案,请继续更新和重定向。