UI库样式文件包该怎么构建?

1,263 阅读5分钟

上篇文章已经搭建了一个调试的服务,现在重点需要处理下所有组件的样式。我们使用elementui引入样式的时候通常是引入一个index.css(目前先考虑全局引入的方式)

import 'element-ui/lib/theme-chalk/index.css';

那么怎样能模仿element-ui的实现方式去处理样式文件呢? 这里简化一下基本是以下几个点:

  1. 创建theme-chalk组件样式包目录进行管理。
  2. 利用sass定义组件库需要的配置和变量以及函数(sass的优势天然适合开发组件库)。
  3. 重点处理图标组件样式。
  4. 配置样式包的打包和优化。

目录

开发之前我们先看一下theme-chalk整体的目录结构信息。

-theme-chalk                            (主包目录)
    -lib                                (所有样式打包后放入该文件夹中)
    -src                                (所有样式源码)
        -common 
            -var.scss                   (颜色变量信息)
        -fonts                          (字体图标文件)
        -mixins                         (通用变量和配置)
            -config.scss                (bem规范配置)
            -mixin.scss                 (scss公共函数配置)
        - button.scss                   (按钮样式)
        - icon.scss                     (图标样式)
        - index.scss                    (样式入口文件)
    - gulpfile.js                       (打包配置文件)

1.创建theme-chalk

这里注意必须通过 lerna create theme-chalk 进行创建,方便后期各个组件引入样式。 此包用来管理整个UI组件库所有样式文件, 包括scss变量配置、各个组件样式、字体图标,样式打包配置文件。

2. 使用bem规范

我们都看见过组件写法例如el-button el-button--primary el-button__header这种定义组件样式的方式就是bem规范, 是一种适合前端团队的CSS写法规范化。

2-1 优先基本配置

src/mixins/config.scss 配置bem规范标识

// 组件ui命名空间  .ai-button
$namespace: 'ai';
// 组件状态  is-disable
$state-prefix: 'is-';
// 组件修饰符 .ai-button--primary
$modifier-separator: '--';
// 组件分割 .ai-button__header
$element-separator: '__'

2-2 配置公共颜色变量

src/common/var.scss

// 配置颜色变量, 这里就简单使用element-ui的颜色配置
$--color-primary: '#409EFF';
$--color-success: '#67C23A';
$--color-warning: '#E6A23C';
$--color-danger: '#F56C6C';
$--color-info: '#909399';
$--color-white: '#FFFFFF';
$--color-black: '#000000';

2-3 配置公共函数

src/mixins/mixin.scss 减少样式编写负担, 可以看为和插槽一样的东西, 具体可以看看代码示例, 其实项目中可以使用这些技巧来简化代码。

@import "../common/var.scss";
@import "config.scss";
// 我们写样式的时候不能一直 .ai-button这样添加样式, 需要靠scss的一些函数来简化样式书写
/**
 命名空间函数
例如: b(button) { color:red } => 最后会编译为 .ai-button { color: red }减少了重复操作
*/
@mixin b($block) {
  $B: $namespace+'-'+$block !global;
  .#{$B} {
    @content;
  }
}

/**
 添加状态
when(disable) { ... }   => .ai-button.is-disable { ... }
@at-root的作用是放在根下面
*/
@mixin when($state) {
  @at-root {
    &.#{$state-prefix + $state} {
      @content;
    }
  }
}

/**
 添加修饰
.ai-button.--primary
*/
@mixin m($modifier) {
  @at-root {
    #{&+$modifier-separator+$modifier} {
      @content;
    }
  }
}

/**
添加子层级元素
e(header)  => .z-button--xxx .z-button__header
*/
@mixin e($element) {
  @at-root {
    & {
      #{"." + $B + $element-separator + $element} {
        @content;
      }
    }
  }
}

3 制作字体图标 (重点)

image.png

作用: 字体图标主要提供给Icon组件来使用, 例如element-ui中就可以展示一个图标, 主要是根据类名进行配置。

3-1 登录iconfont网站寻找图标

这里我选择了以下一套图标加入到项目中,采用Font class的方式进行下载

image.png

3-2 配置图标项目信息

下载前需要配置以下信息, 这样下载完成后所有字体图标样式都是以ai-ui开头的

image.png

3-3复制fonts字体到项目中去

复制字体文件到src/fonts文件中

image.png

3-4创建src/icon.scss文件

其实就是复制了iconfont.css文件, 但是需要在上面进行修改简化字体图标的使用, 例如: 之前想使用loading必须通过.ai-ui-icons .ai-icon-loading, 但是这样来定义一个图标需要写两个类名非常麻烦, 可以通过匹配类名的方式来解决 (开发时候也可以使用);

@import "mixins/config.scss";
@font-face {
  font-family: "ai-ui-icons"; /* Project id 3951547 */
  src: url("./fonts/iconfont.woff2?t=1678781370523") format('woff2'),
  url("./fonts/iconfont.woff?t=1678781370523") format('woff'),
  url("./fonts/iconfont.ttf?t=1678781370523") format('truetype');
}

// 只要以ai-icon开始就能匹配上
// # sass中取值使用#并且后面必须加一个双引号
[class^="#{$namespace}-icon-"] {
  font-family: "ai-ui-icons" !important;
  font-size: 16px;
  font-style: normal;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

.ai-icon-loading:before {
  content: "\e601";
}

4. 尝试使用

4-1 处理icon组件

用户只需要提供组件名字来命中相应的字体图标库规则就能匹配到字体样式

// packages/icon/src/icon.vue
<template>
    <i :class="`ai-icon-${name}`"></i>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
    name: 'AiIcon',
    props: {
        name: {
            type: String,
            default: ''
        }
    }
})
</script>

4-2 将所有样式通过index.scss入口引入

// theme-chalk/src/index.scss
@import 'icon.scss' // 引入字体图标样式
@import 'button.scss' // 引入按钮样式
@import 'xxx' // 其他组件的样式

4-3 web/main.ts中进行引入调试

import { createApp } from 'vue';
import AiUi from 'ai-ui';
import App from './App.vue';
import 'theme-chalk/src/index.scss'; // 引入了样式包的入口文件,挂载到了全局中

createApp(App).use(AiUi).mount('#app');

4-4 App.vue中进行使用发现可以使用icon组件并且有样式说明成功了

然后运行上次配置的服务启动发现页面已经有了icon组件并且展示了对应的图标

image.png

<div>
    <Ai-Button></Ai-Button>
    <Ai-Icon name="bianji"></Ai-Icon>
</div>

5. 配置glup来打包theme-chalk里面所有的scss文件

虽然上面的方法可以使用,但是不应该让用户去引入开发时候的sass, 应该保持和elementui一样让用户去引用一个打包好的css文件

import 'theme-chalk/src/index.scss'; // 错误的
import 'element-ui/lib/theme-chalk/index.css'; // 正确方式

5-1 采用gulp来进行打包

gulp相对于webpack能更好的处理样式文件,所以这里选择gulp进行样式打包, 首先安装所需依赖:

yarn add gulp gulp-autoprefixer gulp-cssmin gulp-dart-sass gulp-rename -D -W

5-2 创建gulpfile.js配置文件

将打包的结果都放入到lib文件夹中

// theme-chalk/gulpfile.js
const { series, src, dest } = require('gulp');
const sass = require('gulp-dart-sass');
const autoprefixer = require('gulp-autoprefixer');
const cssmin = require('gulp-cssmin');

function compile() {
    return src('./src/*.scss') // 找出所有的sass文件
        .pipe(sass.sync()) // 同步进行编译
        .pipe(autoprefixer({})) // 添加兼容性前缀
        .pipe(cssmin()) // 进行压缩
        .pipe(dest('./lib')) // 结果生成到lib文件夹中
}

// 拷贝字体样式然后压缩
function copyfonts() {
    return src('./src/fonts/**').pipe(cssmin()).pipe(dest('./lib/fonts'));
}


// 先编译第一个方法然后执行拷贝方法
exports.build = series(compile, copyfonts);

5-3 配置打包脚本命令

// pacakge.json
"scripts": {
  "web-dev": "webpack serve --config ./web/webpack.config.js",
  "build:theme": "gulp build --gulpfile packages/theme-chalk/gulpfile.js"
}

然后运行打包命令对theme-chalk文件进行打包得到以下文件

image.png

执行后打包的结果都会放入到theme-chalk/lib中, 之后在mait.ts引入这个index.css即可。

最后

虽然上面已经完成了样式打包, 但是目前还只是支持全局引入的方式, 后面会继续分析如何配置如何按需引入的方式进行打包。

最后还吐槽下目前接手的一个项目, 不知道大家有没有这样的经历, 明明所有的事都已经做完了, 但是还是每天要坐到10点多才能走,天天感觉就像坐牢一样。 事情做完了还要演戏给上面看,感觉真心累。 只有写写文章缓解缓解压抑的心情(所以最近写的比较多哈), 同时也希望能帮助大家了解UI库构建的一个过程。

项目地址: github.com/underFyh/AI…