Я новичок в 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
}
}
};
Итак, вопрос сводится к следующему: как я могу динамически загружать компонент?



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


How can I dynamically load a component?
Попробуй это:
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.jsimport 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
О, теперь я понял! Я отредактировал свой ответ. А также проект на CodeSandbox.
Хорошо выглядеть. Вы знаете, есть ли способ выполнить весь импорт в определениях маршрутов? Я планирую загружать маршруты из API, и было бы неплохо сделать все как можно ближе друг к другу.
Я только начал работать с VueJS в течение 3 месяцев, и мне очень жаль, что я не смог ответить на этот дополнительный вопрос. Может быть, кто-то знает, как это сделать, тогда вы можете задать еще один вопрос.
@GTHvidsten Вы можете подумать о том, чтобы принять или проголосовать за ответ, если он действительно поможет :) Было бы очень приятно, если бы вы это сделали :)
Я сделаю это, но я всегда придерживаюсь политики ожидания, по крайней мере, несколько дней, чтобы принять решение, чтобы дать другим возможность ответить;)
Я наконец нашел время, чтобы проверить это, и я сожалею, что это не сработало. Я изменил свой main.js, чтобы загрузить подстраницу перед определением маршрутизатора: const subpage = () => import('./subpage.js'); и использовать эту переменную в определении маршрута вместо прямого импорта: { path: '/subpage', component: subpage }. Subpage.js загружается динамически, но содержимое компонента и URL-адрес по-прежнему не отображается / не изменяется.
Я также заметил, что ваш codeandbox-example использует webpack + babel + различные другие фреймворки, тогда как я ищу решение без "любые дополнительные библиотеки (т. е. никаких веб-пакетов и т.п.)".
Я разделяю ваши пожелания о «максимально экономичной» кодовой базе и поэтому сделал этот простой пример кода ниже (также доступный по адресу https://codesandbox.io/embed/64j8pypr4k).
Я тоже не являюсь опытным пользователем Vue, но при исследовании я подумал о трех возможностях;
imports,requirejs,<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.info(json);
that.msg =
'<p style = "color: green;">' + JSON.stringify(json) + "</p>";
})
.catch(error => {
console.info(error);
that.msg =
'<p style = "color: red;">Error fetching: ' + error + "</p>";
});
}, 2000);
}
},
created() {
this.fetchData();
}
});
Как видите - здесь функция loadComponent() творит "волшебство" загрузки компонентов.
Итак, это работает, но, вероятно, это не лучшее решение в отношении (по крайней мере) следующего:
Надеюсь, я вам помог: D
Я хотел увидеть, насколько сегодня полезен «новый» динамический импорт (https://developers.google.com/web/updates/2017/11/dynamic-import), поэтому я провел с ним несколько экспериментов. Они делают асинхронный импорт намного проще, и ниже мой пример кода (без Webpack / Babel / просто чистый Chrome-friendly JS).
Я сохраню свой старый ответ (Как динамически загружать компоненты в маршруты) для потенциальной справки - загрузка скриптов таким образом работает в большем количестве браузеров, чем динамический импорт (https://caniuse.com/#feat=es6-module-dynamic-import).
Итак, в конце я заметил, что вы на самом деле были очень, очень, очень близки к своей работе - на самом деле это была просто синтаксическая ошибка при экспорте импортированного модуля JS (без запятой).
Пример, приведенный ниже, также работал у меня (к сожалению, lint Codesandbox (es) не разрешает синтаксис, но я проверил его локально, и он работал (в 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.info("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>'.
Тем не менее, это работает только в:
Спасибо за вклад. Я посмотрю. Однако вы выполняете импорт в верхней части файла, что означает, что все файлы будут загружены при первой загрузке страницы, вместо того, чтобы загружаться лениво, когда пользователь переходит по определенному маршруту. Как бы вы добавили в свой пример отложенную загрузку?