如何动态加载路线中的组件

时间:2018-09-29 09:19:53

标签: javascript vue.js vue-component vue-router

我是Vue的新手,我正在尝试使用vue-router和动态加载组件,而无需使用任何其他库(因此没有webpack或类似的库)。

我已经创建了索引页并设置了路由器。第一次加载页面时,我可以看到尚未加载subpage.js,单击<router-link>时可以看到subpage.js文件已加载。但是,URL不会更改,组件也不会出现。

这是我到目前为止所拥有的:

index.html

<html>
<head>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
</head>
<body>
    <div id="app">
      <h1>Hello App!</h1>
      <router-link to="/subpage">To subpage</router-link>
      <router-view></router-view>
    </div>
    <script src="main.js"></script>
</body>
</html>

main.js

const router = new VueRouter({
  routes: [
    { path: '/subpage', component: () => import('./subpage.js') }
  ]
})

const app = new Vue({
    router
}).$mount('#app');

subpage.js

export default {
    name: 'SubPage',
    template: '<div>SubPage path: {{msg}}</div>'
    data: function() {
        return {
            msg: this.$route.path
        }
    }
};

因此,问题归结为:如何动态加载组件?

3 个答案:

答案 0 :(得分:0)

  

如何动态加载组件?

尝试一下:

App.vue

<template>
  <div id="app">
    <router-link to="/">Home</router-link>
    <router-link to="/about">About</router-link>
    <hr/>
    <router-view></router-view>
  </div>
</template>

<script>
export default {
  name: 'app',
  components: {}
};
</script>

main.js

import Vue from 'vue';
import VueRouter from 'vue-router';
import App from './App.vue';

Vue.use(VueRouter);
Vue.config.productionTip = false;

const Home = () => import('./components/Home.vue');
const About = () => import('./components/About.vue');

const router = new VueRouter({
  mode: 'history',
  routes:[
    {path:'/', component: Home},
    {path:'/about',component: About}
  ]
})
new Vue({
  router,
  render: h => h(App)
}).$mount('#app');

Home.vue

<template>
  <div>
    <h2>Home</h2>
  </div>
</template>

<script>
export default {
  name: 'Home'
};
</script>

About.vue

<template>
  <div>
    <h2>About</h2>
  </div>
</template>

<script>
export default {
  name: 'About'
};
</script>

这样,组件Home将被自动加载。

这是演示:https://codesandbox.io/s/48qw3x8mvx

答案 1 :(得分:0)

我与您一样希望拥有“尽可能精简”的代码库,因此在下面做了这个简单的示例代码(也可以从https://codesandbox.io/embed/64j8pypr4k访问)。

我也不是Vue超级用户,但是在研究时,我考虑了三种可能性:

  • 动态import
  • require js,
  • 生成了<script src />
  • 老派JS包含。

看起来最后一个方法最简单,也最省力:D可能不是最佳实践,并且可能很快就会过时(至少会提供动态导入支持)。

NB:此示例对较新的浏览器(使用本机Promises,Fetch,Arrow函数...)友好。因此,请使用最新的Chrome或Firefox进行测试:)可以通过一些polyfill和重构等方式来支持较旧的浏览器。但这将为代码库增加很多...

所以-按需动态加载组件(之前未包含):


index.html

<html>

<head>
    <meta charset="utf-8">
    <meta http-equiv="x-ua-compatible" content="ie=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Vue lazyload test</title>
  <style>
html,body{
  margin:5px;
  padding:0;
  font-family: sans-serif;
}

nav a{
  display:block;
  margin: 5px 0;
}

nav, main{
  border:1px solid;
  padding: 10px;
  margin-top:5px;
}

    .output {
        font-weight: bold;
    }

  </style>
</head>

<body>
    <div id="app">
    <nav>
      <router-link to="/">Home</router-link>
      <router-link to="/simple">Simple component</router-link>
      <router-link to="/complex">Not sooo simple component</router-link>
    </nav>
      <main>
          <router-view></router-view>
    </main>
    </div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.0.3/vue.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue-router/2.0.1/vue-router.min.js"></script>
<script>
    function loadComponent(componentName, path) {
      return new Promise(function(resolve, reject) {
        var script = document.createElement('script');

        script.src = path;
        script.async = true;

        script.onload = function() {
          var component = Vue.component(componentName);

          if (component) {
            resolve(component);
          } else {
            reject();
          }
        };
        script.onerror = reject;

        document.body.appendChild(script);
      });
    }

    var router = new VueRouter({
      mode: 'history',
      routes: [
        {
          path: '/',
          component: {
            template: '<div>Home page</div>'
          },
        },
        {
          path: '/simple',
          component: function(resolve, reject) {
            loadComponent('simple', 'simple.js').then(resolve, reject);
          }
        },
         { path: '/complex', component: function(resolve, reject) { loadComponent('complex', 'complex.js').then(resolve, reject); }
        }
      ]
    });

    var app = new Vue({
      el: '#app',
      router: router,
    });
</script>

</body>

</html>

simple.js

Vue.component("simple", {
  template: "<div>Simple template page loaded from external file</div>"
});

complex.js

Vue.component("complex", {
  template:
    "<div class='complex-content'>Complex template page loaded from external file<br /><br />SubPage path: <i>{{path}}</i><hr /><b>Externally loaded data with some delay:</b><br /> <span class='output' v-html='msg'></span></div>",
  data: function() {
    return {
      path: this.$route.path,
      msg: '<p style="color: yellow;">Please wait...</p>'
    };
  },
  methods: {
    fetchData() {
      var that = this;
      setTimeout(() => {
        /* a bit delay to simulate latency :D */
        fetch("https://jsonplaceholder.typicode.com/todos/1")
          .then(response => response.json())
          .then(json => {
            console.log(json);
            that.msg =
              '<p style="color: green;">' + JSON.stringify(json) + "</p>";
          })
          .catch(error => {
            console.log(error);
            that.msg =
              '<p style="color: red;">Error fetching: ' + error + "</p>";
          });
      }, 2000);
    }
  },
  created() {
    this.fetchData();
  }
});

如您所见-函数loadComponent()的作用是在这里加载组件。

所以它可行,但就以下(至少)方面而言,它可能不是最佳解决方案:

  • 使用JS插入标签可被视为安全问题 在不久的将来,
  • 性能-同步加载文件会阻塞线程(这可以 成为应用生命中的重要禁忌)
  • 我没有测试缓存等。这可能是生产中的真正问题,
  • 您失去了(Vue)组件的美感-像作用域的CSS,HTML和 可以自动与Webpack捆绑在一起的JS,
  • 您松开了Babel的编译/转译,
  • 热模块更换(以及状态持久性等)-我相信
  • 我可能忘记了其他明显的问题 高级:D

希望我可以帮助您:D

答案 2 :(得分:0)

我想看看今天的“新”动态导入(https://developers.google.com/web/updates/2017/11/dynamic-import)有多有用,所以我做了一些实验。它们确实使异步导入更容易,下面是我的示例代码(没有Webpack / Babel /只是纯Chrome友好的JS)。

我将保留我的旧答案(How to dynamically load components in routes)供潜在参考-加载比动态导入(https://caniuse.com/#feat=es6-module-dynamic-import)在更多浏览器中工作的脚本。

因此,最后我注意到您实际上非常非常接近您的工作-实际上,这只是导出导入的JS模块时出现语法错误(缺少逗号)。

下面的示例也对我有用(不幸的是,Codesandbox的(es)lint不允许使用该语法,但是我已经在本地对其进行了检查,并且可以使用(在Chrome浏览器中,甚至Firefox都不喜欢该语法:(SyntaxError:import关键字只能出现在模块中)));


index.html

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" /> 
  <title>Page Title</title>
</head>

<body>
    <div id="app">
      <h1>Hello App!</h1>
      <router-link to="/temp">To temp</router-link>
      <router-link to="/module">To module</router-link>
      <router-view></router-view>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
    <script src="main.js"></script>
</body>
</html>

main.js:

'use strict';

const LazyRouteComponent = {
    template: '<div>Route:{{msg}}</div>',
    data: function() {
        return {
            msg: this.$route.path
        }
    }
}


const router = new VueRouter({
  routes: [
    {
        path: '/temp',
        component: {
            template: '<div>Hello temp: {{msg}}</div>',
            data: function() {
                return {
                    msg: this.$route.path
                }
            }
        }
    },
    { path: '/module', name: 'module', component:  () => import('./module.js')},
    { path: '*', component: LazyRouteComponent }
  ]
})

const app = new Vue({
    router
}).$mount('#app');

和主要区别 module.js

export default {
    name: 'module',
    template: '<div>Test Module loaded ASYNC this.$route.path:{{msg}}</div>',
    data: function () {
       return {
          msg: this.$route.path
       }
    },
    mounted: function () {
      this.$nextTick(function () {
        console.log("entire view has been rendered after module loaded Async");
      })
    }
}

所以-几乎与您的代码完全一样-但带有所有逗号;

subpage.js

export default {
    name: 'SubPage',
    template: '<div>SubPage path: {{msg}}</div>',
    data: function() {
        return {
            msg: this.$route.path
        }
    }
};

所以-您的代码有效(我通过复制粘贴对其进行了测试)-实际上,您在template: '<div>SubPage path: {{msg}}</div>'之后只是缺少了一个逗号。

尽管如此,这似乎仅适用于:

  • Chrome浏览器> = v63
  • Chrome for Android> = v69
  • Safari> = v11.1
  • IOS Safari> = v11.2

({https://caniuse.com/#feat=es6-module-dynamic-import)...