从搭建工程讲到CSS Modules

背景

这周的主要的工做就是搭建新工程的架子,项目基于vue-cli构建。基本的功能在脚手架里都已经具有,可是仍是须要针对具体的业务场景来作一些定制。css

mock

以如今先后端分离的开发模式来说,一个正常的开发流程大概是这样:
前端

这样前端能够不用等后端接口彻底写完才能够开发,先后端并行开发,提升团队效率。vue

而脚手架提供的功能只有一个proxy,不能知足咱们的需求。因此须要咱们本身写一个mock-middleware。大概是这样的一个功能,就不贴mock-middleware的代码了:webpack

module.exports = () => {
    const argv = Array.prototype.slice.call(process.argv, 2);
    const proxyAddress = argv[0];
    if (!argv.length || proxyAddress === 'mock') {
        return mockMiddleware;
    } else {
        return proxyMiddleware(proxyAddress);
    }
}复制代码

最终的效果就是运行 npm run dev mock ,会将api请求(注意:这里只会代理api请求,而不代理静态资源)转发到mock数据。在先后端开发完成以后,能够运行npm run dev {proxy_ip}将api请求代理到某台开发服务器进行联调。git

固然这篇文章的重点不是这个,而是想总结下在SPA应用里如何规范的写CSS。github

CSS模块化

故事的起点从组件库的选择提及,项目选择了开源的element 做为组件库。而这套组件库并不符合考拉前端组的视觉规范,而目前也没有计划fork一条分支来维护,因而就采起了比较简单粗暴的作法,样式覆盖。先来看一段element中的样式:web

@component-namespace el {
  @b progress {
    position: relative;
    line-height: 1;

    @e text {
      font-size:14px;
      display: inline-block;
      vertical-align: middle;
      margin-left: 10px;
      line-height: 1;
    }
    @m circle {
      display: inline-block;
    }
  }
}复制代码

可能有同窗在刚看到这段样式的时候会有点蒙,可是仔细看@b,@e,@m,不就是bem规范么。
vue-cli

BEM规范

关于BEM规范,网上有不少文章介绍。知乎上也有一篇文章来讨论其优劣,这里不去讨论BEM规范的好处和坏处,我以为只要在一个工程里约定好一种规范并严格执行,这样老是不会错的。npm

一方面为了和组件库的CSS规范保持统一,另外一方面我我的以为BEM的优势仍是大于缺点,所以在项目里也准备按照这个规范来写。gulp

那么问题来了,浏览器是不认识上面这些@b,@e,@m的语法的,这个时候就须要postcss来帮忙了。

postcss

postcss和gulp,webpack等工具同样,他自己并非一种预处理器或者后处理器,而是经过各类插件来完成转换(好比很流行的Autoprefixer就是它的一个插件)。

postcss-salad能够认为是一个postcss插件的集合,支持最新的css语法,一些sass嵌套的语法以及bem转化。这时候先来一份配置文件postcss.config.js

module.exports = {
  plugins: [
    require('postcss-salad')({
      browsers: ['ie > 9', 'last 2 versions'],
      features: {
        bem: {
          shortcuts: {
            component: 'b',
            descendent: 'e',
            modifier: 'm'
          },
          separators: {
            descendent: '__',
            modifier: '--'
          }
        }
      }
    })
  ]
}复制代码

bem之间的链接符能够自定义,这边是为了和element的组件保持一致。这时候咱们能够先经过postcss-cli这个工具来看一下效果。咱们就用上述那段css来用postcss+postcss-salad进行转换,获得结果以下:

.el-progress {
    position: relative;
    line-height: 1
}

.el-progress__text {
    font-size: 14px;
    display: inline-block;
    vertical-align: middle;
    margin-left: 10px;
    line-height: 1
}

.el-progress--circle {
    display: inline-block
}复制代码

已经达到了咱们预想的效果,那么接下来要想在webpack中使用postcss确定会须要一个loader,也就是postcss-loader

而后将刚才的postcss.config.js做为postcss-loader的配置文件导入。

{
  loader: 'postcss-loader',
  options: {
    config: {
      path: 'path/to/postcss.config.js'
    }
  }
}复制代码

至此,就能够在webpack项目中,用这种嵌套的bem语法来进行css的书写了。

CSS Modules

故事尚未结束,你们都知道在写单页面应用的时候,一个常见的需求是但愿组件间的css做用域是互相隔离的。这时候第一反应想到就是Scoped CSS,vue-loader也是支持Scoped CSS的(实际上对CSS进行转换的仍是postcss)。那么Scoped CSS是如何来处理这个问题的呢:

<style scoped>
.example {
  color: red;
}
</style>

=>

<style>
.example[_v-f3f3eg9] {
  color: red;
}
</style>复制代码

能够看到Scoped CSS会在class后面加上一段hash,从而来实现CSS做用域的隔离,可是在实践过程当中,会发现几个问题:

  1. Scoped CSS将同时做用在父组件和子组件上,也就是没法作到父子组件样式的隔离。
  2. 因为Scoped CSS改变了DOM中的class,所以就没法在组件内去覆盖element组件的全局样式了。

这时候就须要CSS Modules出场了,CSS Modules是目前CSS模块化方案中被接受度较高的一种方案,网上也有不少的文章去介绍它。一样的,咱们须要先看下CSS Modules能作什么事情呢?

刚才讲到,postcss有不少的插件,那么确定也会有CSS Modules的插件了。咱们一样经过以前的postcss-cli来进行测试,首先配置上这个插件:

require('postcss-modules')({
  generateScopedName: '[local]--[hash:base64:5]'
})复制代码

这里的[local]表明类名,[hash:base64:5]按照给定规则生成的hash值,还能够用的变量有[name]表明标签名,[path]表明路径等
CSS Modules 生成的class能够自定义规则,所以也能够用自定义的规则,而不用bem规范,能够看项目的具体状况来定

仍是一样那段css代码,看下转换的结果:

.el-progress--3tuDF {
    position: relative;
    line-height: 1
}

.el-progress__text--1W8n3 {
    font-size: 14px;
    display: inline-block;
    vertical-align: middle;
    margin-left: 10px;
    line-height: 1
}

.el-progress--circle--3OD0E {
    display: inline-block
}复制代码

跟咱们预设的样式规则一致,这样就能够解决上述的第一个问题,作到每一个组件内的样式是惟一的。

可是咱们发现CSS Modules一样会改变class,那么也一样没法在组件内覆盖element组件的全局样式,这时候能够用CSS Modules的全局样式写法,:global{.class}

:global(.el-progress) {
  position: relative;
  line-height: 1;
}

=>

.el-progress {
  position: relative;
  line-height: 1;
}复制代码

这样就不会在class上加上hash后缀,从而能够达到覆盖全局样式的目的。

CSS Modules 在Vue+webpack项目中的实践

首先在Vue-loader的配置文件中配置生成class规则:

cssModules: {
    localIdentName: '[local]--[hash:base64:5]',
    camelCase: true
}复制代码

而后在组件内经过在style上添加module打开CSS Modules。

<style module>
</style>复制代码

css-loader 会将一个 $style对象注入到当前组件。因此在实际中使用大概是这个样子:

<header :class="$style['titan-header']">
</header>复制代码

有时候咱们会有这样的用法:

<div :class="{ 'active': selectedIndex == index} ">
</div>复制代码

这时候样式就会做为对象的属性名,而咱们知道用了CSS Modules,就必须用$style.active来替换'active',还好咱们有ES6!
ES6有一个特性是用双括号支持用计算属性做为属性名,也就是这样:

<div :class="{ [$style.active]: selectedIndex == index} ">
</div>复制代码

总结

bem+CSS Modules在新项目的实践已经有一周的时间,并无发现什么问题,才写下这篇文章来总结。以上只是我在本次工程搭建过程当中的一些总结,并不保证观点彻底正确,供你们参考。

相关文章
相关标签/搜索