至關長一段時間 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