前言
操作元素的 class 列表和内联样式是数据绑定的一个常见需求。因此,在将
v-bind
用于 class 和 style 时,Vue 做了专门的增强。本章将对此做详细的说明并额外推荐在项目中使用Sass
提升样式的编码效率。
绑定 HTML Class
对象语法
我们可以传给
v-bind:class
一个对象,以动态地切换 class:
<div :class="{ active: isActive }"></div>
上面的语法表示
active
这个 class 存在与否将取决于数据 propertyisActive
结果的真假。
你可以在对象中传入更多字段来动态切换多个 class。此外,
v-bind:class
指令也可以与普通的 class attribute 共存:
<div class="static" :class="{ active: isActive, 'text-danger': hasError }" ></div>
data() {
return {
isActive: true,
hasError: false
}
}
渲染结果:
当
isActive
或者hasError
变化时,class 列表将相应地更新。例如,如果hasError
的值为true
,class 列表将变为"static active text-danger"
。
绑定的数据对象不必内联定义在模板里:
<div :class="classObject"></div>
data() {
return {
classObject: {
active: true,
'text-danger': false
}
}
}
渲染的结果和上面一样。我们也可以在这里绑定一个返回对象的计算属性。这是一个常用且强大的模式:
data() {
return {
isActive: true,
error: null
}
},
computed: {
classObject() {
return {
active: this.isActive && !this.error,
'text-danger': this.error && this.error.type === 'fatal'
}
}
}
数组语法
我们可以把一个数组传给
v-bind:class
,以应用一个 class 列表:
<div :class="[activeClass, errorClass]"></div>
data() {
return {
activeClass: 'active',
errorClass: 'text-danger'
}
}
渲染为:
如果你也想根据条件切换列表中的 class,可以用三元表达式:
<div :class="[isActive ? activeClass : '', errorClass]"></div>
这样写将始终添加
errorClass
,但是只有在isActive
为真时才添加activeClass
。不过,当有多个条件 class 时这样写有些繁琐。所以在数组语法中也可以使用对象语法:
<div :class="[{ active: isActive }, errorClass]"></div>
绑定内联样式
对象语法
v-bind:style
的对象语法十分直观——看着非常像 CSS,但其实是一个 JavaScript 对象。CSS property 名可以用驼峰式 (camelCase) 或短横线分隔 (kebab-case,记得用引号括起来) 来命名:
<div :style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
data() {
return {
activeColor: 'red',
fontSize: 30
}
}
直接绑定到一个样式对象通常更好,这会让模板更清晰:
<div :style="styleObject"></div>
data() {
return {
styleObject: {
color: 'red',
fontSize: '13px'
}
}
}
同样的,对象语法也可以结合返回对象的计算属性使用。
数组语法
v-bind:style
的数组语法可以将多个样式对象应用到同一个元素上:
<div :style="[baseStyles, overridingStyles]"></div>
Sass
Sass 是一款强化 CSS 的辅助工具,它在 CSS 语法的基础上增加了变量、嵌套、混合、导入等高级功能,这些拓展令 CSS 更加强大与优雅,也有助于更好地组织管理样式文件,以及更高效地开发项目。
Vue 中使用 Sass
你可以在创建项目的时候选择预处理器 (Sass/Less/Stylus)。如果当时没有选好,内置的 webpack 仍然会被预配置为可以完成所有的处理。你也可以手动安装相应的 webpack loader:
npm install -D [email protected] sass
,这里我们安装两个依赖以提供在项目中使用 Sass 的能力。注意: 我们对安装的sass-loader
指定了安装版本,因为默认安装的最新版本会出现不支持当前vue-cli
内置的 webpack(至少之前我们安装的vue-cli版本不支持)。
现在我们就可以在
*.vue
文件中这样来使用:
<div class="outer-layer">
<div class="inner-layer"></div>
</div>
// 在使用 sass 时需要在 style标签上声明 lang 为 scss
// SCSS 是 Sass3 引入新的语法,其语法完全兼容 CSS3,并且继承了 Sass 的强大功能
<style lang="scss">
// sass 支持嵌套的语法
.outer-layer{
width: 200px;
height: 200px;
background-color: rgb(255, 74, 74);
display: flex;
justify-content: center;
align-items: center;
.inner-layer{
width: 120px;
height: 120px;
background-color: rgb(53, 201, 65);
}
}
</style>
渲染结果:
使用变量
sass
使用$
符号来标识变量,比如$highlight-color
和$sidebar-width
:
<style lang="scss">
$main-bgc: rgb(255, 74, 74);
$item-bgc: rgb(53, 201, 65);
.outer-layer{
width: 200px;
height: 200px;
background-color: $main-bgc;
display: flex;
justify-content: center;
align-items: center;
// 此变量仅作用当前定义的规则块
$item-size: 120px;
.inner-layer{
width: $item-size;
height: $item-size;
background-color: $item-bgc;
}
}
</style>
渲染结果和上面直接赋值的效果一致。
任何可以用作
css
属性值的赋值都可以用作sass
的变量值,甚至是以空格分割的多个属性值,如$basic-border: 1px solid black;
,或以逗号分割的多个属性值,如$plain-font: "Myriad Pro"、Myriad、"Helvetica Neue";
与
CSS
属性不同,变量可以在css
规则块定义之外存在。当变量定义在css
规则块内,那么该变量只能在此规则块内使用。如果它们出现在任何形式的{...}
块中(如@media
或者@font-face
块),情况也是如此。
sass
的变量名可以与css
中的属性名和选择器名称相同,包括中划线和下划线。这完全取决于个人的喜好,sass
并不想强迫任何人一定使用中划线或下划线,所以这两种用法相互兼容。用中划线声明的变量可以使用下划线的方式引用,反之亦然:
<style lang="scss">
$main_bgc: rgb(255, 74, 74);
$item_bgc: rgb(53, 201, 65);
.outer-layer{
width: 200px;
height: 200px;
background-color: $main-bgc;
display: flex;
justify-content: center;
align-items: center;
// 此变量仅作用当前定义的规则块
$item-size: 120px;
.inner-layer{
width: $item_size;
height: $item_size;
background-color: $item-bgc;
}
}
</style>
其渲染结果保持不变。
嵌套 CSS 规则
css
中重复写选择器是非常恼人的。如果要写一大串指向页面中同一块的样式时,往往需要 一遍又一遍地写同一个ID
:
#content article h1 {
color: #333 }
#content article p {
margin-bottom: 1.4em }
#content aside {
background-color: #EEE }
像这种情况,
sass
可以让你只写一遍,且使样式可读性更高。在Sass中,你可以像我们上一个例子那样在规则块中嵌套规则块。sass
在输出css
时会帮你把这些嵌套规则处理好,避免你的重复书写:
// 最终生成的css和上面css效果一样
#content {
article {
h1 { color: #333 }
p { margin-bottom: 1.4em }
}
aside { background-color: #EEE }
}
父选择器的标识符&
上面这样的嵌套生成的是 css 中的后代选择器,但在有些情况下你却不会希望
sass
使用这种后代选择器的方式生成这种连接,最常见的一种情况是当你为链接之类的元素写:hover
这种伪类时,你并不希望以后代选择器的方式连接。比如说,下面这种情况sass
就无法正常工作:
article a {
color: blue;
:hover { color: red }
}
这意味着
color: red
这条规则将会被应用到选择器article a :hover
,article
元素内链接的所有子元素在被hover
时都会变成红色。这是不正确的!你想把这条规则应用到超链接自身,而后代选择器的方式无法帮你实现。
解决之道是使用一个特殊的
sass
选择器,即父选择器。在使用嵌套规则时,父选择器能对于嵌套规则如何解开提供更好的控制。它就是一个简单的&
符号,且可以放在任何一个选择器可出现的地方:
article a {
color: blue;
&:hover { color: red }
}
当包含父选择器标识符的嵌套规则被打开时,它不会像后代选择器那样进行拼接,而是
&
被父选择器直接替换:
// 对应生成的css
article a {
color: blue }
article a:hover {
color: red }
群组选择器的嵌套
在
CSS
里边,.button, button
会命中 button 元素和类名为 .button 的元素。这种选择器称为群组选择器。群组选择器的规则会对命中群组中任何一个选择器的元素生效。
.button, button {
margin: 0;
}
当看到上边这段代码时,你可能还没意识到会有重复性的工作。但会很快发现:如果你需要在一个特定的容器元素内对这样一个群组选择器进行修饰,情况就不同了。
css
的写法会让你在群组选择器中的每一个选择器前都重复一遍容器元素的选择器:
.container h1, .container h2, .container h3 {
margin-bottom: .8em }
非常幸运,
sass
的嵌套特性在这种场景下也非常有用。当sass
解开一个群组选择器规则内嵌的规则时,它会把每一个内嵌选择器的规则都正确地解出来:
.container {
h1, h2, h3 {margin-bottom: .8em}
}
对于内嵌在群组选择器内的嵌套规则,处理方式也一样:
// 书写的scss
nav, aside {
a {
color: blue}
}
// 生成的css
nav a, aside a {
color: blue}
子组合选择器和同层组合选择器:>、+和~
这些组合选择器可以毫不费力地应用到
sass
的规则嵌套中。可以把它们放在外层选择器后边,或里层选择器前边。sass
会如你所愿地将这些嵌套规则一一解开组合在一起:
// 书写的scss
article {
~ article {
border-top: 1px dashed #ccc }
> section {
background: #eee }
dl > {
dt {
color: #333 }
dd {
color: #555 }
}
nav + & {
margin-top: 0 }
}
// 生成的css
article ~ article {
border-top: 1px dashed #ccc }
article > footer {
background: #eee }
article dl > dt {
color: #333 }
article dl > dd {
color: #555 }
nav + article {
margin-top: 0 }
嵌套属性
在
sass
中,除了CSS选择器,属性也可以进行嵌套。尽管编写属性涉及的重复不像编写选择器那么糟糕,但是要反复写border-style``border-width``border-color
以及border-*
等也是非常烦人的。在sass
中,你只需敲写一遍border
:
nav {
border: {
style: solid;
width: 1px;
color: #ccc;
}
}
嵌套属性的规则是这样的:把属性名从中划线-的地方断开,在根属性后边添加一个冒号:,紧跟一个
{ }
块,把子属性部分写在这个{ }
块中。就像css
选择器嵌套一样,sass
会把你的子属性一一解开,把根属性和子属性部分通过中划线-连接起来,最后生成的效果与你手动一遍遍写的css
样式一样:
nav {
border-style: solid;
border-width: 1px;
border-color: #ccc;
}
混合器
混合器使用
@mixin
标识符定义。看上去很像其他的CSS @
标识符,比如说@media
或者@font-face
。这个标识符给一大段样式赋予一个名字,这样你就可以轻易地通过引用这个名字重用这段样式。下边的这段sass
代码,定义了一个非常简单且常用的混合器,目的是快速生成一个标准的子元素居中的flex-box
:
@mixin flex-box {
display: flex;
justify-content: center;
align-items: center;
}
然后就可以在你的样式表中通过
@include
来使用这个混合器,放在你希望的任何地方。@include
调用会把混合器中的所有样式提取出来放在@include
被调用的地方。那么我们最开始的例子可以这样改造:
<style lang="scss">
$main_bgc: rgb(255, 74, 74);
$item_bgc: rgb(53, 201, 65);
@mixin flex-box {
display: flex;
justify-content: center;
align-items: center;
}
.outer-layer{
width: 200px;
height: 200px;
background-color: $main-bgc;
@include flex-box;
// 此变量仅作用当前定义的规则块
$item-size: 120px;
.inner-layer{
width: $item_size;
height: $item_size;
background-color: $item-bgc;
}
}
</style>
混合器并不一定总得生成相同的样式。可以通过在
@include
混合器时给混合器传参,来定制混合器生成的精确样式。当@include
混合器时,参数其实就是可以赋值给css
属性值的变量。这种方式跟JavaScript
的function
很像:
@mixin link-colors($normal, $hover, $visited) {
color: $normal;
&:hover { color: $hover; }
&:visited { color: $visited; }
}
当混合器被
@include
时,你可以把它当作一个css
函数来传参。如果你像下边这样写:
// 书写的scss
a {
@include link-colors(blue, red, green);
}
// 生成的css
a {
color: blue; }
a:hover {
color: red; }
a:visited {
color: green; }
为了在
@include
混合器时不必传入所有的参数,我们可以给参数指定一个默认值。参数默认值使用$name: default-value
的声明形式,默认值可以是任何有效的css
属性值,甚至是其他参数的引用,如下代码所示:
@mixin link-colors(
$normal,
$hover: red,
$visited: $normal
)
{
color: $normal;
&:hover { color: $hover; }
&:visited { color: $visited; }
}
选择器继承
使用
sass
的时候,最后一个减少重复的主要特性就是选择器继承。选择器继承是说一个选择器可以继承为另一个选择器定义的所有样式。这个通过@extend
语法实现,如下代码:
//通过选择器继承继承样式
.error {
border: 1px solid red;
background-color: #fdd;
}
.seriousError {
@extend .error;
border-width: 3px;
}
在上边的代码中,
.seriousError
将会继承样式表中任何位置处为.error
定义的所有样式。以class="seriousError"
修饰的html
元素最终的展示效果就好像是class="seriousError error"
。.seriousError
不仅会继承.error
自身的所有样式,任何跟.error
有关的组合选择器样式也会被.seriousError
以组合选择器的形式继承,如下代码:
//.seriousError从.error继承样式
.error a{ //应用到.seriousError a
color: red;
font-weight: 100;
}
h1.error { //应用到hl.seriousError
font-size: 1.2rem;
}
Sass文件的导入
css
有一个特别不常用的特性,即@import
规则,它允许在一个css
文件中导入其他css
文件。然而,后果是只有执行到@import
时,浏览器才会去下载其他css
文件,这导致页面加载起来特别慢。sass
也有一个@import
规则,但不同的是,sass
的@import
规则在生成css
文件时就把相关文件导入进来。这意味着所有相关的样式被归纳到了同一个css
文件中,而无需发起额外的下载请求。
所以,针对常用公共sass变量、混合等都应该提取到一个sass文件以提升代码的复用率和规划项目的样式管理。现在我们再次改造最初的例子:
// 在新建 assets/common.scss 中写入
$main_bgc: rgb(255, 74, 74);
$item_bgc: rgb(53, 201, 65);
@mixin flex-box {
display: flex;
justify-content: center;
align-items: center;
}
// 引入scss文件
<style lang="scss">
@import './assets/common.scss';
.outer-layer{
width: 200px;
height: 200px;
background-color: $main-bgc;
@include flex-box;
// 此变量仅作用当前定义的规则块
$item-size: 120px;
.inner-layer{
width: $item_size;
height: $item_size;
background-color: $item-bgc;
}
}
</style>
其最终效果依然不变。
内容预告
本章我们介绍了 Vue 的样式绑定以及介绍能有效提高我们编码效率的 Sass,在下一章节,我们将深入 Vue 的底层原理,进行 Vue 响应式原理的说明。