Skip to content

主题使用sass 五部曲 ​

核心 ​

注意

  1. 设定公共变量值(globals)

  2. 定义私有变量,私有变量的值就是公共变量的名称

  3. 定义一个方法循环出来

  4. 调用这个方法.放到私有类名或者root下面

流程 ​

  1. 创建 html 里面的类名生成

  2. 利用 type 生成主色调和色阶色

  3. 构建 创建变量的规范

  4. 构建 创建变量的方法

  5. 把变量名绑定到第二步生成的值

  6. 私有变量三部曲

  • 自定义私有变量比如button的私有变量

  • 私有变量的值对应全局变量

  • 定义循环私有变量的方法

  • 在类名里面调用这个方法

创建 html 里面的类名生成 ​

(1) 创建 一个usenamespace文件,用于生成类名 ​

ts
// 这是第一步 为了给html 生成 类名
// 默认的命名空间

export const defaultNamespace = "Y";

/*
 * 通过bem的方法生成所有的命名规则
 */

const _bem = (
  namespace: string,
  block: string,
  element?: string,
  modifierAttr?: string,
  modifierValue?: string,
): string => {
  // block
  let classname = `${namespace}-${block}`; // 结果Y-Buton
  // element
  element && (classname += `__${element}`); // 结果Y-Button__icon
  // modifier
  modifierAttr && (classname += `--${modifierAttr}`); // 结果Y-Button--disabled
  // modifierValue
  modifierValue && (classname += `_${modifierValue}`); // 结果Y-Button--disabled_disabled
  return classname;
};

// 最后 归纳
export const useNamespace = (block: string) => {
  // 默认
  const namespace = defaultNamespace;
  // block
  const b = () => _bem(namespace, block);
  // element
  const e = (element: string | undefined) =>
    element ? _bem(namespace, block, element) : "";
  // modifier
  const m = (
    modifierAttr: string | undefined,
    modifierValue?: string | undefined,
  ) =>
    modifierAttr ? _bem(namespace, block, "", modifierAttr, modifierValue) : "";
  // is
  const is = (name: string, state: boolean) => {
    return name && state ? `is-${name}` : "";
  };
  return {
    is,
    namespace,
    b,
    e,
    m,
  };
};

(2) 在组件里面使用 ​

vue
<template>
  <button
    :class="[
      ns.b(),
      ns.m(props.type),
      ns.m('size', props.size),
      ns.is('round', props.round),
      ns.is('disabled', props.disabled),
      ns.is('text', props.text),
      ns.is('link', props.link),
      ns.is('block', props.block),
      ns.is('border', props.border),
      ns.is('circle', props.circle),
    ]"
    :disabled="disabled"
  >
    <i
      v-if="props.prefix"
      class="iconfont"
      :class="[ns.e('icon'), props.prefix]"
    ></i>
    <span>
      <slot></slot>
    </span>
    <!--后缀图标-->
    <i
      v-if="props.suffix"
      class="iconfont"
      :class="[ns.e('icon'), props.suffix]"
    ></i>
  </button>
</template>

<script setup lang="ts">
import { onMounted } from "vue";
import { useNamespace } from "../../utils/usenamespace";

const props = defineProps({
  type: {
    type: String,
    default: "",
  },
  text: {
    type: Boolean,
    default: false,
  },
  round: {
    type: Boolean,
    default: false,
  },
  disabled: {
    type: Boolean,
    default: false,
  },
  link: {
    type: Boolean,
    default: false,
  },
  block: {
    type: Boolean,
    default: false,
  },
  border: {
    type: Boolean,
    default: false,
  },
  size: {
    type: String,
    default: "", // 小small,中 就不用写 大large
  },
  circle: {
    type: Boolean,
    default: false,
  },
  // loading
  loading: {
    type: Boolean,
    default: false,
  },
  // 图标前面
  prefix: {
    type: String,
    default: "",
  },
  // 图标后面
  suffix: {
    type: String,
    default: "",
  },
  // loading事件 beforeChange 事件和click事件只会执行beforeChange事件
  beforeChange: {
    type: Function,
    default: undefined,
  },
});

const ns = useNamespace("button");

onMounted(() => {
  console.log("按钮组件挂载");
});
</script>

(3) 生成的结果 ​

html
<button class="Y-button Y-button--primary Y-button--size">
  <i class="iconfont Y-button__icon icon-LOADING"></i>
  <span>测试图标1</span>
  <!--后缀图标--><!--v-if-->
</button>

利用sass生成主色调和层次色 ​

  • vars.scss(定义所有的变量,最后通过$global导出),在通过方法绑定到全局 这个就是全局变量
scss
// 第2步 创建主色调和主色调层次色
@use "sass:map";
@use "sass:math";
@use "sass:color";
// type 分成5种
$types: primary, success, warning, error;
// sizes 有三种 small, default, large
$sizes: small, default, large;

/* 定义主色调 */

// 主色调初始化
$colors: () !default;

// 主色调合并

$colors: map.deep-merge(
  (
    // 白色
    "white": #ffffff,
    // 黑色
    "black": #000000,
    // 主要
    "primary": (
        "base": #3069ff,
      ),
    // 成功
    "success": (
        "base": #14cd70,
      ),
    // warning
    "warning": (
        "base": #ffa81a,
      ),
    // error
    "error": (
        "base": #ff4a5b,
      )
  ),
  $colors
);

// 获取主色调

$color-white: map.get($colors, "white") !default;

$color-black: map.get($colors, "black") !default;

$color-primary: map.get($colors, "primary", "base") !default;

$color-success: map.get($colors, "success", "base") !default;

$color-warning: map.get($colors, "warning", "base") !default;

$color-error: map.get($colors, "error", "base") !default;

/** 

* 设置颜色层次比如 

* (error:('base': #ff4a5b,"light-9": rgb(255, 236.9, 238.6), "light-8": rgb(255, 218.8, 222.2), ))

* 设置颜色层次

* $type 类型 primary, success, warning, error

* $number 百分比计算器

* $mode 标识

* $mix-color 混合种颜色 

* 最后 !global 设为关键色
*/

@mixin set-color-level($type, $number, $mode: "light", $mix-color) {
  $colors: map.deep-merge(
    (
      $type: (
        "#{$mode}-#{$number}": color.mix(
            $mix-color,
            map.get($colors, $type, "base"),
            math.percentage(math.div($number, 10))
          ),
      ),
    ),
    $colors
  ) !global;
}

// 开始生成

@each $type in $types {
  @for $i from 1 through 9 {
    @include set-color-level($type, $i, "light", $color-white);
  }
}
/**

* @debug $colors;

* 结果:

*   (
    // 白色
    'white':#ffffff,
    // 黑色
    'black':#000000,
    // 主要
    'primary':(
      "base": #3069ff,
      "light-9": rgb(234.3, 240, 255), 
      "light-8": rgb(213.6, 225, 255), 
      "light-7": rgb(192.9, 210, 255), 
      "light-6": rgb(172.2, 195, 255), 
      "light-5": rgb(151.5, 180, 255), 
      "light-4": rgb(130.8, 165, 255), 
      "light-3": rgb(110.1, 150, 255), 
      "light-2": rgb(89.4, 135, 255), 
      "light-1": rgb(68.69999999999999, 120, 255), 
    ),
    // 成功
    'success':(
      "base": #14cd70,
      "light-9": rgb(231.5, 250, 240.7), 
      "light-8": rgb(208, 245, 226.4), 
      "light-7": rgb(184.5, 240, 212.10000000000002), 
      "light-6": rgb(161, 235, 197.8), 
      "light-5": rgb(137.5, 230, 183.5), 
      "light-4": rgb(114, 225, 169.2), 
      "light-3": rgb(90.5, 220, 154.89999999999998), 
      "light-2": rgb(67, 215, 140.60000000000002), 
      "light-1": rgb(43.49999999999999, 210, 126.29999999999998), 
    ),
    // warning 
    'warning':(
      "base": #ffa81a,
      "light-9": rgb(255, 246.3, 232.1), 
      "light-8": rgb(255, 237.6, 209.2), 
      "light-7": rgb(255, 228.9, 186.3), 
      "light-6": rgb(255, 220.2, 163.4), 
      "light-5": rgb(255, 211.5, 140.5), 
      "light-4": rgb(255, 202.8, 117.6), 
      "light-3": rgb(255, 194.1, 94.7), 
      "light-2": rgb(255, 185.4, 71.8), 
      "light-1": rgb(255, 176.70000000000002, 48.89999999999999), 
    ),
    // error
    'error':(
      'base': #ff4a5b,
      "light-9": rgb(255, 236.9, 238.6), 
      "light-8": rgb(255, 218.8, 222.2),
      "light-7": rgb(255, 200.7, 205.8), 
      "light-6": rgb(255, 182.6, 189.4), 
      "light-5": rgb(255, 164.5, 173), 
      "light-4": rgb(255, 146.4, 156.6), 
      "light-3": rgb(255, 128.3, 140.2), 
      "light-2": rgb(255, 110.2, 123.8), 
      "light-1": rgb(255, 92.1, 107.4), 
    ) 
*/

/** 边框 */
$border-color: () !default;
$border-color: map.deep-merge(
  (
    "default": #e2e6f1,
    // 边框主色
    "disabled": #ebeef5,
    // 边框禁用
    "hover": #c9cdd9,
    // 鼠标悬停
  ),
  $border-color
);

/** 文字颜色 */
$text-color: () !default;
$text-color: map.deep-merge(
  (
    "primary": #4d5059,
    // 文字主色
    "secondary": #7c808c,
    // 文字辅色
    "disabled": #abb1bf,
    // 文字禁用
    "placeholder": #c9cdd9,
    // 文字占位
    "light-8": #f5f6fa,
    // 文字占位
    "light-9": #fafbff,
    // 文字占位
  ),
  $text-color
);

/** 颜色 */
$color: () !default;
$color: map.deep-merge(
  (
    "white": $color-white,
    // 白色
    "black": $color-black,
    // 黑色
    "disabled-bg": "#F5F6FA",
  ),
  $color
);

/** 组件尺寸 */
$component-size: () !default;
$component-size: map.deep-merge(
  (
    "small": 24px,
    // 小的
    "default": 32px,
    // 默认的
    "large": 40px,
    // 大的
  ),
  $component-size
);

/** 文字尺寸 */
$font-size: () !default;
$font-size: map.deep-merge(
  (
    "small": 12px,
    // 小的
    "default": 14px,
    // 默认的
    "large": 16px,
    // 大的
  ),
  $font-size
);

/** 全局配置 */
$global: (
  // 边框颜色
  "border-color": $border-color,
  // 文字颜色
  "text-color": $text-color,
  // 边框颜色
  "component-size": $component-size,
  // 文字颜色
  "font-size": $font-size,
  // 颜色
  "color": $color
);

创建变量名配置文件 ​

注意

示例:color:#ff4a5b 但是我们必须得把#ff4a5b换成一个变量,这样主题色替换的时候 我们直接改变变量就可以了

  • config.scss(生成类名的方式尊重bem规范)
scss
$namespace: "y" !default; // 前缀
$connect: "-" !default; // 块、子级
$element-connect: "__" !default; // 元素
$modifier-connect: "--" !default; // 修改器
$modifier-value-connect: "_" !default; // 修改器的值
$state-prefix: "is-" !default; // 状态前缀,如禁用、圆角、加载等

创建生成变量名的方法 ​

  • function.scss
scss
@use "config.scss" as *;

/** 组合变量名称 */
@function createVarName($list) {
  $name: "--" + $namespace; // --拼接前缀
  @each $item in $list {
    // each循环
    @if $item != "" {
      // 判断不为空时执行
      $name: $name + "-" + $item; // 开始拼接
    }
  }
  @return $name; // 返回结果
}

/** 获取变量名称 */
@function getVarName($args...) {
  @return createVarName(($args));
}

/**

@debug getVarName('color', 'priamry', 'light', 2);

- 结果 --y-color-priamry-light-2

**/

把变量名绑定到生成的值 ​

  • mixins.scss
scss
// 第五步把变量名和第二步的值对应起来
@use "sass:map";
@use "function" as *;
@use "var.scss" as *;
@use "config.scss" as *;

// 暴露出去的变量
@forward "function";
@forward "var";

// 绑定主色调

@mixin set-color() {
  @each $type in $types {
    // 定义一个变量
    $color: map.get($colors, $type, "base");
    // 赋值
    #{createVarName(('color', $type))}: #{$color};
  }
}

/** 
* 结果
* --y-color-primary: #3069ff;
* --y-color-success: #14cd70;
* --y-color-warning: #ffa81a;
* --y-color-error: #ff4a5b;
**/

// 绑定主色调层次色

@mixin set-color-light() {
  @each $type in $types {
    @for $i from 1 through 9 {
      // 定义一个变量
      $color: map.get($colors, $type, "light-#{$i}");
      // 赋值
      #{createVarName(('color', $type,'light' ,$i))}: #{$color};
    }
  }
}

/** 
* 结果
* --y-color-primary-light-1: rgb(68.7, 120, 255);
* --y-color-primary-light-2: rgb(89.4, 135, 255);
* --y-color-primary-light-3: rgb(110.1, 150, 255);
* --y-color-primary-light-4: rgb(130.8, 165, 255);
* --y-color-primary-light-5: rgb(151.5, 180, 255);
* --y-color-primary-light-6: rgb(172.2, 195, 255);
* --y-color-primary-light-7: rgb(192.9, 210, 255);
* --y-color-primary-light-8: rgb(213.6, 225, 255);
* --y-color-primary-light-9: rgb(234.3, 240, 255);
**/

// 绑定全局变量看的是global.scss
// 拿border-color 举例
// $key= border-color
// $data = {default:xxx,disabled:xxxx}
// $type = default或者disabled
// $value = 就是生成的变量 #e2e6f1,

@mixin set-global-var() {
  @each $key, $data in $global {
    @if $data {
      // 判断是否存在数据
      @each $type, $value in $data {
        #{createVarName(($key, $type))}: #{$value};
      }
    }
  }
}

/** 结果
* --y-border-color-default: #e2e6f1;
* --y-border-color-disabled: #ebeef5;
* --y-border-color-hover: #c9cdd9;
* --y-padding-small: 6px;
* --y-padding-default: 10px;
* --y-padding-large: 10px;
*/

// 设置组件的变量 对应的就是componentVar.scss 文件
@mixin set-component-var($name, $var, $varKey: "default") {
  @each $key, $value in $var {
    $varName: getVarName($name, $key);
    $val: map.get($var, $key, $varKey);
    @if ($val) {
      #{$varName}: var(#{$val});
    }
  }
}

:root {
  // 绑定主色调
  @include set-color();
  // 绑定主色调层次色
  @include set-color-light();
  // 绑定全局变量
  @include set-global-var();
}

组件里面调用 ​

html
<style scoped lang="scss">
  .Y-button {
    padding: var(#{--y-padding-large});
  }
</style>

自定义私有变量(Button) ​

componentVar.scss ​

注意

这里就相当于boder-color:("default":--y-border-color-default)

scss
@use "function" as *;
// 按钮组件配置
@function buttonVar($type: "") {
  $button: (
    // 默认
    "border-color": (
        "default": getVarName("border-color", "default"),
        "type": getVarName("color", $type),
      ),
    "text-color": (
      "default": getVarName("text-color", "primary"),
      "type": getVarName("color", "white"),
    ),
    "bg-color": (
      "default": getVarName("color", "white"),
      "type": getVarName("color", $type),
    ),
    "is-link-color": (
      "default": getVarName("text-color", "primary"),
      "type": getVarName("color", $type),
    ),
    "is-text-color": (
      "default": getVarName("text-color", "primary"),
      "type": getVarName("color", $type),
    ),
    "is-text-bg-color": (
      "default": getVarName("color", "white"),
      "type": getVarName("color", "white"),
    ),
    // hover
    "hover-text-color": (
        "default": getVarName("color", "primary"),
        "type": getVarName("color", "white"),
      ),
    "hover-bg-color": (
      "default": getVarName("color", "primary", "light-9"),
      "type": getVarName("color", $type, "light-3"),
    ),
    "hover-border-color": (
      "default": getVarName("color", "primary", "light-7"),
      "type": getVarName("color", $type, "light-3"),
    ),
    "hover-is-link-color": (
      "default": getVarName("text-color", "secondary"),
      "type": getVarName("color", $type, "light-4"),
    ),
    "hover-is-text-color": (
      "default": getVarName("text-color", "primary"),
      "type": getVarName("color", $type),
    ),
    "hover-is-text-bg-color": (
      "default": getVarName("text-color", "light-8"),
      "type": getVarName("color", $type, "light-9"),
    ),
    // disabled
    "disabled-text-color": (
        "default": getVarName("text-color", "disabled"),
        "type": getVarName("color", "white"),
      ),
    "disabled-bg-color": (
      "default": getVarName("color", "disabled-bg"),
      "type": getVarName("color", $type, "light-5"),
    ),
    "disabled-border-color": (
      "default": getVarName("border-color", "disabled"),
      "type": getVarName("color", $type, "light-5"),
    ),
    "disabled-is-link-color": (
      "default": getVarName("text-color", "disabled"),
      "type": getVarName("color", $type, "light-5"),
    ),
    "disabled-is-text-color": (
      "default": getVarName("text-color", "disabled"),
      "type": getVarName("color", $type, "light-5"),
    ),
    "disabled-is-text-bg-color": (
      "default": getVarName("color", "white"),
      "type": getVarName("color", "white"),
    ),
    "size": (
      "default": getVarName("component-size", "default"),
    ),
    "font-size": (
      "default": getVarName("font-size", "default"),
    )
  );
  @return $button;
}

把值写到全局变量里面 ​

  • vars.scss 举个例子
scss
$border-color: () !default;
$border-color: map.deep-merge(
  (
    "default": #e2e6f1,
    // 边框主色
    "disabled": #ebeef5,
    // 边框禁用
    "hover": #c9cdd9,
    // 鼠标悬停
  ),
  $border-color
);

/** 全局配置 */
$global: (
  // 边框颜色
  "border-color": $border-color
);

定义循环变量的方法 ​

  • 这布在上面已经写了.这里在复述下

  • set-global-var() 这里就是把全局变量都循环出来.我放在root下面了

  • set-component-var() 这里就是组件私有变量循环出来.我后面会放到对应的Y-button里面或者其他的类名

  • 顺序就是 定义全局->绑定全局->定义私有->私有属性的值就是全局变量-> 绑定私有

scss
// 绑定主色调层次色

@mixin set-color-light() {
  @each $type in $types {
    @for $i from 1 through 9 {
      // 定义一个变量
      $color: map.get($colors, $type, "light-#{$i}");
      // 赋值
      #{createVarName(('color', $type,'light' ,$i))}: #{$color};
    }
  }
}

// 循环全局变量看的是global.scss
// 拿border-color 举例
// $key= border-color
// $data = {default:xxx,disabled:xxxx}
// $type = default或者disabled
// $value = 就是生成的变量 #e2e6f1,
@mixin set-global-var() {
  @each $key, $data in $global {
    @if $data {
      // 判断是否存在数据
      @each $type, $value in $data {
        #{createVarName(($key, $type))}: #{$value};
      }
    }
  }
}

// 循环私有设置组件的变量 对应的就是componentVar.scss 文件
@mixin set-component-var($name, $var, $varKey: "default") {
  @each $key, $value in $var {
    $varName: getVarName($name, $key);
    $val: map.get($var, $key, $varKey);
    @if ($val) {
      #{$varName}: var(#{$val});
    }
  }
}

:root {
  // 绑定主色调
  @include set-color();
  // 绑定主色调层次色
  @include set-color-light();
  // 绑定全局变量
  @include set-global-var();
}

私有变量调用 ​

html
<style lang="scss" scoped>
  // 调用私有变量
  @use "../../styles/componentVar.scss" as *;
  // 调用循环私有变量的方法
  @use "../../styles/mixins.scss" as *;
  // 调用获取类名的方法
  @use "../../styles/function.scss" as *;

  .Y-button {
    // 循环私有变量
    @include set-component-var("button", buttonVar());
    // 引用变量
    border-color: var(#{getVarName("button", "border-color")});
  }

  /*
  .Y-button {
    --y-button-border-color: var(--y-border-color-default);
    --y-button-text-color: var(--y-text-color-primary);
    --y-button-bg-color: var(--y-color-white);
    --y-button-is-link-color: var(--y-text-color-primary);
    --y-button-is-text-color: var(--y-text-color-primary);
    --y-button-is-text-bg-color: var(--y-color-white);   
  }
  */
</style>