element-ui 样式系统

记录一些我自己在看element-ui源码过程中关于样式部分的理解,如果有不准确的地方,欢迎探讨^_^。

一、样式目录与打包方式

1.1 样式代码目录

element-ui的样式代码位于packages\theme-chalk\src目录下:\

包含了公用的样式变量定义、混合宏(mixins)、函数、字体文件等和各个组件的scss样式文件。

1.2 样式代码打包

根目录下的package.json中,build:theme指令是用于打包样式的命令

"build:theme": "node build/bin/gen-cssfile && gulp build --gulpfile packages/theme-chalk/gulpfile.js && cp-cli packages/theme-chalk/lib lib/theme-chalk"
复制代码

该打包命令有3步:

  1. “node build/bin/gen-cssfile”:
    用于生成index.scss文件,生成的index.scss文件中通过@import导入了base.scss和各个组件的scss文件,那么index.scss可以作为整个组件库的样式总入口文件。

  2. “gulp build –gulpfile packages/theme-chalk/gulpfile.js”:
    packages/theme-chalk/gulpfile.js为入口执行gulp build命令;
    gulpfile.js中,以packages/theme-chalk/src下的所有scss文件为入口进行编译;
    所以既可以编译出整个组件库的样式文件index.css(以index.scss为入口编译出来的),也可以编译出各个组件单独的css样式文件(以各组件自身scss文件为入口)。

  3. “cp-cli packages/theme-chalk/lib lib/theme-chalk”:
    一个简单的拷贝操作

二、样式变量

element-ui中样式相关的scss变量都在packages\theme-chalk\src\common\var.scss文件中。
element-ui对样式变量进行了分层,尤其是对于颜色变量的处理。所谓变量分层是指变量分为3个层级:顶层变量 推导变量 组件变量

  1. 顶层变量
    包括:品牌的主色、success、warning、danger、info等常用色。如:

    $--color-primary: #409EFF !default;
    $--color-white: #FFFFFF !default;
    $--color-black: #000000 !default;
    $--color-success: #67C23A !default;
    $--color-warning: #E6A23C !default;
    $--color-danger: #F56C6C !default;
    $--color-info: #909399 !default;
    复制代码
  2. 推导变量
    即根据顶层变量,通过一定的运算推导所得到。如:

    $--color-primary-light-1: mix($--color-white, $--color-primary, 10%) !default; /* 53a8ff */
    $--color-primary-light-2: mix($--color-white, $--color-primary, 20%) !default; /* 66b1ff */
    $--color-primary-light-3: mix($--color-white, $--color-primary, 30%) !default; /* 79bbff */
    $--color-primary-light-4: mix($--color-white, $--color-primary, 40%) !default; /* 8cc5ff */
    $--color-primary-light-5: mix($--color-white, $--color-primary, 50%) !default; /* a0cfff */
    $--color-primary-light-6: mix($--color-white, $--color-primary, 60%) !default; /* b3d8ff */
    $--color-primary-light-7: mix($--color-white, $--color-primary, 70%) !default; /* c6e2ff */
    $--color-primary-light-8: mix($--color-white, $--color-primary, 80%) !default; /* d9ecff */
    $--color-primary-light-9: mix($--color-white, $--color-primary, 90%) !default; /* ecf5ff */
    复制代码
  3. 组件变量
    即组件样式中直接使用的变量

    $--button-primary-border-color: $--color-primary !default;
    复制代码

样式分层带来的好处有:

  • 代码规范,易于维护和更新;
  • 友好的主题定制功能,修改顶层变量即可达到换肤的效果。

三、按需引入时的样式导入原理

element-ui提供了按需引入的功能,旨在减少引入的组件文件体积,从而减少打包后的项目文件体积。
我们知道,element-ui的组件vue代码中并没有样式的代码,组件的样式是单独编写并打包成独立的css文件的。那么在按需引入时,是如何引入组件的样式呢。
按需引入时,我们需要使用babel-plugin-component插件,并配置:

// .babel.rc
{
  "presets": [
    ["es2015", { "modules": false }]
  ],
  "plugins": [["component", [
    {
      "libraryName": "element-ui",
      "styleLibraryName": "theme-chalk"
    }
  ]]]
}
复制代码

这是一个babel的插件,其功能其实很简单:在代码编译过程中,当检测到import element-ui的组件时,自动插入一条import 组件的css文件的语句,从而将组件的样式也引入了。

四、使用Sass混合宏(@mixin),快速编写BEM样式代码

BEM是一种CSS命令规范,作用是使css命名更加容易阅读和理解。具体可以参考这边文章:CSS — BEM 命名规范
在element-ui中,广泛使用了BEM的命名方式,而其样式代码中使用BEM的方式也值得借鉴。

4.1 定义b/m/e混合宏

packages\theme-chalk\src\mixins\mixins.scss文件中定义了混合宏b/m/e

/* BEM
 -------------------------- */
@mixin b($block) {
  $B: $namespace+'-'+$block !global;

  .#{$B} {
    @content;
  }
}

@mixin e($element) {
  $E: $element !global;
  $selector: &;
  $currentSelector: "";
  @each $unit in $element {
    $currentSelector: #{$currentSelector + "." + $B + $element-separator + $unit + ","};
  }

  @if hitAllSpecialNestRule($selector) {
    @at-root {
      #{$selector} {
        #{$currentSelector} {
          @content;
        }
      }
    }
  } @else {
    @at-root {
      #{$currentSelector} {
        @content;
      }
    }
  }
}

@mixin m($modifier) {
  $selector: &;
  $currentSelector: "";
  @each $unit in $modifier {
    $currentSelector: #{$currentSelector + & + $modifier-separator + $unit + ","};
  }

  @at-root {
    #{$currentSelector} {
      @content;
    }
  }
}
复制代码

4.2 业务scss文件中使用b/m/e混合宏

以card.scss的代码片段来看:

// card.scss
@import "mixins/mixins";
@import "common/var";

@include b(card) {
  border-radius: $--card-border-radius;
  border: 1px solid $--card-border-color;
  background-color: $--color-white;
  overflow: hidden;
  color: $--color-text-primary;
  transition: 0.3s;

  @include when(always-shadow) {
    box-shadow: $--box-shadow-light;
  }

  @include when(hover-shadow) {
    &:hover,
    &:focus {
      box-shadow: $--box-shadow-light;
    }
  }

  @include e(header) {
    padding: #{$--card-padding - 2 $--card-padding};
    border-bottom: 1px solid $--card-border-color;
    box-sizing: border-box;
  }

  @include e(body) {
    padding: $--card-padding;
  }
}
复制代码

编译后的card.css文件:

.el-card {
  border-radius: 4px;
  border: 1px solid #ebeef5;
  background-color: #fff;
  overflow: hidden;
  color: #303133;
  -webkit-transition: 0.3s;
  transition: 0.3s;
}
.el-card.is-always-shadow,
.el-card.is-hover-shadow:focus,
.el-card.is-hover-shadow:hover {
  -webkit-box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
.el-card__header {
  padding: 18px 20px;
  border-bottom: 1px solid #ebeef5;
  -webkit-box-sizing: border-box;
  box-sizing: border-box;
}
.el-card__body {
  padding: 20px;
}
复制代码

这样的BEM书写设计后续可以借鉴到自己的项目中。

五、响应式

element-ui中提供了基于媒体查询的响应式布局能力,来看看是如何实现的

5.1 响应式断点变量

packages\theme-chalk\src\common\var.scss文件中定义了关于媒体查询的响应式断点变量

/* Break-point
--------------------------*/
$--sm: 768px !default;
$--md: 992px !default;
$--lg: 1200px !default;
$--xl: 1920px !default;

// maps类型变量
$--breakpoints: (
  'xs' : (max-width: $--sm - 1),
  'sm' : (min-width: $--sm),
  'md' : (min-width: $--md),
  'lg' : (min-width: $--lg),
  'xl' : (min-width: $--xl)
);
复制代码

其中的$--breakpoints是maps类型变量

5.2 定义res混合宏

packages\theme-chalk\src\mixins\mixins.scss文件中定义了混合宏res

/* Break-points
 -------------------------- */
@mixin res($key, $map: $--breakpoints) {
  // 循环断点Map,如果存在则返回
  @if map-has-key($map, $key) {
    @media only screen and #{inspect(map-get($map, $key))} {
      @content;
    }
  } @else {
    @warn "Undefeined points: `#{$map}`";
  }
}
复制代码

代码中map-has-key map-get inspect是scss内置的函数

5.3 使用res编写响应式样式

@include res(xs) {
  .el-col-xs-0 {
    display: none;
  }
  @for $i from 0 through 24 {
    .el-col-xs-#{$i} {
      width: (1 / 24 * $i * 100) * 1%;
    }

    .el-col-xs-offset-#{$i} {
      margin-left: (1 / 24 * $i * 100) * 1%;
    }

    .el-col-xs-pull-#{$i} {
      position: relative;
      right: (1 / 24 * $i * 100) * 1%;
    }

    .el-col-xs-push-#{$i} {
      position: relative;
      left: (1 / 24 * $i * 100) * 1%;
    }
  }
}

@include res(sm) {
  // 类似代码,省略
}

@include res(md) {
  // 类似代码,省略
}

@include res(lg) {
  // 类似代码,省略
}

@include res(xl) {
    // 类似代码,省略
}
复制代码

编译出来的col.css(代码太多,就展示图片吧)
image.png

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享