Модульный CSS - это не особенность vue, это особенность связки css-loader + vue-loader для webpack. Роль css-loader тут в замене имен классов (а так же имен keyframes и прочих внутренних штук, но не имен тегов и не id) на хэш от пути к файлу и оригинального имени класса, а так же предоставлении объекта с маппингом оригинального имени класса в сопоставленный ему хэш. А роль vue-loader в добавлении миксина с beforeCreate хуком прокидывающим объект маппинга в this.$style
Из документации css-loader можно узнать, что любой селектор или его часть можно обернуть в конструкцию :global() чтобы указанные имена классов не переименовывались.
style[scoped]
да с этим познакомился, но тогда не будет модульности у классов
Данная штука работает на уровне самого компилятора vue и она дает модульность за счет добавления атрибута вида v-1234567 (где 1234567 - хэш от содержимого файла .vue) ко всем тегам и добавления селектора по атрибуту. Поставленную задачу это действительно решает, оставляя оригинальные классы и работая даже на имена тегов, но ценной более медленных селекторов по атрибуту.
Почему нельзя было решить эту задачу добавлением доп класса, как это сделано в svelte например, с которого Эван Ю почти полностью своровал дизайн... Это известно лишь самому Эвану, который впрочем очень часто единолично принимает не самые оптимальные по производительности решения, когда не умеет сделать нормально.
Еще одним решением будет немного покодить. Указанный в вопросе фрагмент шаблона
<template>
<div>
<div :class="test1">test1</div>
<div :class="test2">test2</div>
</div>
</template>
будет искать поля с ключами test1 и test2 в this, а значит их вполне можно туда переложить в beforeCreate хуке из this.$style, например так:
const mixin = {
beforeCreate() {
const applyClassNames = () => Object.keys(this.$style).forEach(className => {
this[className] = this.$style[className];
});
if(!this.$style) {
// наш миксин может оказаться раньше миксина лоадера
Object.defineProperty(this, '$style', {
enumerable: true,
configurable: true,
get() {},
set: $style => {
Object.defineProperty(this, '$style', {
enumerable: true,
configurable: true,
writable: true,
value: $style
};
applyClassNames();
}
});
return;
}
applyClassNames();
}
};
export default {
mixins: [mixin],
// ...
};
Правда минусом подхода будет то, что все имена классов окажутся непосредственно в this компонента, что черевато конфликтами.