webpack多頁應用架構系列(五):據說webpack連less/css也能打包?

本文首發於 Array_Huang的技術博客—— 實用至上,非經做者贊成,請勿轉載。
原文地址: https://segmentfault.com/a/1190000006897458
若是您對本系列文章感興趣,歡迎關注訂閱這裏: https://segmentfault.com/blog/array_huang

前言

過去講前端模塊化、組件化,更多仍是停留在js層面,畢竟js做爲一種更典型的程序語言,在這方面的想象和操做空間都更大一些。但近年來,組件化要求得更多了,HTML/CSS/JS這三件套一件可都不能少(甚至包括其它類型的資源,好比說圖片),而這樣的組件,無疑是高內聚的。javascript

文章簡介

本文將介紹如何使用webpack來打包less/css(沒用過sass,但畢竟也是經過loader來加載的,相信與less無異),首先是介紹相關的webpack plugin&loader,而後將介紹如何加載不一樣應用層次的less/css。css

用到什麼loader了?

《webpack多頁應用架構系列(二):webpack配置經常使用部分有哪些?》裏我就說過,webpack的核心只能打包js文件,而js之外的資源都是靠loader進行轉換或作出相應的處理的。下面我就來介紹打包less/css所須要的loader。前端

less-loader

針對less文件,咱們首先須要使用less-loader來加載。less-loader會調用所依賴的less模塊對less文件進行編譯(包括@import語法)。至於說less-loader所接受的參數,實質上大部分是傳遞給less模塊使用的參數,因爲我本人應用less的程度不深,所以沒有傳任何參數、直接就使用了。若是你以前對less模塊就已經有了一套配置的話,請參考less-loader的文檔進行配置。java

另外,less-loader並不會針對url()語法作特別的轉換,所以,若是你想把url()語句裏涉及到的文件(好比圖片、字體文件等)也一併用webpack打包的話,就必須利用管道交給css-loader作進一步的處理。node

css-loader

針對css文件,咱們須要使用css-loader來加載.css-loader的功能比較強大,一些新穎的特性好比Local Scope或是CSS Modules都是支持的。webpack

我目前只用到了css-loader的壓縮功能(Minification),對於這個功能,有一點是須要注意的,那就是若是你的代碼裏也和我同樣,有許多爲了瀏覽器兼容性的廢棄CSS代碼的話,請務必關閉autoprefixer已避免你的廢棄CSS代碼被css-loader刪除了,形如css?minimize&-autoprefixergit

上面提到css-loader會對url()語句作處理,這裏稍微再說兩句。在less/css裏的這url()語句,在css-loader看來,就跟require()語句是同樣的,只要在webpack配置文件裏定義好加載各種型資源的loader,那這url()語句實際上什麼資源都能處理。通常我在url()語句都會以相對路徑的方式(相對於此語句所在的less/css文件)來指定文件路徑;請不要使用以/開頭(即相對於網站根目錄,由於對於文件系統來講,這明顯是使人混淆的)的路徑,儘管css-loader也能夠經過設置root參數來適配。github

postcss-loader

習慣用postcss的童鞋們有福啦,webpack能夠經過postcss-loader來兼容postcss。因爲postcss只算是一個加分項,所以這裏也不做過多介紹,只介紹一下如何把postcss撘進webpack,不明白的童鞋麻煩先把postcss搞懂了再看。web

放上個人腳手架項目的代碼:bootstrap

var precss       = require('precss');
var autoprefixer = require('autoprefixer');

module.exports = {
    module: {
        loaders: [
            {
                test:   /\.css$/,
                exclude: /node_modules|bootstrap/,
                loader: 'css?minimize&-autoprefixer!postcss',
            }
        ]
    },
    postcss: function () {
        return [precss, autoprefixer({
            remove: false,
            browsers: ['ie >= 8', '> 1% in CN'],
        })];
    }
}

從loader的配置'css?minimize&-autoprefixer!postcss'上看,實際上就是先讓postcss-loader處理完了再傳遞給css-loader。而postcss項則是postcss-loader所接受的參數,實際上就是返回一個包含你所須要的postcss's plugins的數組啦,這些plugin有各自的初始化參數,不過這些都是postcss的內容了,這裏就不作介紹了。

用到什麼Plugin了?

加載less/css這一塊主要用到的是extract-text-webpack-plugin(下文簡稱爲ExtractTextPlugin吧),並且因爲我用的是webpack 1,所以用的也是相對應webpack 1的版本(1的文檔在這裏不要搞錯了哈)。

ExtractTextPlugin的做用是把各個chunk加載的css代碼(多是由less-loader轉換過來的)合併成一個css文件並在頁面加載的時候以<link>的形式進行加載。

相對於使用style-loader直接把css代碼段跟js打包在一塊兒並在頁面加載時以inline的形式插入DOM,我仍是更喜歡ExtractTextPlugin生成並加載CSS文件的形式;倒不是看不慣inline的css,只是用文件形式來加載的話會快不少,尤爲後面介紹用webpack來生成HTML的時候,這<link>會直接生成在<head>裏,那麼在CSS的加載上就跟傳統的前端頁面沒有差異了,體驗很是棒。

ExtractTextPlugin的初始化參數很少,惟一的必填項是filename參數,也就是如何來命名生成的CSS文件。跟webpack配置裏的output.filename參數相似,這ExtractTextPlugin的filename參數也容許使用變量,包括[id]、[name]和[contenthash];理論上來講若是隻有一個chunk,那麼不用這些變量,寫死一個文件名也是能夠的,但因爲咱們要作的是多頁應用,必然存在多個chunk(至少每一個entry都對應一個chunk啦)。這裏我是這麼設置的:

new ExtractTextPlugin('[name]/styles.css'), // [name]對應的是chunk的name,我在webpack配置中是這樣

[name]對應的是chunk的name,我在webpack配置中把各個entry的name都按index/indexindex/login這樣的形式來設置了,那麼最後css的路徑就會像這樣:build/index/index/styles.css,也就是跟chunk的js文件放一塊了(js文件的路徑形如build/index/index/entry.js)。

除了要把這初始化後的ExtractTextPlugin放到webpack配置中的plugins參數裏,咱們還要在loader配置裏作相應的修改:

module.exports = {
    module: {
        loaders: [
            {
                test:   /\.css$/,
                exclude: /node_modules|bootstrap/,
                loader: ExtractTextPlugin.extract('css?minimize&-autoprefixer!postcss'),
            }
        ]
    },
}

如此一來,ExtractTextPlugin就算是配置好了。

如何加載不一樣應用層次的less/css

在個人設計中,有三種應用層次的less/css代碼段:

  • 基礎的、公用的代碼段,包括CSS框架、在CSS框架上進行定製的CSS theme,基本上每一個頁面都會應用到這些CSS代碼段。
  • 組件的代碼段,這裏的組件指的是你本身寫的組件,並且組件自己含有js,並負責加載css以及其它邏輯。
  • 每一個頁面獨有的CSS代碼段,極可能只是對某些細節進行微調。

首先來回顧一下我設計的文件目錄結構:

├─src # 當前項目的源碼
    ├─pages # 各個頁面獨有的部分,如入口文件、只有該頁面使用到的css、模板文件等
    │  ├─alert # 業務模塊
    │  │  └─index # 具體頁面
    │  ├─index # 業務模塊
    │  │  ├─index # 具體頁面
    │  │  └─login # 具體頁面
    │  │      └─templates # 若是一個頁面的HTML比較複雜,能夠分紅多塊再拼在一塊兒
    │  └─user # 業務模塊
    │      ├─edit-password # 具體頁面
    │      └─modify-info # 具體頁面
    └─public-resource # 各個頁面使用到的公共資源
        ├─components # 組件,能夠是純HTML,也能夠包含js/css/image等,看本身須要
        │  ├─footer # 頁尾
        │  ├─header # 頁頭
        │  ├─side-menu # 側邊欄
        │  └─top-nav # 頂部菜單
        ├─config # 各類配置文件
        ├─iconfont # iconfont的字體文件
        ├─imgs # 公用的圖片資源
        ├─layout # UI佈局,組織各個組件拼起來,因應須要能夠有不一樣的佈局套路
        │  ├─layout # 具體的佈局套路
        │  └─layout-without-nav # 具體的佈局套路
        ├─less # less文件,用sass的也能夠,又或者是純css
        │  ├─base-dir
        │  ├─components-dir # 若是組件自己不須要js的,那麼要加載組件的css比較困難,我建議能夠直接用less來加載
        │  └─base.less # 組織全部的less文件
        ├─libs # 與業務邏輯無關的庫均可以放到這裏
        └─logic # 業務邏輯

基礎代碼段

基礎的CSS代碼(實際上個人項目中用的都是less)我統一都放到src/public-resource/less目錄裏。我使用一個抽象的文件base.less將全部的less文件組織起來(利用@import),這樣的話我用js加載起來就方便多了。

在個人腳手架項目(Array-Huang/webpack-seed)裏,CSS框架我用的是bootstrap,而且使用了bootstrap-loader進行加載,所以就沒有把bootstrap的CSS文件放到src/public-resource/less/base-dir目錄裏,這個目錄裏放的都是我定製的theme了。

src/public-resource/less/components-dir目錄放的是某些第三方組件所用到的css,又或是不含js的組件所用到的css。其實這部分CSS是否應該歸在下一類,我也考慮良久,只是因爲歸到下一類的話加載起來不方便,不方便緣由以下:

  • 某些第三方庫是要你本身加載CSS的,若是你打算寫適配器來封裝這些第三方庫,那天然能夠直接在適配器來加載CSS,這就屬於下一類了;然而,有一些使用起來很簡單的庫,你還寫適配器那就有點多此一舉了。
  • 某些本身寫的組件可能僅包含HTML和CSS,那麼誰來加載CSS?

因此乾脆仍是交由base.less一併加載了算了。

我設計了一個common.page.js,並在每個頁面的入口文件裏都首先加載這common.page.js,那麼,只要我在這common.page.js里加載base.less,全部的頁面都能享受到這份基礎CSS代碼段。

組件代碼段

組件的代碼我都放在了src/public-resource/components,每個組件統一放在一個獨立的目錄,並由該組件的js負責加載其CSS。

頁面代碼段

頁面獨有的CSS我天然是放在該頁面本身的目錄裏,利用該頁面的入口文件進行加載。

最終生成的CSS代碼都在哪?

因爲我使用了ExtractTextPlugin,所以這些CSS代碼最終都會生成到所屬chunk的目錄裏成爲一個CSS文件。

  • 基礎代碼段確定是保存在CommonsChunkPlugin所生成的公共代碼chunk所在的目錄裏了,在個人腳手架項目(Array-Huang/webpack-seed)裏就是build/commons了(個人公共代碼chunk的name是'commons')。
  • 組件代碼段看狀況,該組件用的頁面多的話(大於CommonsChunkPlugin的minChunks參數)就會被歸到跟基礎代碼段一塊兒咯;反之,則哪一個頁面用到它,就放到哪一個頁面chunk的目錄裏咯。
  • 頁面代碼段就不用想了,確定是在那個頁面chunk的目錄裏了,畢竟才用了1次。

示例代碼

諸位看本系列文章,搭配我在Github上的腳手架項目食用更佳哦(笑):Array-Huang/webpack-seed(https://github.com/Array-Huang/webpack-seed)

附系列文章目錄(同步更新)

本文首發於 Array_Huang的技術博客—— 實用至上,非經做者贊成,請勿轉載。
原文地址: https://segmentfault.com/a/1190000006897458
若是您對本系列文章感興趣,歡迎關注訂閱這裏: https://segmentfault.com/blog/array_huang
相關文章
相關標籤/搜索