От автора: импортировать изображения из папки assets, когда путь статичен, не так сложно и во Vue, и в Nuxt.js. Но загрузка изображений с динамическими путями часто сбивает с толку разработчиков, которые являются относительными новичками в одной из этих платформ. В этой статье я хочу продемонстрировать процесс динамической загрузки изображений во Vue и Nuxt.
Более того, я объясню, почему статические изображения могут быть просто загружены и что делать, когда путь должен быть динамическим. Если вы хотите пропустить описание внутренних компонентов и объяснения, вы можете перейти непосредственно к решению и к CodeSandbox. Но вы упустите важную информацию.
Статические изображения
Представьте себе компонент под названием Doggos, который должен выводить изображение милого щенка. Я имею в виду, мы все любим щенков, не так ли?
Единственное, что нужно нашим компонентам — это шаблон с одним тегом изображения, указывающим на путь. В идеале, используя псевдоним:
1 |
<img src="@/assets/doggos/riley.jpg"> |
(с псевдонимом для исходного каталога)
1 |
<img src="../assets/doggos/riley.jpg"> |
(относительный путь без псевдонима)
Но что, если у нас есть список милых щенков, и пользователь может решить, какое изображение отобразить на странице?
Стандартная идея
Обычная практика загрузки динамических изображений во Vue или Nuxt — использовать систему привязки Vue. Представьте, что у нас есть небольшой компонент, в котором мы можем выбрать щенка:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<template> <div> <div> <label v-for="doggo in dogNames" :key="doggo" style="margin-right: 2rem"> <input type="radio" :value="doggo" v-model="selectedDog"> {{ doggo }} </label> </div> <!-- Here should be the dog image --> </div> </template> <script> export default { data () { return { selectedDog: "", dogNames: ["Riley", "Annie", "Marvin"] } } } </script> |
Все, что осталось сделать, это получить правильное изображение для selectedDog. Поэтому можно подумать: «Нет ничего проще! :src вам в помощь!»
1 |
<img :src="`../assets/doggos/${selectedDog.toLowerCase()}.jpg`" :alt="selectedDog"> |
Давайте добавим эту строку и посмотрим, что произойдет, когда мы нажмем кнопку, соответствующую Райли… Облом, разбитое изображение и просто тэг alt! Давайте посмотрим на DOM. Он содержит следующий тег изображения:
1 |
<img src="../assets/doggos/riley.jpg" alt="Riley"> |
Что это значит? Это означает, что путь к активу не был заменен. Это строка, к которой относится выражение в приведенной выше строке шаблона.
Папка public/static в качестве возможного обходного пути (обычно не рекомендуется)
Если мы переместим изображения собак в папку public (или папку static в Nuxt) и используем код выше с новой ссылкой на папку (/public/doggo/${selectedDog.toLowercase()}.jpg), «это заработает ». Но это не оптимально, и я действительно не рекомендовал бы этот обходной путь. Разница между двумя папками assets и public/ static и причина неудачной первой попытки — это Webpack.
Содержимое в папке public или static, напрямую отображается в корне веб-приложений (обычно /) и не обрабатывается Webpack. Без оптимизации, добавленных хэшей контента и т. д.
Хотя это иногда необходимо, например, для предварительного просмотра изображений, для которых требуется фиксированный URL-адрес, для нашего варианта использования это не дает никаких преимуществ. Что если мы хотим поменять изображения Марвина, когда он немного подрос? Мы можем столкнуться с проблемами кеширования. Так что давайте копнем немного глубже и найдем основную причину, вместо того, чтобы идти по пути «быстрого исправления», которое в долгосрочной перспективе будет занимать больше времени. Вы, вероятно, знакомы с подобным сценарием.
Управление активами Vue и Webpack
Все компоненты одного файла (с расширением .vue) обрабатываются Webpack и vue-loader. Благодаря им отдельные компоненты файлов и все их удивительные функции, такие как поддержка препроцессоров CSS, настраиваемые блоки и горячая перезагрузка с сохранением состояния, работают «из коробки». Это также включает в себя обработку статических активов, как в нашем первоначальном примере компонента.
Webpack импортирует статические ресурсы, такие как ../assets/doggos/riley.jpg, как так называемые запросы модулей , что означает, что они обрабатываются соответствующим загрузчиком Webpack, определенным через конфигурацию. И vue-cli, и Nuxt настраивают обработку для нескольких типов файлов из коробки, включая изображения с различными расширениями, такими как jpg, png или gif.
После обработки наш начальный пример тега изображения <img src=»../assets/doggos/riley.jpg» alt=»Riley»> будет скомпилирован в функцию рендеринга, которая выглядит примерно так:
1 2 3 4 5 6 |
createElement('img', { attrs: { src: require('../assets/doggos/riley.jpg'), // this is now a module request alt: 'Riley' } }) |
Путь к изображению теперь заменен запросом модуля Webpack. Это прекрасно работает для статических ресурсов, потому что их пути известны во время сборки. Когда речь заходит о динамическом контенте, включая директиву Vue v-bind, Webpack не знает, на что будут полагаться выражения во время выполнения. Вот почему общая первая идея, как описано выше, не работает, как ожидалось.
Что мы можем сделать?
Если мы более внимательно посмотрим на скомпилированный код нашего простого тега изображения с атрибутом static source, мы увидим, что нам нужно для решения проблемы.
Решение
Чтобы указать Webpack, какие изображения должны быть загружены, мы должны самостоятельно выполнить запрос модуля. Это делается путем вызова require(…) с правильным путем. В нашей ситуации это привело бы к следующему тегу изображения:
1 |
<img :src="require(`../assets/doggos/${selectedDog.toLowerCase()}.jpg`)" :alt="selectedDog"> |
Вместо привязки атрибута src к пути изображения, мы привязываем его к модулю webpack, запрошенному для пути изображения. Теперь мы можем извлечь эту неприятную часть в собственное вычисляемое свойство для лучшей читаемости.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
export default { computed: { dogImage () { if (!this.selectedDog) { return } const fileName = this.selectedDog.toLowerCase() return require(`../assets/doggos/${fileName}.jpg`) // the module request } } } |
Очень важно использовать строгое выражение require, когда речь идет о возможном имени файла изображения. Если мы воспользуемся приведенным выше кодом, окончательная сборка будет включать в себя каждое изображение с расширением .jpg в папке /assets/doggos.
Это происходит потому, что webpack не может угадать, какое из изображений будет фактически использоваться во время выполнения, поэтому он включает их все для предотвращения ошибок. Чем менее вы строги, тем больше файлов будут совпадать и будут включены в пакет через веб-пакет, что приведет к увеличению размера пакета.
Мы решили проблему! С помощью require мы можем загрузить изображение с динамическими путями. Как обычно, окончательный (рабочий) код, включая изображения милых щенков, доступен на CodeSandbox.
Заключение
Загрузка изображений с динамическими путями не так сложна, если знать, что происходит за кулисами. Используйте строгое выражение require, у вас никогда не возникнет проблем с динамическими изображениями! Я надеюсь, что вы узнали кое-что о динамических изображениях, а также о процессе обработки статических ресурсов.
Если вы хотите продолжить чтение, я предлагаю ознакомиться с документацией по vue-loader и Webpack. Я действительно надеюсь, что вам понравилась эта статья! Если вы знаете некоторых людей, которые также имеют проблемы с динамической загрузкой изображений во Vue или Nuxt, я бы попросил вас распространить ссылку и помочь им!
Автор: Alexander Lichter
Источник: //blog.lichter.io
Редакция: Команда webformyself.
Комментарии (1)