众所周知,Vue 中动态绑定样式是用 :style
,或者是动态绑定 :class
class,不同的 class 样式提前写好且不一样。但是如果是 ::after
伪元素或者要改变的样式用 js 计算很复杂但是用 CSS 计算很简单的话,这种方法就略显得麻烦。有没有什么办法能用 Vue 直接改变 CSS,而不用这一套绑定的办法呢。答案是有的!
这里给出两个实现方案。
第一个方案——动态 style 标签
其实早在 Vue 0.x 和 1.x 版本这样做一度很流行(和这种升级的方案略有不同)
第一个例子
<template>
<div>
<component is="style">
.foo[data-id="{{ uniqueId }}"] {
color: {{ color }};
}
.foo[data-id="{{ uniqueId }}"] .bar {
text-align: {{ align }}
}
</component>
<div class="foo" :data-id="uniqueId">
<div class="bar">
hello world
</div>
</div>
</div>
</template>
<script>
export default {
computed: {
uniqueId() {
return 一个独一无二的id; // 是因为这样生成的 style 没有 scoped,别的组件也能使用这个样式
},
color() {
return someCondition ? 'red' : '#000';
},
align() {
return someCondition ? 'left' : 'right';
}
}
}
</script>
第二个例子
<div id="app">
<v-style>
.{{ className }} {
background: {{ bgColor }};
position: relative;
}
.{{ className }}:hover {
color: {{ hoverColor }};
}
.{{ className }}::after {
content: '';
display: block;
height: 40px;
width: 40px;
border: 1px solid black;
border-radius: 50%;
position: absolute;
top: 100%;
}
</v-style>
<div class="temp">
</div>
</div>
<script>
Vue.component('v-style', {
render: function (createElement) {
return createElement('style', this.$slots.default)
}
});
export default {
data () {
return {
className: "temp",
hoverColor: "yellow",
bgColor: "blue"
}
},
computed () {
// 这里和上面一样,所以略去生成 uniqueId 的过程
}
}
</script>
第二个方案——CSS 变量
上面的方案之所以被 Vue 官方不赞成,是因为 Vue 在每次渲染的时候会把每个组件的 style 标签单独拎出来,比较耗费性能。所以有没有一个直观的方案就是 Vue 直接操纵 CSS 呢,有的,借助 CSS 变量就可以。
<template>
<div class="test">
<span :style="spanStyle" class="span1">hello world</span>
<br>
<span :style="{'--width': widthVar}" class="span2">hello earth</span>
</div>
</template>
<script>
export default {
data() {
return {
spanStyle: {
"--color": "red"
},
widthVar: "100px"
};
}
}
</script>
<style scoped>
.span1 {
color: var(--color);
}
.span2 {
text-align: center;
position: relative;
width: var(--width);
}
.span2::after {
content: '';
display: block;
position: absolute;
left: 100%;
width: var(--width);
height: var(--width);
border-radius: 50%;
border: 2px solid black;
}
</style>
但相信大多数人写 Vue 用的都是 CSS 预处理器,这里也给出使用预处理器使用这种方案的方法(我这里给出 Sass 的用法,Stylus,Less与之类似)
<style scoped lang="sass">
// 只在使用 CSS 变量的时候和前面略有不同
.span2
width: #{'var(--width)'}
</style>
这里的#{'var(--width)'}
是 Sass 里的插值,类似于 js 里的${}
。官方的解释用法见下图。
意思就是这样插入一个字符串,在把 Sass 编译成 CSS 后会完整地保留引号之内的内容(既然 Sass 不能直接使用 CSS 变量,那就编译后使用嘛)。其它的预处理器应该都有类似的插值语法,大家在官方文档里搜索关键词 interpolation
或者quoted string
应该就可以查到了。
总结
第二种方案虽然用起来直观,但不是说第一种方案就一无是处了。第一种方案在用来动态定义全局 CSS 变量的时候很好用。例子如下
<component is="style">
:root {
--bg-color: {{bgColor}};
--box-size: {{boxSize}};
}
</component>
<script>
export default {
data () {
return {
bgColor: "white",
boxSize: "30px"
}
}
}
</script>
将二者相辅相成并且结合 Vue 原有的:class
和:style
,相信大家能写出更优雅的 Vue 代码。
来更新一下啦,又发现一个将 Sass(SCSS) 变量和 js 共享的方法,我试了一下,应该是 webpack 没配置好,没成功,大家可以试一下
SCSS 和 js 变量共享