Vue.js/NuxtJS — как создавать компоненты с дизайном по умолчанию, который можно настроить с помощью файла конфигурации JSON.

avatar
Théo Lavaux
8 августа 2021 в 17:54
86
1
3

Я разрабатываю веб-сайт NuxtJS, и страницы/компоненты могут иметь либо общий дизайн по умолчанию, либо тот, который настраивается клиентом и будет указан в URL-адресе.

Что-то в строках:

http://localhost:3000/registration — для общей страницы

http://localhost:3000/client-name/registration — страница для конкретного клиента

Для достижения этой цели у меня есть файл конфигурации JSON для каждого клиента (скажем, client-name.json) со следующей структурой.

{
  "some_configuration_property": {},
  "another_configuration_property": {},
  "design": {
    "logoUrl": "/assets/client-name/logo.png",
    "backgroundColor": "#000000",
    "primaryColor": "#ffffff",
    "secondaryColor": "#ffff00"
  },
}

Для начала я реализовал систему маршрутизации и могу успешно читать конфигурацию каждого клиента на основе текущего маршрута (внутри тега <script> файла Vue этого маршрута), внутри метода настройки (я использую @nuxt /состав-апи).

Проблема, с которой я сейчас столкнулся, заключается в том, чтобы выяснить, как передать эти «переменные проекта» в тег <style> моего файла Vue, который использует SCSS. Поведение, которое я хотел реализовать, заключалось в том, чтобы иметь дизайн по умолчанию для определенного компонента/страницы, но это могло быть переопределено этими «переменными дизайна», характерными для каждого клиента.

  • Первое, что пришло мне в голову, это использовать переменные CSS, которые позволит мне создавать переменные со значениями по умолчанию, но я сможет переопределить внутри стилей. Я создал образец компонента для тестирования, и он работал со свойствами CSS и псевдоэлементом v-deep. Однако это означает, что мне придется создавать класс для каждого клиента в настраиваемом компоненте, а этого я бы хотел избежать. Сначала я подумал об этом подходе, потому что он дал бы мне большую гибкость в выборе использования этих цветов дизайна внутри стилей.

Пример:

// From the customizable component
.my-button {
   color: (--button-color, teal);
}

// Styling from a parent component/view
// Had to create a selector with a style like <div> for superior specificity though, not so clean
v::deep {
  div {
    &.my-button {
      --button-color: purple;
    }
  }
}
  • Я видел селектор /deep/ или псевдоселектор ::v-deep, но я не думаю, что это очень чистое решение, поскольку оно будет часто использоваться в кодовой базе. Компонент стиля от родителей сделал бы код трудно поддерживаемым.

  • Еще один подход может заключаться в передаче переменной, например, classArray, внутри метода настройки для динамического связывания классов CSS с элементами DOM. Хотя было бы слишком обременительно создавать класс CSS для каждого клиента со связанными стилями.

Вот так:

<template>
  <my-button :class="classArray"></my-button>
</template>

<script lang="ts">
import { defineComponent } from '@nuxtjs/composition-api'

export default defineComponent({
  name: 'MyPage',
  setup() {
    const clientName = 'someClientName';
    const classArray = [clientName]
    return { classArray };
  },
})
</script>

<style lang="scss" scoped>
.someClientName {
  // some custom styles
}
</style>

Как бы вы поступили в этой ситуации?

Спасибо за помощь!

Источник
Estus Flask
8 августа 2021 в 18:45
0

Я не уверен, что слежу за проблемой с CSS vars parent/child. Но таким образом вы потеряете функции SASS (манипуляции с цветом и т. д.). Самый простой способ — coderhelper.com/questions/62370457/… .

Théo Lavaux
8 августа 2021 в 19:20
0

Я уже использую nuxt-style-resources для глобальных переменных Sass! Но это не объясняет, как я мог создать общий код для каждого клиента, мне пришлось бы создавать определенные стили на каждой странице или компоненте, верно? Или вы имеете в виду другой подход?

Estus Flask
8 августа 2021 в 21:13
0

Да, я имел в виду, что для этого нужно скомпилировать приложение для каждого клиента, что, вероятно, не то, на что вы надеялись, но, вероятно, единственный вариант, который не использует переменные CSS. Можете ли вы прояснить проблему с переменными CSS? Вы можете определить их динамически при загрузке конфигурации с помощью setProperty, затем их можно использовать с функцией SASS, например. primaryColor(), который генерирует что-то вроде var(--primary-color, $defaultPrimaryColor).

Théo Lavaux
9 августа 2021 в 08:10
0

Я заставил свойства CSS работать вместе с v-deep! Но проблема по-прежнему в том, что мне придется создавать класс для каждого клиента в настраиваемом компоненте, а этого я бы хотел избежать. Вы говорите об этом setProperty? developer.mozilla.org/en-US/docs/Web/CSS/…

Théo Lavaux
9 августа 2021 в 08:53
0

Отредактировал мой ответ, чтобы добавить рабочий код CSS vars

Estus Flask
9 августа 2021 в 12:09
0

Глубокие селекторы выглядят громоздкими и не используют SASS для создания тем. Да, это то, что я имел в виду. Я предлагаю по-прежнему использовать SASS, только вам нужно использовать такие функции, как primaryColor(), вместо прямого использования переменных, таких как $primaryColor. А --primary-color и т. д. определяются для всего документа во время выполнения с помощью setProperty.

Théo Lavaux
9 августа 2021 в 13:02
0

Не могли бы вы поделиться примером, чтобы было легче понять? setProperty используется для определения встроенных стилей элемента, не лучше ли определить эти свойства CSS в глобальном файле SCSS для атрибута :root?

Théo Lavaux
9 августа 2021 в 13:39
0

Я не понимаю, как вы будете настраивать компонент для конкретного клиента с вашим решением. Хорошо, вы используете функцию SASS для возврата var(--primary-color, $defaultPrimaryColor), но если --primary-color определено в документе, это не решает мою проблему, потому что он не может быть привязан к URL-адресу, как я объяснил в своем ответе. Напротив, я хочу иметь возможность переопределить --primary-color из тега родительского компонента style.

Estus Flask
9 августа 2021 в 13:42
0

Пожалуйста, уточните, почему вам нужно сделать это для каждого компонента. Вопрос, кажется, относится к теме глобального клиента. В любом случае, переменная может быть определена для любого элемента в DOM, поскольку она каскадная, как и другие стили.

Estus Flask
9 августа 2021 в 13:53
0

не лучше ли определить эти свойства CSS в глобальном файле SCSS для атрибута :root? — да, но у вас нет этих свойств во время сборки. Если вы не хотите перестраивать SCSS «на лету» для каждой страницы, что не является хорошей идеей в продакшене, переменные должны устанавливаться динамически, как только они становятся доступными, то есть при загрузке конфигурации.

Ответы (1)

avatar
Estus Flask
9 августа 2021 в 13:49
1

Если во время выполнения необходимо загрузить пользовательскую конфигурацию темы, для этого необходимо использовать переменные (свойства) CSS. Они могут быть заключены в функции SCSS и иметь резервные темы по умолчанию:

.
// theme.scss
$primaryColor: #abc;
// $buttonColor: $primaryColor

@function primaryColor() {
  @return #{var(--primary-color, $primaryColor)}
}

@function buttonColor() {
  @return #{var(--button-color, primaryColor())}
}

Затем primaryColor() и т. д. используются вместо прямого использования $primaryColor и т. д., как это делается в обычной теме SCSS:

// From the customizable component
.my-button {
   color: buttonColor();
}

И пользовательская тема может быть применена при загрузке ко всему документу или его части (иерархии компонентов), на которые должна влиять пользовательская тема:

const config = await loadClientConfig();
document.documentElement.style.setProperty(--primary-color, config.design.primaryColor)
Théo Lavaux
9 августа 2021 в 14:52
0

Я не правильно понял ваше решение, теперь я понял, спасибо, что поделились своей точкой зрения!