CSS Modules 初探(基于vue-cli4) —— 学了才知道这么简单

3,117 阅读1分钟

零配置

webpack 自带的 css-loader 组件,自带了 CSS Modules,没有其他依赖,开箱即用,同时也提供了一些配置选项,通过简单的配置就可以生成你想要的css!

开启

vue-loader指南中讲到,必须通过向 css-loader 传入 modules: true 来开启:

// webpack.config.js
{
  module: {
    rules: [
      // ... 其它规则省略
      {
        test: /\.css$/,
        use: [
          'vue-style-loader',
          {
            loader: 'css-loader',
            options: {
              // 开启 CSS Modules
              modules: true,
              // 自定义生成的类名
              localIdentName: '[local]_[hash:base64:8]'
            }
          }
        ]
      }
    ]
  }
}

// vue.config.js
module.exports = {
  css: {
    loaderOptions: {
      css: {
        modules: {
          modules: true
        }
      }
    }
  }
}

但实践中发现,不需配置也可使用,大概是版本有更新

全局使用

1.直接在main.js中引入global.module.css

// main.js
import './global.module.css'

2.:global(.classname)

// global.module.css

// .red为局部css
.red {
  color: red;
}

// .blue为全局css
:global(.blue) {
  color: blue
}

3. xx.vue文件中使用:

<div :class="global.red">app.vue</div>
<div class="blue">app.vue</div>

3.结果

可以发现,

第一行的文字颜色仍是黑色,.red没有生效,

第二行的文字颜色已经设置成了蓝色,.blue成功生效

局部使用

上面例子中的.red没有生效,因为它是局部作用域,那怎么让它生效呢?

在.vue或.js文件等需要使用的地方中,把该 css 或其它预处理文件 —— 以 .module.(css|less|sass|scss|styl) 结尾的 —— 作为 CSS Modules 导入 (用一个变量接收),eg:

import styles from './foo.module.css'
// 所有支持的预处理器都一样工作
import sassStyles from './foo.module.scss'
<template>
  <div id="app">
    <div :class="global.red">app.vue</div>
    <div class="blue">app.vue</div>
    <HelloWorld msg="Welcome to Your Vue.js App"/>
  </div>
</template>
<script>
import HelloWorld from './components/HelloWorld.vue'
import globalModule from './global.module.css'
export default {
  name: 'App',
  components: {
    HelloWorld
  },
  data() {
    return {
      globalModule
    }
  },
  created() {
    console.log(globalModule) 
    // {red: "global-module_red_39Ula"}
  }
}
</script>

composes

/* colors.css */
.primary {
  color: #720;
}
.secondary {
  color: #777;
}

/* other helper classes... */

/* submit-button.css */
.common { /* font-sizes, padding, border-radius */ }
.normal {
  composes: common;
  composes: primary from "../shared/colors.css";
}

优点

css的正常打开方式的弊端

实际开发中,我们往往会碰到下面这些情况:

/* components/submit-button.css */
.Button { /* all styles for Normal */ }
.Button--disabled { /* overrides for Disabled */ }
.Button--error { /* overrides for Error */ }
.Button--in-progress { /* overrides for In Progress */

<button class="Button Button--in-progress">Processing...</button>

1.命名混乱:上面的.Button是普通的按钮的样式,其他三个是在此基础上的样式叠加。如果要增加一种新的按钮,命名也是像上面一样。当程序变得更复杂,或多人协作开发,html中的类名就会很长,不但混乱不易阅读,还影响项目的压缩。 2.依赖管理不好:以上面为例,button的组件有一个css,一个html页面的多个组件,就有多个这种引入语句,就会显得很乱。 3.全局污染:当项目趋于复杂,有些类名多个组件共用,有些类名单个组件局部使用,这样管理就会变得混乱。而且多人协作后引入好几个css文件,别人一个样式可能把你写的东西搞得完全认不出来,到时你就知道模块化有多爽了。 4.无法共享样式

CSS Modules 的优点

1.只需简单的命名,不再需要叠加式起名字了。最终生成的类名会根据事先配置好的前缀+唯一的序列码,压缩自然也小一些。 2.依赖管理,不会污染。 3.composes可复用

增加一些配置

1.css.requireModuleExtension去掉文件名中的 .module

// vue.config.js
module.exports = {
  css: {
    requireModuleExtension: false
  }
}

2.css.loaderOptions.css 自定义生成类名,所有的 css-loader 选项在这里都是支持的,例如 localIdentNamecamelCase

// vue.config.js
module.exports = {
  css: {
    requireModuleExtension: false,
    loaderOptions: {
      css: {
        // 注意:以下配置在 Vue CLI v4 与 v3 之间存在差异。
        // Vue CLI v3 用户可参考 css-loader v1 文档
        // https://github.com/webpack-contrib/css-loader/tree/v1.0.1
        modules: {
          // 默认配置为'[name]_[local]_[hash:base64:5]'
          // name 是文件名,local是类名
          localIdentName: 'ph_local_[hash:base64:5]'
        },
        localsConvention: 'camelCaseOnly'
      }
    }
  }
}

3.css.loaderOptions.[sass|scss|less].prependData向预处理器 Loader 传递选项(globalVars变量)

以sass/scss、less为例,向所有 Sass/Less 样式传入共享的全局变量:

// vue.config.js
module.exports = {
  css: {
    loaderOptions: {
      // 给 sass-loader 传递选项
      sass: {
        // @/ 是 src/ 的别名
        // 所以这里假设你有 `src/variables.sass` 这个文件
        // 注意:在 sass-loader v7 中,这个选项名是 "data"
        prependData: `@import "~@/variables.sass"`
      },
      // 默认情况下 `sass` 选项会同时对 `sass` 和 `scss` 语法同时生效
      // 因为 `scss` 语法在内部也是由 sass-loader 处理的
      // 但是在配置 `data` 选项的时候
      // `scss` 语法会要求语句结尾必须有分号,`sass` 则要求必须没有分号
      // 在这种情况下,我们可以使用 `scss` 选项,对 `scss` 语法进行单独配置
      scss: {
        prependData: `@import "~@/variables.scss";`
      },
      // 给 less-loader 传递 Less.js 相关选项
      less:{
        // http://lesscss.org/usage/#less-options-strict-units `Global Variables`
        // `primary` is global variables fields name
        globalVars: {
          primary: 'orange',
          blue: 'blue',
          green: 'green'
        }
      }
    }
  }
}
  // main.js
  import Vue from 'vue'
  import App from './App.vue'
  // import './global.css'
+ import './global.less'
  Vue.config.productionTip = false

  new Vue({
    render: h => h(App),
  }).$mount('#app')

.red {
  color: @primary;
}
:global(.blue) {
  color: @green
}

生效