至关长一段时间 CSS 老是在页面上做为一个全局的存在,之前这个『特性』影响还不算很大,命名上注意一点,好比使用 BEM 也能必定程度上解决问题。javascript
可是随着 web 组件化的需求愈来愈强烈,CSS 的这种特性开始成为束缚开发者自由飞翔的绳索,每个 CSS 类名都有可能产生意想不到的冲突,或者被各个组件覆盖来覆盖去,每当修改一个组件时,咱们必须谨小慎微,注意是否会在全局环境下产生冲突。css
更严重的是,组件化的背景下,JS + 模板 + CSS 才能称为一个完整的组件,每一个组件若是单独引用一个 CSS 文件,你只能经过约束类命名来规避不一样组件间可能产生的冲突。同时因为 CSS 的全局性,组件的样式可能会互相影响,这彻底打破了组件的低耦合高内聚原则。html
固然,CSS 并不能算编程语言,不过是一款 DSL,原本咱们不能要求它那么多,可是不甘心的程序员们想出了各类方法让 CSS 更像编程语言,从 SASS、Less 到如今有点火的 CSS in JS 都是为了解决这个问题。vue
而 CSS Modules 的解决思路有所不一样,它在编写 CSS 的时候加入了局部做用域/全局做用域的概念。java
CSS Module 的规则很是简单,全部你不指明是全局做用域的都会当初局部做用域来处理。webpack
这里以 SASS 为例,好比你写一段:git
.title {
height: 80px;
line-height: 80px;
font-size: 24px;
color: #0a95bf;
}复制代码
出来的样式是:程序员
.title_1hf8_ {
height: 80px;
line-height: 80px;
font-size: 24px;
color: #0a95bf
}复制代码
title
变成了 title_1hf8_
你大概猜到了,CSS Module 实现局部做用域的方法是把类经过必定规则作个编码。github
组件中引用是这样的:web
import * as Style from './index.scss';复制代码
这样就能够在模板中使用了:
<div v-bind:class="Sytles.title">MD Converter</div>复制代码
出来的 html 就是:
<div class="title_1hf8_">MD Converter</div>复制代码
这里用的是 TypeScript 和 Vue ,其它地方用法也差很少。
『局部做用域』有了,下面是全局做用域:
:global {
.output {
background: #fff;
height: 100%;
min-height: 100%;
padding: 1em;
word-break: break-all;
}
}复制代码
把你想做为全局做用域的东西用 :global{}
包起来就能够了。
通常用 CSS Module 使用 Webpack 的 css-loader 便可,这里由于用的是 TypeScript,会有点不同。
先来个完整的 Webpack 配置文件:
var path = require('path');
var webpack = require('webpack');
const ExtractTextPlugin = require("extract-text-webpack-plugin");
module.exports = {
//页面入口文件配置
entry: {
"index.entry": __dirname + '/src/js/index.entry.js',
},
//入口文件输出配置
output: {
filename: '[name].js',
chunkFilename: "[name].chunk.js",
},
externals: {
"vue": "Vue",
},
plugins: [
new webpack.optimize.UglifyJsPlugin({
compress: {warnings: false},
output: {comments: false},
sourceMap: true
})
],
module: {
rules: [
{
test: /\.html$/,
loader: 'text-loader'
},
{
test: /\.scss$/,
use: ExtractTextPlugin.extract({
fallback: {
loader: 'style-loader',
options: {
insertAt: 'top'
}
},
use: [
{
loader: 'typings-for-css-modules-loader',
options: {
modules: true,
namedExport: true,
camelCase: true,
minimize: true,
localIdentName: "[local]_[hash:base64:5]"
}
},
{
loader: 'sass-loader',
options: {
outputStyle: 'expanded',
sourceMap: true
}
}
]
})
},
],
},
plugins: [
new ExtractTextPlugin({
filename: (getPath) => {
return getPath('../css/[name].css').replace('css/js','css');
},
allChunks: true
}),
]
};复制代码
这里主要用到三个 Loader,分别是 sass-loader,typings-for-css-modules-loader 和 style-loader。
若是不是 TypeScript 把 typings-for-css-modules-loader 换成 css-loader,稍微改下配置便可。
启用 CSS Module 很简单,就一句话 modules: true,
而后下面有个选项 localIdentName: "[local]_[hash:base64:5]"
是用来指定生成类名的规则的,title_1hf8_
就是根据这个规则来的,你也能够弄得复杂点,好比 [path][name]__[local]--[hash:base64:5]
简单解释下三个 Loader 的做用:
<style>
标签打到页面上。整个过程就是,读到一个 SCSS 文件,丢给 sass-loader 处理成 css,而后给 typings-for-css-modules-loader 生成 xxx.scss.d.ts 文件而且把 css 处理成 JavaScript 可使用的样子(这步实际上是 css-loader 在处理,为啥要把 css 文件处理成 JavaScript 能够用的样子呢,由于 webpack 只能处理 JavaScript,因此须要作转换),最后把处理好的给 style-loader,页面加载的时候就会打到页面上。
其实 loader 的本质就是 anything to JavaScript,由于 Webpack 只处理 JavaScript。记住这一点,就对为何要用这个 loader 那个 loader 有个清晰的认识了。
在用 TypeScript 写 Vue 组件的时候,定义组件时,能够 require 一个 html 做为 template:
@Component({
template: require('./index.html'),
})复制代码
为啥能够 require html 文件呢,由于上面的 webpack 配置中有这句:
{
test: /\.html$/,
loader: 'text-loader'
},复制代码
把 html 文件用 text-loader
处理了,把 require html 变成一段 text,利用 Webpack, 模板和代码优雅分离。
除此以外,CSS Modules 还有定义变量,继承别的类,import 其它模块等特性,不过这些 SASS 大多也有。
最后上一个简单的例子,TypeScript + Vue 的 Markdown 简单编辑器。
demo 代码地址:
github.com/bob-chen/md
记录一些所思所想,写写科技与人文,写写生活状态,写写读书心得,主要是扯淡和感悟。
欢迎关注,交流。
微信公众号:程序员的诗和远方
公众号ID : MonkeyCoder-Life