На одном из проекте, я столкнулся с задачей, где нужно было адаптировать приложение на Vue.js для использования одного и тот же интерфейса, но на разных доменах и с подтягиванием разных данных (SaaS на минималках). Казалось бы, проблема легко решаемая, ведь в Nuxt 3 уже есть хук useRequestURL для таких случаев. Однако проект уже был написан на Vue 2 / Nuxt 2, и переход на новую версию только ради поддержки мультидоменности был нецелесообразен для клиента.
Перейдём сразу к реализации мультидоменности на Vue (Nuxt.js 2), используя только возможности фреймворка. Нам нужно определить домен ещё до первого запроса к API. Я перепробовал несколько подходов, включая использование middleware, но наилучший результат показало получение домена непосредственно при инициализации API.
Домен необходимо обрабатывать в двух режимах:
- Из заголовков сервера в SSR-режиме.
- При навигации между страницами через state приложения.
Настройка плагина API
Все запросы к API обрабатываются через единственный
скрипт api.js
. Это удобно, когда все запросы находятся в одном месте. Советую использовать
такой подход и в ваших приложениях.
Подключаем наш api.js в nuxt.config.js как плагин:
plugins: [
{ src: '@/plugins/api.js' },
]
//plugins/api.js
class Api {
constructor(client, domain, store) {
// Создаем экземпляр axios с заголовками
this.instance = client.create({
headers: {
'Access-Control-Allow-Origin': true,
},
});
this.store = store;
this.version = "v1";
// Определяем базовый URL для API
if (domain !== "") {
this.domain = `https://${domain}/api/${this.version}`;
} else {
this.domain = `/api/${this.version}`;
}
}
// Пример метода
async getBannersList() {
let res = {};
let errorMessageText = '';
const params = {};
await this.instance.get(`${this.domain}/banners`, params)
.then(response => {
if (response.data.success) {
res = response.data.result;
}
}).catch(error => {
console.log(error);
});
return res;
}
}
export default ({ app, store, req }, inject) => {
let domain = "";
// Получаем домен из заголовков сервера или из store
if (typeof req !== 'undefined') {
domain = req.headers.host;
} else {
domain = store.state.domain;
}
const api = new Api(app.$axios, domain, store);
inject('api', api);
};
Настройка Store
Сохраняем в стору домен, будем его получать, при навигации по сайту без перезагрузки страницы (когда не используется SSR):
// store/index.js
export const state = () => ({
domain: '',
});
export const mutations = {
SET_DOMAIN(state, domain) {
state.domain = domain;
},
};
export const actions = {
nuxtServerInit({ commit }, context) {
// Устанавливаем домен при инициализации сервера
if (context.req && context.req.headers.host) {
commit('SET_DOMAIN', context.req.headers.host);
}
},
};
Использование API в компонентах
Теперь, когда наш API настроен, давайте посмотрим, как использовать его в компонентах:
<script>
export default {
async fetch() {
await this.$api.getBannersList()
.then(response => {
this.banners = response;
});
}
}
</script>
С клиентской частью разобрались, но также нам нужно
настроить и серверную часть приложения. Чтобы наше API работало с разных доменов, нужно дополнительно
прописать заголовок Access-Control-Allow-Origin
для поддержки CORS. Реализация зависит от
того на чём разработано REST API.
Кстати, если вы ещё не знаете, что такое REST API и как его проектируют, то вам обязательно нужно заглянуть вот на эту статью.