Вы занимаетесь блогингом или созданием контента на нескольких языках? Или у вас есть множество социальных медиа-аккаунтов, которыми вы хотите поделиться? Тогда вам понравится этот адаптивный и многоязычный компонент футера для Vue 3!

Данный компонент футера разработан с учетом удобства использования, а также адаптивности и настраиваемости. Он автоматически отображает социальные ссылки и иконки на нужном языке и подходящем типе устройства. Минималистический дизайн футера позволяет пользователю сосредоточиться на главном содержимом страницы. Компонент прост в использовании, а его адаптивность гарантирует хороший внешний вид на любом устройстве и размере экрана.

Данные

Языки и ссылки на социальные сети для этого компонента футера хранятся в файлах JSON в папке data. Файл language.json содержит массив объектов, каждый из которых представляет язык. Свойство value каждого объекта языка представляет первые две буквы кода языка, а свойство text хранит название языка на его родном языке. Свойство flag хранит эмодзи флага соответствующей страны.

[
  {
    "value": "en",
    "text": "English",
    "flag": "????????"
  },
  {
    "value": "et",
    "text": "Eesti",
    "flag": "????????"
  },
  ........
]

Ниже вы найдете файл social.json, который содержит массив объектов. Каждый объект служит представлением ссылки на социальную сеть. Свойство icon содержит имя соответствующего файла SVG в папке assets, а свойство name содержит название самой социальной платформы. Свойство url содержит ссылку на социальную сеть. Наконец, свойство tags является массивом строк, которые показывают языки, на которых отображается ссылка. Если строка равна "all", то ссылка видна на всех языках. Если строка равна "desktop", то ссылка будет показываться только на настольных устройствах. Аналогично, если строка равна "mobile", то ссылка будет отображаться только на мобильных устройствах.

[
  {
    "icon": "facebook.svg",
    "name": "Facebook",
    "url": "https://www.facebook.com/vuewebdev",
    "tags": ["en", "de", "vi", "et", "desktop"]
  },
  {
    "icon": "twitter.svg",
    "name": "Twitter",
    "url": "https://twitter.com/DiveWithOleg",
    "tags": ["en", "de", "vi", "et", "desktop"]
  },
  {
    "icon": "github.svg",
    "name": "GitHub",
    "url": "https://github.com/Divewitholeg",
    "tags": ["all", "desktop"]
  },
    ........
]

Компонент выбора языка

Позвольте рассказать о компоненте выбора языка. Это простой элемент выбора, связанный с файлом lang.json, который содержит массив языков, назначенных переменной langs. По умолчанию переменная lang установлена на en. Затем эта переменная lang привязана к v-model элемента select, что позволяет пользователям легко переключаться между доступными языковыми опциями.
Когда пользователь выбирает язык из выпадающего списка, событие lang срабатывает, и выбранное значение языка передается родительскому компоненту через переменную language.value. Таким образом, выбранная настройка языка может использоваться во всем приложении.

<script setup>
import langSelection from "../data/lang.json";
import { ref } from "vue";

const langs = Object.assign(langSelection);
const lang = ref("en");
defineEmits(["lang"]);
</script>
<template>
  <div>
    <select v-model="lang" class="p-1 text-sm rounded-lg text-gray-700">
      <option
        class="block px-4 py-2 hover:bg-gray-100"
        v-for="language in langs"
        :key="language.text"
        :value="language.value"
        @click="() => $emit('lang', language.value)"
      >
        {{ language.flag }} {{ language.text }}
      </option>
    </select>
  </div>
</template>

Компонент Footer

Давайте заглянем в компонент Footer, который состоит из двух компонентов - FooterMobile.vue и FooterDesktop.vue.

FooterMobile.vue

Начнем с FooterMobile.vue - это простой элемент div, стилизованный с помощью TailwindCSS. Компонент принимает массив social в качестве свойства, который итерируется с помощью директивы v-for. Каждый объект в массиве social определен как link. Чтобы отображать ссылки на социальные сети на основе языковых настроек пользователя, директива v-if проверяет, включает ли массив link.tags строку mobile, и если он включает строку all (для всех языков) или переменную lang (для каждого конкретного языка). Если директива v-if оценивается как true, отображается элемент div с соответствующей иконкой социальной сети и ссылкой. Элемент a имеет атрибут href со значением переменной link.url, а элемент img имеет атрибут src со значением переменной link.icon. Атрибут alt имеет значение переменной link.name, которая важна для доступности веб-страниц.

<script setup>
const props = defineProps({
  lang: {
    type: String,
    required: true,
  },
  social: {
    type: Array,
    required: true,
  },
});
</script>
<template>
  <div class="flex justify-center my-2">
    <div v-for="link in social" :key="link.url">
      <div
        v-if="
          link.tags.includes('mobile') &&
          (link.tags.includes('all') || link.tags.includes(lang))
        "
        class="mx-3"
      >
        <a :href="link.url">
          <img
            :src="`/social/${link.icon}`"
            :alt="link.name"
            width="25"
            height="25"
        /></a>
      </div>
    </div>
  </div>
</template>

FooterDesktop.vue

Компонент FooterDesktop.vue отличается от компонента FooterMobile.vue только в том, что директива v-if проверяет, включает ли массив link.tags строку desktop вместо строки mobile.

<script setup>
const props = defineProps({
  lang: {
    type: String,
    required: true,
  },
  social: {
    type: Array,
    required: true,
  },
});
</script>
<template>
  <div class="flex justify-center my-2">
    <div v-for="link in social" :key="link.url">
      <div
        v-if="
          link.tags.includes('desktop') &&
          (link.tags.includes('all') || link.tags.includes(lang))
        "
        class="mx-3"
      >
        <a :href="link.url">
          <img
            :src="`/social/${link.icon}`"
            :alt="link.name"
            width="25"
            height="25"
        /></a>
      </div>
    </div>
  </div>
</template>

App.vue

Компонент App.vue является родительским компонентом. Он импортирует файл social.json и присваивает его переменной social. Переменная lang инициализируется значением en. Переменная lang передается компонентам FooterMobile.vue и FooterDesktop.vue в качестве prop. Переменная lang передается компонентам FooterMobile.vue и FooterDesktop.vue через событие lang, генерируемое компонентом LanguageSelect.vue.

useWindowSize - это функция композиции из пакета @vueuse/core. Она возвращает ширину и высоту окна браузера. Переменная width используется для определения того, какой компонент должен быть отображен - FooterMobile.vue или FooterDesktop.vue.
defineAsyncComponent - это функция из пакета vue. Она используется для определения асинхронного компонента. Компоненты FooterMobile.vue и FooterDesktop.vue импортируются асинхронно.

Функция changeLang вызывается, когда событие lang генерируется компонентом LanguageSelect.vue.

Переменная social передается компонентам FooterMobile.vue и FooterDesktop.vue в качестве prop.

<script setup>
import NavigationLanguageSelect from "./components/NavigationLanguageSelect.vue";
import { useWindowSize } from "@vueuse/core";
import { ref } from "vue";
import { defineAsyncComponent } from "vue";
import socialMedia from "./data/social.json";

const social = Object.assign(socialMedia);
const FooterMobile = defineAsyncComponent(() =>
  import("./components/FooterMobile.vue")
);
const FooterDesktop = defineAsyncComponent(() =>
  import("./components/FooterDesktop.vue")
);
const { width } = useWindowSize();
const lang = ref("en");
function changeLang(newLang) {
  lang.value = newLang;
}
</script>

<template>
  <div class="bg-pink-300">
    <nav
      class="fixed top-0 h-12 w-screen flex items-center justify-between z-10 bg-pink-300"
    >
      <div class="ml-3">
        <img
          src="./assets/logo200black.svg"
          alt="logo of Oleg Rõbnikov Web Development"
          width="150"
          height="70"
        />
      </div>
      <div class="mr-3">
        <NavigationLanguageSelect @lang="changeLang" />
      </div>
    </nav>
    <main class="h-screen mt-12 mb-10 [&>p]:m-6 [&>p]:p-2 [&>p]:leading-6">
      <h1 class="text-xl text-center m-6">Oleg Rõbnikov Web Development</h1>
      <h2 class="text-lg text-center m-4">
        Responsive and Language Sensitive Footer
      </h2>
      <p>
        Current language is changed to <strong>{{ lang }}</strong
        >. Please note that language select doesn't change the language of this
        text. It is there to demonstrate how the footer changes based on the
        language selected.
      </p>
      <p>
        Width of the window is <strong>{{ width }}px</strong>. Try to resize the
        window. The footer will change based on the width of the window.
      </p>
      <p>
        Footer is the usual place for all your social media links like Facebook,
        Twitter, etc. As your website becomes more diverse and you add languages
        and media, the footer becomes overcrowded with all the icons. Most of
        these icons are not used by one part of your audience, while others are
        a mere distraction for another part of the website's visitors
      </p>
      <p>
        Let's show our visitors links to social media that they are more likely
        to use based on their language and the type of device they are using!
      </p>
    </main>
    <footer class="fixed bottom-0 h-10 z-10 w-full">
      <FooterMobile
        v-if="width < 769"
        :lang="lang"
        :social="social"
      /><FooterDesktop v-else :lang="lang" :social="social" />
    </footer>
  </div>
</template>

Заключение

Мы надеемся, что наш метод создания адаптивного и мультиязычного футера будет полезен для вас. Если у вас возникнут какие-либо вопросы или предложения, пожалуйста, оставьте комментарий ниже. Также было бы интересно узнать, какое решение вы используете для отображения ссылок на социальные сети в своем футере. Благодарим вас за чтение!

Этот репозиторий доступен на GitHub

Комментарии (2)


  1. danilovmy
    00.00.0000 00:00
    +3

    А не проще ли было поставить фильтр на передаваемые social:

    function filterBy(social, type) {
    return social.filter( item => item.tags.includes(type))
    }

    и после

    <FooterDesktopOrMobile :lang="lang" :social="filterBy(social, width < 769 ? 'mobile' : 'desctop' )" />

    а то копипаста же лютая.


  1. LinaRSH
    00.00.0000 00:00

    Ну ради публикации можно было бы и причесать кодстайл. Например линтером или линтером с преттиером:
    - FooterMobile и FooterDesktop написаны в разном стиле, в одном варианте атрибуты написаны по одному на строку, в другом все в одной строке. Как можно исправить и автомотизировать можно посмотреть тут https://eslint.vuejs.org/rules/max-attributes-per-line.html
    - Object.assign(socialMedia) в данном коде вообще бесполезен так как мутирует исходное значение
    - Сортировка импортов отсутствует, деструктуризацию vue нужно объединить