使用less预处理语言定义常用的class

2,579 阅读8分钟

前言

相信我们做前端的都有一个特别头疼的问题,就是如何给html标签定义css名称而且又要防止样式冲突,并且在写样式的时候,存在很多重复性的样式,比如margin,padding,font-size,color等等,我最近就在想一个问题,怎样才能更高效的将页面进行布局并具有高效的可维护性,并且如果将此套代码集成到其他新项目,是否可以快速使用呢?Less提供的函数可以帮助我们快速定义一些常用的class,话不多说,开干!

Less

Less 是一门 CSS 预处理语言,它扩展了 CSS 语言,增加了变量、Mixin、函数等特性,使 CSS 更易维护和扩展。

Less 可以运行在 Node 或浏览器端。

具体的文档可以访问less官网查阅。

思路梳理

颜色

可以发现,很多ui组件库都会提供5种行为相关的颜色:primary(主题),success(成功),info(通知),warning(警告),error(错误)。同样我们可以参考这种模式定义变量:

//variable.less:定义变量文件

/* 行为相关颜色 */
@color-primary: #4897ff;
@color-success: #4cd964;
@color-info:#007aff;
@color-warning: #f0ad4e;
@color-error: #dd524d;

//定义下面两个变量的意义在于方便遍历处理,接下来就知道用途所在;
//因为less变量不支持定义对象形式,所以需要将key和value单独定义,一一对应即可;
@list:primary,success,info,warning,error;//颜色列表key
@colors:@color-primary,@color-success,@color-info,@color-warning,@color-error;//颜色列表value

定义变量之后,我们就可以根据自己的需求去定义一些常用的class:

下面会用到less的一些语法,具体的使用可以访问官网:

  • 循环Loops

    示例:

    .generate-columns(@n, @i: 1) when (@i =< @n) {
      .column-@{i} {
        width: (@i * 100% / @n);
      }
      .generate-columns(@n, (@i + 1));
    }
    
    .generate-columns(4);
    

    输出结果:

    .column-1 {
      width: 25%;
    }
    .column-2 {
      width: 50%;
    }
    .column-3 {
      width: 75%;
    }
    .column-4 {
      width: 100%;
    }
    
  • 选取extract

    实例:

    @list: apple, pear, coconut, orange;
    value: extract(@list, 3);
    

    输出结果:

    value: coconut;
    
  • 长度length

    实例:

    @list: "banana", "tomato", "potato", "peach";
    n: length(@list);
    

    输出结果:

    n:4
    
  • 颜色操作darken/lighten

//color.less:颜色文件

@import './variable.less';//上个定义变量的文件

// 生成主题字体颜色方法
.create-color(@lt,@i:1,@key:extract(@lt, @i),@val:extract(@colors,@i)) when (length(@lt)>=@i){
    .color-@{key}{
        color : @val;
    }
    .create-color(@lt, @i + 1);
}
.create-color(@list);

为了方便测试,可以使用lessc工具(npm i less -g),编译less为css(如果项目中有less环境,则会自动编译无需此步骤):

lessc color.less > color.css

输出结果:

.color-primary {
  color: #4897ff;
}
.color-success {
  color: #4cd964;
}
.color-info {
  color: #007aff;
}
.color-warning {
  color: #f0ad4e;
}
.color-error {
  color: #dd524d;
}

通过上面的方式,我们可以很方便定义一些常用的class,当在html标签中需要使用时,直接加入到classname就可以,也无需考虑命名的问题;当然还有很多其他的class也需要定义,比如字体,间距(padding ,margin)等,方式是同理的;

padding

我以padding举个例子:间距的方式可以考虑size的形式来命名:xs,sm,base,lg,xl等,当然如果你有其他的可以继续添加;

//variable.less

/* 间距 */
@spacing-base: 20px;
@spacing-xs: @spacing-base - 15px;//5px
@spacing-sm: @spacing-base - 10px;//10px
@spacing-lg: @spacing-base + 10px;//30px
@spacing-xl: @spacing-base + 20px;//40px

@size:xs,sm,base,lg,xl;
@spacings:@spacing-xs,@spacing-sm,@spacing-base,@spacing-lg,@spacing-xl;

定义间距的函数来生成class

//spacing.less

@import './variable.less';

//padding==================
// 生成间距方法
.create-padding(@lt,@i:1,@key:extract(@lt, @i),@val:extract(@spacings,@i)) when (length(@lt)>=@i){
    .padding-@{key}{
        padding : @val;
    }
    .create-padding(@lt, @i + 1);
}
.create-padding(@size);

编译输出结果:

.padding-xs {
  padding: 5px;
}
.padding-sm {
  padding: 10px;
}
.padding-base {
  padding: 20px;
}
.padding-lg {
  padding: 30px;
}
.padding-xl {
  padding: 40px;
}

除了padding,margin,font-size,border-radius等都可以使用size的形式来定义,方法是一样的;

总结

程序员一定要学会越来越‘懒’,重复性的工作坚决不做,所以我们就要去学习和探索如何才能做到‘懒’到极致,就像我们写style一样,我们会发现padding,margin,text-align,display,font-size,color,background-color等会在每个页面甚至每个标签都会不断的重复,不仅写的很烦,而且还特别降低效率,这样我们就需要去寻找属于我们自己偷懒的方法;当然这些class只是一部分,后续我会将我定义好的less文件提供给大家下载,希望大家能指出存在的问题以及有没有更好的建议。

后续

文件名定义

通过需要的功能点来定义常用的class,方便按需引入和定位查找;

--border.less //边框,圆角等;

--color.less //字体颜色,背景色等;

--font.less //字体样式,大小,字体,粗细等;

--form.less //表单样式,input,textarea等;

--icon.less //字体图标以及样式;

--main.less //将所有的less文件全部引入,即全局引入;

--position.less //位置样式,display,text-align,position,flex等

--reset.less //解决浏览器兼容性问题和一些系统默认样式重置

--size.less //尺寸,width,height等

--spacing.less //间距,margin,padding等

--transition.less //动画

--variable.less //所有的变量

举个栗子:

1、按钮

<h2>按钮</h2>
<div class="padding-l-base">
<h3>行为颜色</h3>
<div class="padding-l-base">
  <button class='bgcolor-primary highlight me-button'>primary</button>
  <button class='bgcolor-success highlight me-button'>success</button>
  <button class='bgcolor-info highlight me-button'>info</button>
  <button class='bgcolor-warning highlight me-button'>warning</button>
  <button class='bgcolor-error highlight me-button'>error</button>
</div>
<h3>行为颜色渐变</h3>
<div class="padding-l-base">
  <button class='linear-g-primary highlight me-button'>primary</button>
  <button class='linear-g-success highlight me-button'>success</button>
  <button class='linear-g-info highlight me-button'>info</button>
  <button class='linear-g-warning highlight me-button'>warning</button>
  <button class='linear-g-error highlight me-button'>error</button>
</div>
<h3>不同尺寸</h3>
<div class="padding-l-base">
  <button class='linear-g-primary highlight me-button small'>小28px</button>
  <button class='linear-g-primary highlight me-button'>默认34px</button>
  <button class='linear-g-primary highlight me-button large'>大40px</button>
</div>
<h3>镂空</h3>
<div class="padding-l-base">
  <button class='me-button bgcolor-inverse highlight color-primary border-primary'>primary</button>
  <button class='me-button bgcolor-inverse highlight color-success border-success'>success</button>
  <button class='me-button bgcolor-inverse highlight color-info border-info'>info</button>
  <button class='me-button bgcolor-inverse highlight color-warning border-warning'>warning</button>
  <button class='me-button bgcolor-inverse highlight color-error border-error'>error</button>
  <button class='me-button bgcolor-inverse highlight text-c-pri border-base'>inverse</button>
</div>
</div>

结果:

补充:可能有些人会说这个看起来一堆的class,代码编写起来可能会更加繁琐并且没有效率;其实不然,我们的很多需要复用的组件,都是要单独封装的,这样,我们维护起来,你会发现,会更方便并且更容易维护,而且定义的这些公用样式,还可以给其他标签使用,例如我就定义一个button

<template>
  <button
    class='me-button'
     :class="[
       (gradient&&!disabled)?'linear-g-'+color:(inverse?'bgcolor-inverse':'bgcolor-'+color),
       (inverse&&!disabled)?('color-'+color+' border-'+color):'',
       disabled?'bg-c-dis':(loadingShow?'me-loading':'highlight'),
       size
     ]" @click='buttonClick'>
     <i v-show='loadingShow' class="meiconfont meicon-loading margin-r-xs"></i>
     <span>
       <span v-if='!$slots.default'>buttonName</span>
       <slot></slot>
     </span>
  </button>
</template>

<script>
export default {
  name:'meButton',
  props:{
    //是否加载
    'loading':{
      type:Boolean,
      default:false
    },
    //延迟时间
    'duration':{
      type:Number,
      default:2
    },
    //是否渐变
    'gradient':{
      type:Boolean,
      default:false
    },
    //颜色主题
    'color':{
      default:'primary'
    },
    //是否镂空
    inverse:{
      type:Boolean,
      default:false
    },
    //大小尺寸
    'size':{
      type:String,
      default:''
    },
    //是否禁用
    'disabled':{
      type:Boolean,
      default:false
    },
    //是否延迟
    'timeout':{
      type:Boolean,
      default:true
    }
  },
  watch:{
    loading:{
      handler:function(n,o){
        if(!n){
          clearTimeout(this.clickTimer)
          this.clicked = false
          clearTimeout(this.loadingTimer)
          this.loadingShow = false
        }else{
          this.loadingTimer =setTimeout(() => {
            this.loadingShow = true
          }, 1000);
        }
      }
    }
  },
  data () {
    return {
      clicked:false,
      clickTimer:null,
      loadingShow:false,
      loadingTimer:null
    };
  },
  methods: {
     buttonClick(e){
      if(this.clicked || this.disabled) return
      this.$emit('click',e)
      if(this.isTimeout){
        this.clicked = true
        this.clickTimer = setTimeout(()=>{
          this.clicked = false
        },this.duration*1000)
      }
     }
  }
}
</script>

然后,我们就可以这样使用:

<template>
<div class="padding-base bg-c-inv margin-b-base">
    <h3>行为颜色</h3>
    <div class="padding-l-base">
      <me-button v-for="item in lists" :key='item' :color='item' class='margin-r-xs'>{{item}}</me-button>
      <me-button :disabled='true'>disabled</me-button>
    </div>
    <h3>行为颜色渐变</h3>
    <div class="padding-l-base">
      <me-button v-for="item in lists" :key='item' :color='item' :gradient='true' class='margin-r-xs'>{{item}}</me-button>
    </div>
    <h3>不同尺寸</h3>
    <div class="padding-l-base">
      <me-button v-for="item in sizes" :key='item.size' color='primary':gradient='true' :size='item.size' class='margin-r-xs'>{{item.name}}</me-button>
    </div>
    <h3>镂空</h3>
    <div class="padding-l-base">
      <me-button v-for="item in lists" :key='item' :color='item' :inverse='true' class='margin-r-xs'>{{item}}</me-button>
      <button class='me-button bgcolor-inverse highlight text-c-pri border-base'>inverse</button>
    </div>
    <h3>带Loading效果</h3>
    <div class="padding-l-base">
      <me-button :loading='btnLoading' @click='changebtnLoading'>点击加载loading</me-button>
    </div>
  </div>
</template>
<script>
export default {
    data(){
        return {
            lists:[
                'primary',
                'success',
                'info',
                'warning',
                'error'
            ],
            btnLoading:false,
            sizes:[
                {size:'small',name:'small 28px'},
                {size:'',name:'normal 34px'},
                {size:'large',name:'large 40px'},
            ],
        }
    }
}
</script>

github

项目已经更新到github上,点击me-project进行跳转,感兴趣的码友可以下载下来尝试一下,我只是个工作一年多的菜鸟,代码很low希望大家多多包涵,有问题或者其他好的建议可以提出来,沟通交流才是前进最大的垫脚石。