Javascript 如何在路由中动态加载组件

Javascript 如何在路由中动态加载组件,javascript,vue.js,vue-component,vue-router,Javascript,Vue.js,Vue Component,Vue Router,我是一名Vue新手,我正在试验Vue路由器和组件的动态加载,而不使用任何附加库(因此没有网页包或类似工具) 我已经创建了一个索引页并设置了一个路由器。当我第一次加载页面时,我可以看到子页面.js尚未加载,当我单击时,我可以看到子页面.js文件已加载。但是,URL不会更改,组件也不会出现 这就是我到目前为止所做的: index.html <html> <head> <script src="https://cdn.jsdelivr.net/npm/vue/di

我是一名Vue新手,我正在试验Vue路由器和组件的动态加载,而不使用任何附加库(因此没有网页包或类似工具)

我已经创建了一个索引页并设置了一个路由器。当我第一次加载页面时,我可以看到
子页面.js
尚未加载,当我单击
时,我可以看到
子页面.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>
<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>
<!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>
子页面js

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

const app = new Vue({
    router
}).$mount('#app');
export default {
    name: 'SubPage',
    template: '<div>SubPage path: {{msg}}</div>'
    data: function() {
        return {
            msg: this.$route.path
        }
    }
};
export default {
    name: 'SubPage',
    template: '<div>SubPage path: {{msg}}</div>',
    data: function() {
        return {
            msg: this.$route.path
        }
    }
};
导出默认值{
名称:'子页面',
模板:'子页面路径:{{msg}}'
数据:函数(){
返回{
msg:this.$route.path
}
}
};
所以问题归结为:如何动态加载组件

如何动态加载组件

试试这个:

App.vue
Home.vue
这样,组件
Home
将自动加载

这是演示:

我与您分享“尽可能精简”代码库的愿望,因此在下面制作了这个简单的示例代码(也可以访问)

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

  • 动态
    import
    s
  • require
    js
  • 老派JS生成的
    包括
看起来最后一种方法最简单,也最省力:D可能不是最佳实践,可能很快就会过时(至少在动态导入支持方面是这样)

注意:这个例子对最近的浏览器很友好(使用本机承诺、获取、箭头函数…)。所以-使用最新的Chrome或Firefox来测试:)支持旧浏览器可能需要一些多边形填充和重构等,但这会给代码库增加很多

So-按需动态加载组件(以前未包括):


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>
<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>
<!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>

Vue懒散负荷试验
html,正文{
保证金:5px;
填充:0;
字体系列:无衬线;
}
导航a{
显示:块;
保证金:5px0;
}
主导航{
边框:1px实心;
填充:10px;
边缘顶部:5px;
}
.产出{
字体大小:粗体;
}
家
简单组件
不是那么简单的组件
函数loadComponent(组件名称、路径){
返回新承诺(功能(解决、拒绝){
var script=document.createElement('script');
script.src=路径;
script.async=true;
script.onload=函数(){
var component=Vue.component(componentName);
if(组件){
解析(组件);
}否则{
拒绝();
}
};
script.onerror=拒绝;
document.body.appendChild(脚本);
});
}
var路由器=新的VueRouter({
模式:“历史”,
路线:[
{
路径:“/”,
组成部分:{
模板:“主页”
},
},
{
路径:'/simple',,
组件:功能(解析、拒绝){
loadComponent('simple','simple.js')。然后(解析,拒绝);
}
},
{path:'/complex',component:function(resolve,reject){loadComponent('complex','complex.js')。然后(resolve,reject);}
}
]
});
var app=新的Vue({
el:“#应用程序”,
路由器:路由器,,
});

simple.js

Vue.component("simple", {
  template: "<div>Simple template page loaded from external file</div>"
});
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();
  }
});
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");
      })
    }
}
Vue.component(“简单”{
模板:“从外部文件加载的简单模板页面”
});

complex.js

Vue.component("simple", {
  template: "<div>Simple template page loaded from external file</div>"
});
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();
  }
});
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");
      })
    }
}
Vue.component(“复杂”{
模板:
“从外部文件加载的复杂模板页面

子页面路径:{{{path}}
外部加载的数据有一定延迟:
”, 数据:函数(){ 返回{ 路径:此。$route.path, msg:“

请稍候…” }; }, 方法:{ fetchData(){ var=这个; 设置超时(()=>{ /*模拟延迟的位延迟:D*/ 取回(“https://jsonplaceholder.typicode.com/todos/1") .then(response=>response.json()) 。然后(json=>{ log(json); 那是= “

”+JSON.stringify(JSON)+“

”; }) .catch(错误=>{ console.log(错误); 那是= 获取错误:“+Error+”

”; }); }, 2000); } }, 创建(){ 这是fetchData(); } });

正如您所看到的,函数
loadComponent()
在这里执行加载组件的“神奇”操作

因此它是可行的,但它可能不是最好的解决方案,至少在以下方面:

  • 使用JS插入标记可以被视为安全问题 在不久的将来
  • 性能-同步加载文件会阻塞线程(这可能会 成为应用程序生命中最重要的一员)
  • 我没有测试缓存等可能是生产中的一个实际问题
  • 你失去了(Vue)组件的美感,比如作用域css、html和 JS可以自动绑定到网页包或其他东西
  • 你松开了巴别塔的编译/播放
  • 热模块更换(和状态持久性等)-我相信,已经不存在了
  • 我可能忘记了其他对我来说显而易见的问题 四年级:D

希望我能帮助你:D

我想看看“新的”动态导入今天有多有用(),所以我用它做了一些实验。它们确实简化了异步导入,下面是我的示例代码(没有Webpack/Babel/只有纯Chrome友好的JS)

我将保留我的旧答案()用于潜在的引用加载脚本,这种方式比动态导入()在更多的浏览器中工作

所以最后我注意到