筆記 - CSS 預處理器與 webpack
認識 CSS preprocessor - Sass、任務管理工具 webpack,以及 CSS naming convention。
CSS 預處理器
CSS (Cascading Style Sheets) 階層樣式表,是從上而下將所有樣式設定逐條夾到 .css 文件中,但有語法重複、可維護性不佳、可讀性不佳的問題。
更有效的 CSS 撰寫與管理:CSS Preprocessor 預處理器
目前流行的 CSS 預處理器: Sass, Less, Stylus
名詞釐清
- Sass/SCSS 為目前業界主流,SCSS (Sassy CSS) 副檔名為
.scss
- Sass indented syntax 副檔名為
.sass
為舊版語法
編寫 .scss
檔案 透過編譯 (compile) 成 plain CSS 讓瀏覽器使用。
安裝 Sass
1 | npm install sass@1.26.7 |
常用情境指令
編譯單一 sass 檔案為 css 檔案
1 | npx sass main.scss output.css |
會跑出很多檔案,其中 output.css 是編譯的結果
追蹤單一 sass 檔案並持續編譯成 css
使用 --watch
當 .scss 檔案有變動,自動編譯成 .css 檔案
1 | npx sass --watch main.scss:output.css |
追蹤資料夾並持續編譯成 css
追蹤整個資料夾(包含子資料夾)中的 .scss 檔案,並輸出 .css 檔案至另一個資料夾。
1 | npx sass --watch scss_dir:css_dir |
接下來在 scss_dir 資料夾裡的變動,都會同步到 css_dir 資料夾中。
Webpack
Webpack 與 gulp 為前端實務上常使用的任務管理工具。
Webpack 為 module bundler (模組打包工具),從進入點 entry point 中開始分析專案結構,找出每個模組間的依賴關係 dependency,並分析其中是否有瀏覽器不能直接使用的語法(例如 scss),甚至檢查程式碼中是否有引入 CSS, 圖片檔等,將他們轉換並打包在一起,最後產出瀏覽器可以識別的檔案(html, js, css)以方便部署。
- 模組化:大型專案中,依照功能切成一個個的小 module (檔案),方便組織與管理。
- 打包:模組會依照各自負責的功能,會有父子組件相互依賴,解析模組間的依賴關係並把最後的結果輸出,方便部署
另外 webpack 對模組有更廣泛的定義,除了 JavaScript 功能之外,另外像是
- 透過 npm 安裝引用的第三方套件
- 使用 CSS 預處理器與法
- 各種圖檔格式引入
其他用途:
- 利用各種 loader 來識別檔案類型,並轉譯成 瀏覽器可接受的語法,例如 Babel 設定來轉譯 JavaScript ES6-11 的語法
- 打包過程中可透過 plugin 工具 (minify, uglify) 來進行程式碼優化。
現今前端框架的 CLI 工具,例如 create-react-app 或 Vue CLI,都有整合 Webpack 專案打包工作的通用的預設。
webpack 官網 | 參考文章-AC | 參考文章-教學文
webpack 開發環境建立
專案初始化
1 | npm init -y |
安裝套件
1 | npm install webpack@4.43.0 webpack-cli@3.3.11 mini-css-extract-plugin@0.9.0 css-loader@3.5.3 sass-loader@8.0.2 |
- webpack & webpack-cli:主要的打包工具
- css-loader & sass-loader:編譯 sass 檔案
- mini-css-extract-plugin:抽離 css 檔案
建立設定檔
專案架構規劃如下圖:
root:index.html 及 webpack.config.js,
src 資料夾::編譯前的原始碼 (source)
網頁的 index.html 放在專案的第一層
Sass 設定檔放在 src/scss/main.scss
設定 webpack 進入點: src/main.js,webpack 會以這個檔案作為出發點依序去找相關連的檔案
1 | import './scss/main.scss' |
webpack 設定
1 | // webpack.config.js |
- entry:進入點指向 ‘./src/main.js’
- output:檔案編譯後的輸出位置,把 main.js 打包成 dist 資料夾裡面的 bundle.js
- rules 裡面針對 *.scss 副檔名的檔案,使用 mini-css-extract-plugin、css-loader 及 sass-loader 三個 loader 來編譯
- 編譯後的 scss 只會改變副檔名,檔名的部分不變 (main.css)
html 載入編輯後檔案
在 index.html 來載入預計編譯好的檔案位置(dist/bundle.js, dist/main.css)
1 | <head> |
設定編譯腳本 script
在 package.json 中新增一個 script
1 | "scripts": { |
執行編譯
1 | npm run build |
run 完之後就可以在專案中看到 dist 資料夾,包含 js 以及被編譯過的 css
Sass 核心語法
變數與運算子
冒號進行變數指派,分號結尾。可以針對數值進行計算。
例如
Sass
1 | $font-stack: Helvetica, sans-serif; |
CSS
1 | .comment { |
補充:考量到網頁的 accessibility,使用相對單位讓瀏覽器自行調整字體大小,以適用於不同使用者。
巢狀結構
在 sass 中可以使用巢狀語法來編寫 組合選擇符(nested combinator)例如空白、> + ~ 等,以達到更好的可讀性。
例子為針對 <article>
底下的 <h1><h2>
標籤進行設定,使文章中標題的表現與全站的標題不同。
Sass
1 | article { |
CSS
1 | article h1 { |
檔案模組化
我們可以將檔案拆成多個,配合上妥善的命名,讓專案變得更容易管理。
- 開頭為底線 _ 的檔名在 Sass 中稱之為 partial,也就是一個低階的模組
- 使用 @use 來引用 partial。引用名稱不包含底線與副檔名
- 模組有自己的命名空間 (namespace) ,使用模組中的變數時,必須給命名空間加上前綴,例如 base.$primary-color
繼承與覆寫
讓多個類別都使用共同的屬性。使用 % 宣告類別,並使用 @extend 來執行繼承。若要覆寫屬性,則直接宣告即可。
例如,頁面中三種訊息要使用不同字體顏色,但同樣的邊線、padding,透過繼承(邊線與 padding)與覆寫(字體顏色)來寫程式碼。
Sass
1 | %message { |
CSS
1 | .message-error, .message-info, .message-success { |
mixin
在物件導向程式設計中,mixin 意指一種工具形式的類別,用以附加在目標類別之上,為目標類別增添額外的方法或屬性,可以將它視作一種更彈性的繼承 (inherit) 的實作方式。
在 Sass 裡,使用 @mixin 進行宣告,使用 @include 使用 mixin。在以下例子中,我們創建了一個名為 font-setting 的 mixin
Sass
1 | @mixin font-setting($size, $weight, $color) { |
CSS
1 | article { |
推薦教學影片 - Learn Sass In 20 Minutes
CSS naming convention
命名慣例透過 class name 了解結點功能或是帶有什麼屬性,以利多人協作。
主流命名慣例分為:
- OOCSS (Object oriented CSS):物件導向命名法,例如 Bootstrap 中 .btn 代表按鈕的樣式,d-flex 代表以 flex 方式排版
- SMACSS (Scalable and Modular Architecture for CSS):明確地透過結構模組化來命名,如 Base、Layout、Module、State、Theme
- BEM (Block Element Modifier):透過 BEM 三者來命名,可以避免樣式衝突,確保每個組件名字都是獨一無二的,同時能以透過觀察 class 名稱,就能了解 CSS 階層架構,例如
list__item--hover
為滑鼠滑過表單子項目時的樣式- Block: 指在 Web 開發中的模組,每個 Block 在邏輯和功能上都應該是互相獨立的
- Element: 相依於 Block 中的一環,不能單獨使用,以 ‘__’ 連接 Block
- Modifier: 用於定義 Block 或 Element 的外觀、狀態或類型,以 ‘–’ 連接對象
1 | <form class="order-form"> |
由於 BEM 命名會產生很長的類別名稱,在 Sass 中可以透過 & 符號帶入父層名稱,來使得代碼更加簡潔:
Sass
1 | .order-form { |
CSS
1 | .order-form { |