TypeScript 中使用 CSS Modules

CSS 的全局性

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

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{} 包起來就能夠了。

配合 SASS 和 TypeScript

通常用 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 的做用:

  1. sass-loader 的做用固然是把 SASS 文件編譯成 CSS 文件;
  2. typings-for-css-modules-loader 是在 css-loader 上包了一層,它的選項徹底兼容 css-loader。除此以外,它會爲每一個 SASS 文件生成對應的 xxx.scss.d.ts 的解釋文件,這樣在 TypeScript 中就能夠正確解析,編輯器裏面也能有很是友好的代碼提示。
  3. style-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

參考

www.ruanyifeng.com/blog/2016/0…

github.com/camsong/blo…

相關文章
相關標籤/搜索