Как динамически загружать компоненты в маршрутах - PullRequest
0 голосов
/ 29 сентября 2018

Я новичок в Vue, и я экспериментирую с vue-router и динамической загрузкой компонентов без использования каких-либо дополнительных библиотек (поэтому нет веб-пакета или чего-то подобного).

Я создал страницу индекса и установилдо роутера.Когда я впервые загружаю страницу, я вижу, что 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 голосов
/ 01 октября 2018

Я поделюсь вашими пожеланиями по поводу «максимально бережной» кодовой базы и поэтому сделал этот простой пример кода ниже (также доступен по адресу https://codesandbox.io/embed/64j8pypr4k).

Я тоже не Vue poweruser, но при исследовании я подумал отри возможности:

  • динамический import с,
  • require js,
  • генерируется JS старой школы <script src /> include.

Похоже, что последний самый простой и требует наименьших усилий: D Вероятно, не лучшая практика и, вероятно, скоро устареет (по крайней мере, с поддержкой динамического импорта).

NB: Этот пример удобен для более свежихбраузеры (с собственными функциями Promises, Fetch, Arrow ...). Итак - используйте для тестирования новейший Chrome или Firefox :) Поддержка старых браузеров может быть реализована с некоторыми полифилами, рефакторингом и т. д. Но это добавит многое в кодовую базу ...

Итак - динамически загружаемые компоненты, по требованию (и не включены ранее):


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, SCOP, HTML и JS, которые могут автоматически связываться с Webpack или чем-то подобным,
  • Вы теряете компиляцию / перенос Babel,
  • Горячая замена модуля (и сохранение состояния и т. Д.) - прошло, я думаю,
  • Я, вероятно, забыл о других проблемах, которые очевидны для пожилых людей: D

Надеюсь, я тебе все же помог: D

0 голосов
/ 02 октября 2018

Я хотел посмотреть, насколько полезны сегодня "новые" динамические импорты (https://developers.google.com/web/updates/2017/11/dynamic-import),, поэтому я провел несколько экспериментов с ним. Они действительно упрощают асинхронный импорт, и ниже приведен пример кода (нет Webpack / Babel /просто чистый JS-совместимый с Chrome).

Я оставлю свой старый ответ ( Как динамически загружать компоненты в маршруты ) для потенциальной справки - загрузка скриптов таким образом работает в большем количестве браузеров, чем динамический импортdo (https://caniuse.com/#feat=es6-module-dynamic-import).

) В конце я заметил, что вы на самом деле были очень, очень, очень близки к своей работе - это была просто синтаксическая ошибка при экспорте импортированного модуля JS (пропущена запятая).

Пример ниже также работал для меня (к сожалению, lint Codesandbox (ов) не позволяет синтаксис, но я проверил его локально, и он работал (в 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 для Android> = v69
  • Safari> = v11.1
  • IOS Safari> = v11.2

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

0 голосов
/ 29 сентября 2018

Как динамически загрузить компонент?

Попробуйте:

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 будет автоматически загружен.

Thisэто демо: https://codesandbox.io/s/48qw3x8mvx

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...