CSS Modules入門Ⅱ:快速上手

原文鏈接:Getting Started with CSS Modules

作者:ROBIN RENDLE

通過渲染編譯JS模板和CSS文件來實現CSS模塊化的方式不止一種。在今天的教程中,我們只著重討論一種在項目中應用CSS Modules的實現方式:

  • 第一部分:什麼是CSS Modules?為什麼要使用它?
  • 第二部分:如何上手CSS Modules
  • 第三部分:在React中使用CSS Modules

在我之前經手的一個項目中,有一個需求是CSS不能依賴客戶端的JS文件。也就是說在部署之前必須打包構建好所有的HTML/CSS文件。我們當時用的是Webpack模塊構建工具。文章接下來會介紹如何實現上述這個需求。

本篇教程中的代碼示例在:discountry/webpack-css-modules-example

安裝Webpack

在安裝完NPM和Node之後,我們需要新建一個空文件夾,然後在該目錄下運行:

npm init --yn

這個命令會按照默認設置生成一個package.json文件。此文件主要包含了某個項目的依賴列表,也就是在我們運行npm install時會被安裝的包都寫在這個文件裡面。

我們用Webpack來完成構建步驟。它可以監聽CSS/JavaScript/HTML文件,並處理所有的中間環節。我差點忘了你可能還不了解Webpack到底是什麼。你可以把它理解為一個構建系統或打包工具:

事實上它是兩者的結合。Webpack會將你項目中所有的資源(圖片/CSS/HTML/JS)視為模塊,你可以導入、編輯、操作、並打包到你最終的輸出文件中。——Maxime Fabre

即使這聽起來很奇怪也請別擔心。在Sass和Gulp還有NPM剛剛出來的時候,一時間也很難讓人接受。

首先讓我們配置好Webpack確保它能正常運作。第一步需要在全局安裝Webpack,這樣你才可以在命令行里使用它:

npm install webpack -gn

之後再在你的項目中安裝Webpack

npm i -D webpackn

現在我們需要在項目中創建一個index.js文件,把它放到/src文件夾里。我個人習慣於把所有的靜態資源(圖片/字體/CSS等)存在一個文件夾里,自己寫的代碼存在/src文件夾內,而電腦生成的代碼則會放在/build文件夾。我們並不需要手動創建/build文件夾,可以讓Webpack自動完成將/src下的代碼打包到/build的操作。

在/src文件夾里我們可以先創建一個名為alert.js的文件。另外還需要在項目的根目錄創建webpack.config.js配置文件。現在我們的項目結構大概是下面這個樣子:

package.jsonnwebpack.config.jsn/node_modulesn/srcn index.jsn alert.jsn

在webpack.config.js配置文件中,寫入以下內容:

module.exports = {n entry: ./src,n output: {n path: build,n filename: bundle.js,n },n};n

這樣配置之後,每次我們在項目中運行webpack命令時,Webpack都會檢測/src目錄下的文件。

接下來在src/index.js加入:

require("./alert.js");n

然後在alert.js里添加:

alert("LOUD NOISES");n

之後在項目的根目錄創建index.html文件,然後添加一個鏈接到打包輸出文件的script標籤:

<!DOCTYPE html>n<html lang="en">n<head>n <meta charset="UTF-8">n <title>Document name</title>n</head>n<body>n <h1>CSS Modules demo</h1>nn <script src="build/bundle.js"></script>n</body>n</html>n

這裡的bundle.js就是webpack打包之後會生成的文件。我們只需要在項目中運行webpack命令就可以生成它了。為了更方便一些,我們可以在package.json中寫上構建的腳本:

"scripts": {n "test": "echo Error: no test specified && exit 1"n},n

這一段是npm默認的腳本配置,我們可以把它替換成如下內容:

"scripts": {n //如果是Windows則改為 webpack && start index.htmln "start": "webpack && open index.html"n},n

之後在項目的命令行中輸入npm start命令,我們就能自動完成webpack的構建並在瀏覽器中打開網頁查看結果。

看起來效果不錯!目前為止webpack的功能應該都走通了。你可以刪掉alert.js測試一下,我們再次試著運行npm start時就會看到錯誤信息。

在Webpack無法找到我們導入的模塊時就會報錯。接下來讓我們更深入地了解一下webpack吧。

配置載入器(Loader)

載入器(Loader)是webpack的使用中非常重要的一部分。你可以把載入器看作一個小插件,在我們導入特定類型的文件時,對應的載入器就會起作用。

例如使用Babel載入器可以允許我們使用一些ES6的新特性來寫JS代碼。不需要像剛才那樣使用require而是可以使用import來導入模塊,以及箭頭函數之類的新的特性:

Babel起到一個類似於編譯器或預處理器的作用,它允許我們用ES6的語法書寫,之後把我們的代碼轉換成可以兼容運行的ES5代碼。

通過下面這行命令來安裝Babel的相關依賴:

npm i -D babel-loader babel-core babel-preset-es2015n

在根目錄創建一個.babelrc文件,在裡面設置:

{n "presets": ["es2015"]n}n

現在我們來配置,讓Babel處理所有我們自己編寫的.js文件。注意配置好,不能讓Babel干擾一些可能會使用的第三方庫。修改webpack.config.js為如下內容:

var path = require(path);nnmodule.exports = {n entry: ./src,n output: {n path: build,n filename: bundle.js,n },n module: {n loaders: [n {n test: /.js/,n loader: babel,n include: path.resolve(__dirname, src),n }n ],n }n};n

載入器(Loader)配置中的test值用來匹配對應的文件類型,include用來指定在哪個路徑下該載入器生效。

我們先來測試一下Babel是不是已經能在Webpack里正常運作了。創建一個新的文件src/robot.js,寫入以下內容:

const greetings = (text, person) => {n return `${text}, ${person}. I read you but I』m sorry, I』m afraid I can』t do that.`;n}nnexport default greetings;n

這一段JS代碼使用了一些ES6的新特性,例如export, const以及let, 箭頭函數、模板標籤等。

現在我們可以試著在src/index.js里通過import導入模塊:

import greetings from ./robot.jsndocument.write(greetings("Affirmative", "Dave"));n

之後再運行一次npm start,這時你瀏覽器打開的網頁里應該就會多出一行字了。

很酷不是么?但截至目前我們還沒有料到CSS Modules呢。在我們開始下一步之前,先刪掉src/下的所有測試代碼。

載入樣式

再安裝兩個載入器,我們的項目模板差不多就準備好了:

npm i -D css-loader style-loadern

css-loader用來讀取CSS文件,style-loader則負責處理並將樣式載入到頁面中。我們先創建src/app.css來測試一下:

.element {n background-color: blue;n color: white;n font-size: 20px;n padding: 20px;n}n

之後通過import語句導入到src/index.js文件中:

import styles from ./app.cssnnlet element = `n <div class="element">n <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Consequatur laudantium recusandae itaque libero velit minus ex reiciendis veniam. Eligendi modi sint delectus beatae nemo provident ratione maiores, voluptatibus a tempore!</p>n </div>n`nndocument.write(element);n

等等!我們剛剛是在JS代碼里導入了樣式文件么?這確實是可以實現的。只需要我們再配置一下webpack.config.js里的內容:

var path = require(path);nnmodule.exports = {n entry: ./src,n output: {n path: build,n filename: bundle.js,n },n module: {n loaders: [n {n test: /.js/,n loader: babel,n include: path.resolve(__dirname, src),n },n {n test: /.css/,n loaders: [style, css],n include: path.resolve(__dirname, src),n }n ],n }n};n

然後運行npm start我們將會看到:

這時可以試著用【審查元素】檢查一下文檔內容,我們會發現,style-loader在<head>中寫入了一個<style>標籤:

現在來捋一捋剛剛到底發生了什麼。我們在一個JavaScript文件里請求了另外一個CSS文件,CSS的內容被寫入到了最終的網頁里。所以在現實中的應用可能是在一個按鈕組件中,buttons.js文件添加了buttons.css作為依賴。然後將按鈕組件導入到其他JS文件中,最後組成模板,並輸出HTML。這樣的方式可以讓我們的代碼組織更合理並且很方便閱讀。

為了讓我們的代碼更加清晰,我個人傾向於把CSS輸出為單獨的文件,而不是直接嵌入HTML裡面。為了實現這個需求,我們需要安裝一個叫做extract text 的 Wepack 插件。

將載入的所有CSS模塊輸出為一個獨立的CSS文件。這樣你的樣式就不至於和JS代碼混淆在一起,而是生成一個獨立的CSS打包文件。假如你的樣式文件體積較大的話,這種方式可以加快頁面載入速度,因為樣式文件可以和JS文件同時載入。

通過下面這行命令來安裝插件:

npm i -D extract-text-webpack-pluginn

之後再對webpack.config.js配置文件稍作修改,替換CSS文件的載入器:

var path = require(path);nvar ExtractTextPlugin = require(extract-text-webpack-plugin);nnmodule.exports = {n entry: ./src,n output: {n path: build,n filename: bundle.js,n },n module: {n loaders: [n {n test: /.js/,n loader: babel,n include: path.resolve(__dirname, src),n },n {n test: /.css/,n loader: ExtractTextPlugin.extract("css"),n }n ],n },n plugins: [n new ExtractTextPlugin("styles.css")n ]n};n

這樣ExtractTextPlugin插件就會為我們生成styles.css文件。

這時我們就不再需要style-loader了。運行完webpack命令後,你打開/build文件夾就能夠找到新生成的styles.css文件。你還需要把生成的樣式文件鏈接手動添加到index.html中:

<link rel="stylesheet" href="build/styles.css">n

試著運行npm start,奇蹟就發生啦~頁面沒有什麼別的變化,但現在是通過外聯CSS實現的。

那麼接下來要如何設置才能利用CSS Modules的作用域特性呢?只需要對webpack.config.js稍作修改:

{n test: /.css/,n loader: ExtractTextPlugin.extract(css?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]),n}n

上面這個配置會生成長得變態的類名。Webpack通過CSS載入器來實現這個功能。

現在來修改我們的index.js,通過styles.element的方式訪問CSS類:

import styles from ./app.cssnnlet element = `n <div class="${styles.element}">n <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Consequatur laudantium recusandae itaque libero velit minus ex reiciendis veniam. Eligendi modi sint delectus beatae nemo provident ratione maiores, voluptatibus a tempore!</p>n </div>n`nndocument.write(element);n

最後再運行一次npm start,我們就可以看到一個不需要擔心作用域的頁面生成啦:

<div class="app__element___2YM3c">n ...n</div>n

不過目前為止我們舉的例子仍然很抽象,還有很多問題沒有涉及到。在真正的開發工作中,我們肯定不能直接任性地通過document.write來輸出頁面。我們該如何架構我們的模塊和文件?實現CSS Modules的功能只完成了一半的工作而已,接下來我們還得考慮如何從別的項目里遷移代碼。

在下一篇教程里,我們將會討論如何通過React來構建簡單的模塊,同時也會補充介紹如何在項目中應用類似Sass和PostCSS的預處理器的特性。

號外!號外!

從零學習前端開發有自己的QQ群啦!

群號:591950591

點擊鏈接加入群【從零學習前端開發】:加群鏈接

驗證問題:想學啊你?回答:我教你啊

我會在群內不定期分享各類前端學習資源,也會偶爾布置實踐小作業供大家一同交流學習襖,以後還會陸續開展各類活動~

推薦閱讀:

前方有最簡單、最實用CSS布局技巧總結!
前端周刊第56期:應接不暇的技術大會
React還是Vue?
Chrome DevTools之Timeline Tool簡介

TAG:CSS | 前端开发 | webpack |