【Vue3.2】styleタグ内でJavaScript変数をバインドできる
Vue.js3.2からは、JavaScriptの変数をCSS変数としてバインドできるようになりました。
つまりは、CSSの値を動的に設定できるということです。
ものは試しのやってみましょう。
<script setup lang="ts">
import { ref } from "@vue/reactivity";
const color = ref("#000000");
</script>
<template>
<h1 class="title">タイトル</h1>
<input type="color" v-model="color" />
</template>
<style>
.title {
color: v-bind(color);
}
</style>
<script>
タグ内で宣言したcolor
という変数を<style>
タグ内でv-bind(color);
として使用しています。
生成されたCSSを見てみると、以下のような感じになっています。
-
--7ba5bd90-color
のようにハッシュ化されたCSS変数がインラインスタイルとして定義されます。 -
<style>
タグでバインドしたJavaScippt変数は上記のハッシュ化されたCSS変数が適用されます。
もちろん、JavaScriptで宣言したリアクティブな変数の値が変更されるたびにCSS変数の値も変化されます。
ここまでが基本的な機能の説明です。他にもいろいろ試してみましょう。
JavaScript式の展開
VSCodeにはなにか怒られるけれど、<template>
でのv-bind
と同じく三項演算子等JavaScriptの式を評価させることができます。
JavaSciptの式を評価させる場合にはクォートで加工必要があるようです。
<script setup lang="ts">
import { ref } from "@vue/reactivity";
const theme = ref<"dark" | "light">("dark");
const toggleTheme = () => {
if (theme.value === "dark") {
theme.value = "light";
} else {
theme.value = "dark";
}
};
</script>
<template>
<div class="container">
<h1 class="title">タイトル</h1>
<button @click="toggleTheme">テーマ変更</button>
</div>
</template>
<style scoped>
.container {
height: 100vh;
width: 100vw;
background-color: v-bind('theme === "dark" ? "#000": "#fff"');
color: v-bind('theme === "dark" ? "#fff": "#000"');
}
</style>
配列やオブジェクトを渡す
v-bind
に配列を渡すとカンマ区切りで展開されます。単純にarray#toString
が呼ばれているようです。
.title {
color: v-bind([ "red", "green", "blue" ]);
}
style attribute {
--7ba5bd90-colors: red,green,blue;
}
オブジェクトも同様です。
<script setup lang="ts">
import { ref } from "@vue/reactivity";
const themes = ref({
light: {
textColor: "#fff",
},
dark: {
textColor: "#000",
},
});
</script>
<style scoped>
.title {
color: v-bind(themes);
}
</style>
style attribute {
--7ba5bd90-themes: [object Object];
}
v-bindが使える場所
<style>
タグ内ならどこでもv-bind
が使えるわけではないっぽい。
<style scoped>
// Unknown word
.container {
v-bind(container)
}
</style>
<style scoped>
// Unknown word
v-bind(container)
</style>
関数呼び出し
JavaScriptの式を評価できるので、こちらも当然できますね。
<script setup lang="ts">
const textColor = () => {
return Math.random() < 0.5 ? "red" : "blue";
};
</script>
<template>
<div class="container">
<h1 class="title">タイトル</h1>
</div>
</template>
<style scoped>
.title {
color: v-bind("textColor()");
}
</style>
関数宣言でも有効です
<script setup lang="ts">
function textColor() {
return Math.random() < 0.5 ? "red" : "blue";
}
</script>
<template>
<div class="container">
<h1 class="title">タイトル</h1>
</div>
</template>
<style scoped>
.title {
color: v-bind("textColor()");
}
</style>
CSSのコメントで囲った文字列をバインドする
CSSのコメントで囲った箇所はしっかり消えていますね。
<script setup lang="ts">
const color = `/* red */blue`;
</script>
<template>
<div class="container">
<h1 class="title">タイトル</h1>
</div>
</template>
<style scoped>
.title {
color: v-bind(color);
}
</style>
element.style {
--7ba5bd90-color: blue;
}
v-bindでCSS変数の参照
これはできないです。
<script setup lang="ts">
const color = "--main-bg-color";
</script>
<template>
<div>
<h1 class="color">タイトル</h1>
<button class="color" @click="onclick">Button</button>
</div>
</template>
<style>
:root {
--main-bg-color: brown;
}
</style>
<style scoped>
.color {
color: var(v-bind(color));
}
</style>
これはできます。
<script setup lang="ts">
const color = "var(--main-bg-color)";
</script>
<template>
<div>
<h1 class="color">タイトル</h1>
<button class="color" @click="onclick">Button</button>
</div>
</template>
<style>
:root {
--main-bg-color: brown;
}
</style>
<style scoped>
.color {
color: v-bind(color);
}
</style>
バインドしたCSSクラスを使用している要素のstyleを動的nullに変更すると・・・?
つまりこういうこと
<script setup lang="ts">
import { ref } from "vue";
const style = ref<any>({ fontSize: "40px" });
const onclick = () => {
style.value = null;
};
const color = "red";
</script>
<template>
<h1 class="color" :style="style">タイトル</h1>
<button class="color" @click="onclick">Button</button>
</template>
<style scoped>
.color {
color: v-bind(color);
}
</style>
正しい動作なのかよくわからないですが、style
をバインドしていた場合その値をnull
にするとインラインスタイルに展開されていたCSS変数も消えてしましまいます。
ちなみに、<template>
にルート要素がある場合その要素に対してCSS変数が展開されるので、ルート要素のstyleをnull
にするとすべてのCSS変数が消えてしまいます。
<script setup lang="ts">
import { ref } from "vue";
const style = ref<any>({ fontSize: "20px" });
const onclick = () => {
style.value = null;
};
const color = "red";
</script>
<template>
<div :style="style">
<h1 class="color">タイトル</h1>
<button class="color" @click="onclick">Button</button>
</div>
</template>
<style scoped>
.color {
color: v-bind(color);
}
</style>
Discussion