Динамический модуль импорта является одной из последних функций JavaScript, чтобы поразить основные браузеры. Основным вариантом использования этой функции являются модули с отложенной загрузкой, которые позволяют доставлять контент, когда это необходимо, а не все сразу.
В этой статье я покажу, как можно создать маршрутизатор с отложенной загрузкой с помощью Vue.js всего за несколько строк кода. Это будет работать изначально в браузерах, в которых реализован динамический импорт модулей, но я также добавлю запасной вариант для старых браузеров.
Получите готовый код здесь, на GitHub .
Вам также может понравиться:
Ленивая загрузка модулей ES2015 в браузере .
Статический JavaScript-модуль Импорт
Если вы используете последнюю версию любого основного браузера, вы можете выполнять статический импорт / экспорт прямо сейчас. Для Vue.js это означает, что вы можете экспортировать определение компонента из файла следующим образом:
BooksPage.js
JavaScript
1
export default {
2
name: 'BooksPage',
3
template: `
4
<div>
5
<h1>Books Page</h1>
6
<p>{{ message }}</p>
7
</div>
8
`,
9
data() {
10
return {
11
message: 'Oh hai from the books page'
12
}
13
}
14
};
И импортируйте его в свое приложение так:
app.js
JavaScript
xxxxxxxxxx
1
import BooksPage from './BooksPage';
2
new Vue({
4
components: {
5
BooksPage
6
}
7
});
Создание компонентных модулей JavaScript позволяет организовать ваше приложение так, чтобы каждая «страница» находилась в отдельном файле. В этом нет ничего нового, если вы используете однофайловые компоненты Vue.js, но благодаря встроенной поддержке теперь эта архитектура может быть реализована без Webpack или Babel.
Я рассмотрел импорт компонентов Vue.js как модулей JavaScript более подробно в недавней статье Vue.js Однофайловые компоненты JavaScript в браузере .
Импорт модуля динамического JavaScript
Если ваши компоненты представляют страницы, было бы лучше получить файлы модуля по требованию, чтобы пользователю не приходилось загружать страницы, которые он не посещал. Однако статический импорт разрешается во время компиляции . Это означает, что вы не можете поместить import BooksPage from './BooksPage';
внутри if
оператора условную загрузку. Вместо этого ваш статический импорт начнет загружаться, как только запустится скрипт, который их загружает.
Вот где происходит динамический импорт . Они могут быть определены во время выполнения , то есть вы можете условно загрузить модуль JavaScript и, следовательно, страницы по требованию. Обратите внимание, что динамический импорт возвращает Promise, который разрешает содержимое модуля.
JavaScript
xxxxxxxxxx
1
import HomePage from './pages/HomePage.js';
2
Vue.component('my-router', {
4
template: '<component :is="page"/>',
5
data() {
6
return {
7
page: HomePage
8
}
9
},
10
methods: {
11
navigate(name) {
12
this.page = return () => import(`./${name}Page.js`)
13
}
14
}
15
});
Примечание:
component
это встроенный мета-компонент, который принимает определение компонента через реквизитis
. Как и все компоненты Vue, это может быть либо определение компонента, либо Promise, который разрешает определение компонента.
Этот код будет работать непосредственно в браузере, если вы используете последнюю версию Safari или Chrome Canary, а вскоре появятся другие браузеры.
Создание Tiny Lazy-Load Router
Имея в виду эту теорию, давайте сделаем наш маршрутизатор с отложенной загрузкой. В шаблоне приложения создайте несколько тегов привязки, где href
путь к модулю компонента для этой страницы. Прослушивание события щелчка на этих элементах, предотвратить перенаправление, и вместо того, чтобы вызвать метод, navigate
.
index.html
HTML
xxxxxxxxxx
1
<div id="app">
2
<nav>
3
<a href="/pages/BooksPage.js" @click.prevent="navigate">Books</a>
4
<a href="/pages/MoviesPage.js" @click.prevent="navigate">Movies</a>
5
<a href="/pages/GamesPage.js" @click.prevent="navigate">Games</a>
6
</nav>
7
<!--Where the page displays-->
9
<component :is="page"></component>
10
</div>
Мы определим navigate
метод в экземпляре Vue, и он примет событие click в качестве аргумента. Используйте значение ссылки href
, т. event.target.pathname
Е. Динамически импортируйте требуемый модуль компонента страницы и назначьте его page
свойству локального состояния. Это динамически связано с component
компонентом.
app.js
JavaScript
xxxxxxxxxx
1
import BooksPage from './pages/BooksPage.js';
2
new Vue({
4
el: '#app',
5
data: {
6
page: BooksPage
7
},
8
methods: {
9
navigate(event) {
10
this.page = () => import(`./${event.target.pathname}`)
11
// Vue.js < 2.5.0
12
// .then(m => m.default)
13
;
14
}
15
}
16
});
Обратите внимание, что версии Vue.js до 2.5.0 должны включать
then
обратный вызов для правильного разрешения определения модуля.
Это оно! Если вы запустите это в браузере, который поддерживает динамический импорт, вы увидите это:
Отступать
А как насчет пользователей, у которых нет последней версии Safari или Chrome? Им нужен запасной вариант. Давайте создадим один с Webpack .
Во-первых, нам нужно navigate
немного изменить метод. Реализация Webpack import()
требует, чтобы он знал заранее, какие файлы ему могут понадобиться для динамической загрузки. Вам не нужно указывать точное имя модуля, просто убедитесь, что значение, которое вы указываете, import()
является разрешаемым файлом или каталогом.
Для этого мы изменим динамическое имя файла, чтобы оно указывало pages
каталог и извлекало имя файла из href
ie ./pages/${event.target.pathname.split('/').pop()}
. Во время компиляции Webpack достаточно умен, чтобы знать, что это означает «некоторый файл в каталоге страниц », и будет обрабатывать любой файл JavaScript в этом каталоге.
Во-вторых, нам нужно поместить комментарий /* webpackChunkName: "pages/[request]" */
в функцию, чтобы Webpack знал, как извлечь этот кусок в отдельный файл. В противном случае Webpack объединит все модули компонентов страницы в один файл, а преимущества отложенной загрузки будут утрачены.
app.js
JavaScript
xxxxxxxxxx
1
navigate(event) {
2
this.page = () => import(
3
/* webpackChunkName: "pages/[request]" */
4
`./pages/${event.target.pathname.split('/').pop()}`
5
)
6
}
Webpack Config
Вы можете использовать эту простую конфигурацию Webpack, которая имеет две примечательные особенности:
- Определяет
chunkFilename
выходное свойство. Это обеспечивает правильное имя модулей компонентов страницы в выходных данных Webpack. - Транспортирует JavaScript с Вавилоном. Вам понадобится плагин,
syntax-dynamic-import
чтобы Babel распознал динамический оператор импорта.
JavaScript
xxxxxxxxxx
1
var path = require('path');
2
var webpack = require('webpack');
3
module.exports = {
5
entry: './app.js',
6
output: {
7
path: path.resolve(__dirname, './dist'),
8
publicPath: '/dist/',
9
filename: 'build.js',
10
chunkFilename: '[name].js'
11
},
12
module: {
13
rules: [
14
{
15
test: /\.js$/,
16
loader: 'babel-loader',
17
exclude: /node_modules/,
18
options: {
19
plugins: [require('babel-plugin-syntax-dynamic-import')]
20
}
21
}
22
]
23
}
24
};
Запустите Webpack с этой конфигурацией, и вы получите файлы сборки, в том числе:
- Все файлы модулей JavaScript передаются в модули CommonJS.
- Основной пакет будет включать реализацию динамического импорта в Webpack.
Проверка поддержки динамического импорта
Как вы говорите браузеру использовать запасной вариант? Насколько я знаю, нет конкретного способа проверить поддержку браузера import()
. Моя стратегия заключается в использовании встроенного сценария в теле документа, который создает новый script
тег и условно меняет источник (основной сценарий или запасной вариант) в зависимости от поддержки import()
.
HTML
xxxxxxxxxx
1
<script type="text/javascript">
2
var script = document.createElement('script');
3
try {
4
Function('import("")');
5
script.src = "app.js";
6
script.type = 'module';
7
} catch(e) {
8
script.src = "dist/build.js";
9
script.type = 'text/javascript';
10
}
11
document.body.appendChild(script);
12
</script>
Обратите внимание, что резервный сценарий обрабатывается как обычный сценарий JavaScript, тогда как основной сценарий рассматривается как модуль.
Заключение
Было бы здорово, если бы мы могли использовать встроенные реализации импорта модулей JavaScript, поскольку такие оптимизации, как отложенная загрузка, можно выполнить с меньшим размером файла и гораздо более простой реализацией. Например, реализация Webpack import()
требует, чтобы он знал заранее, какие файлы ему могут понадобиться для динамической загрузки, а собственная реализация - нет.
На практике такие функции должны быть прогрессивными усовершенствованиями и, следовательно, потребовать Webpack в качестве запасного варианта, который вновь вызывает те же сложности. О, хорошо, это веб-разработчик для вас.