Webpack4.X重修之路 --- 樣式篇

前言

這一篇主要開始介紹使用webpack4打包css以及less(stylus和sass同理)

開始之前

經(jīng)過上一篇Webpack4.X重修之路 --- 基礎(chǔ)篇
現(xiàn)在我們擁有的項目結(jié)構(gòu)如下:

├── package.json
├── scripts                                   // webpack腳本
│   ├── webpack.common.js      // 公用配置
│   ├── webpack.dev.js              // 開發(fā)環(huán)境下配置
│   └── webpack.prod.js            // 生產(chǎn)環(huán)境下配置
└── src
    ├── assets                              // 全局靜態(tài)文件
    │   └── img
    ├── config                             // 單獨引用的全局配置
    │   ├── ip.config.js
    └── index.js                          // 入口文件  

我們在src目錄下新建一個styles目錄,用于保存項目的一些樣式文件,并在styles下新建一個test.css文件用于測試.

Webpack最強大之處在于它有著很多的loader可以處理不用的文件.
我們要打包css文件主要用到幾個

  1. css-loader: 用于使用import引入css文件
  2. style-loader: 將css文件插入html文件中
  3. extract-text-webpack-plugin: 將css文件從js文件中分離出來

安裝

npm i -D extract-text-webpack-plugin style-loader css-loader

PS: 使用webpack4安裝extract-text-webpack-plugin在打包時會出現(xiàn)警告, 解決方法: 安裝 extract-text-webpack-plugin@next

使用

extract-text-webpack-plugin是插件需要另外引入, loader可以直接使用

//  webpack.common.js
const ExtractTextPlugin = require('extract-text-webpack-plugin');

module.exports = {
  ...
  module: {
        rules: [{
            test: /\.css$/,
            use: ExtractTextPlugin.extract({
                fallback: 'style-loader',
                use: ['css-loader']
            })
        }, ]
} 

plugins: [
...
 new ExtractTextPlugin('[name].css') // 傳入的是打包后的文件路徑以及文件名, 根目錄為dist
]

PS: 在生產(chǎn)模式下我們希望對css文件進行壓縮
需要用到

  • optimize-css-assets-webpack-plugin
  • cssnano
// webpack.prod.js
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
const UglifyJsPlugin = require("uglifyjs-webpack-plugin");
const merge = require('webpack-merge');
const common = require('./webpack.common.js');

module.exports = merge(common, {
    mode: 'production',
    plugins: [
        new OptimizeCSSAssetsPlugin({
            assetNameRegExp: /\.less\.css$/g,
            cssProcessor: require('cssnano'),
            cssProcessorOptions: { discardComments: { removeAll: true } },
            canPrint: true
        }),
    ],
// 設(shè)置optimization.minimizer會覆蓋webpack提供的默認值
//因此請務必同時指定JS minimalizer
    optimization: {
        minimizer: [
            new UglifyJsPlugin({
                cache: true,
                parallel: true,
                sourceMap: false // set to true if you want JS source maps
            }),
            new OptimizeCSSAssetsPlugin({})
        ]
    }
})

Less

實際開發(fā)中大多數(shù)情況會使用css預編譯器以及一些插件

  • postcss-loader
  • autoprefixer :用于自動補充前綴
  • less-plugin-functions: 自定義less函數(shù)
  • style-resources-loader: 定義全局的樣式文件
  • less
  • less-loader: 用于處理less文件

我們希望在開發(fā)過程中只需要編譯less文件,生產(chǎn)模式下才需要添加前綴并且壓縮

  • 開發(fā)模式
// webpack.dev.js
...
const path = require('path');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const LessFunc = require('less-plugin-functions');

module.exports = merge(common, {
...
    module: {
        rules: [{
            test: /\.less$/,
            use: ExtractTextPlugin.extract({
                fallback: 'style-loader',
                use: ['css-loader', {
                    loader: 'less-loader',
                    options: {
                        plugins: [new LessFunc()]
                    }
                }, {
                    loader: 'style-resources-loader',
                    options: {
                patterns: path.resolve(__dirname, '../src/styles/common.less')
                    }
                }]
            })
        }, ]
    }
})

style-resources-loaderoptions.patterns參數(shù)即為需要定義為共同樣式的文件路徑,定義之后可以在其他文件中直接使用次less文件中的變量以及函數(shù)

  • 生產(chǎn)模式
    生產(chǎn)模式比開發(fā)模式多了壓縮以及自動添加前綴的功能
// webpack.prod.js
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const Autoprefixer = require('autoprefixer');
const LessFunc = require('less-plugin-functions');

...

rules: [{
            test: /\.less$/,
            use: ExtractTextPlugin.extract({
                fallback: 'style-loader',
                use: ['css-loader', {
                    loader: 'postcss-loader',
                    options: {
                        plugins: [
                            require('autoprefixer')({
                                browsers: ['last 5 versions']
                            })
                        ]
                    }
                }, {
                    loader: 'less-loader',
                    options: {
                        plugins: [new LessFunc()]
                    }
                }, {
                    loader: 'style-resources-loader',
                    options: {
                patterns: path.resolve(__dirname, '../src/style/common.less')
                    }
                }]
            })
        }]
...
    plugins: [
        Autoprefixer
    ],
  • 這一部分功能代碼重復了,而且到現(xiàn)在我們才添加了處理less的,webpack的配置文件代碼就變得有點多了,我們可以使用webpack-chain對現(xiàn)如今的配置進行重構(gòu).

先放重構(gòu)前的代碼

  • webpack.base.js(原webpack.common.js)
const path = require('path');
const fs = require('fs');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');

module.exports = {
    entry: {
        app: path.resolve(__dirname, '../src/index.js')
    },
    output: {
        filename: 'js/[name].bundle.js',
        path: path.resolve(__dirname, '../dist')
    },
    module: {
        rules: [{
            test: /\.css$/,
            use: ExtractTextPlugin.extract({
                fallback: 'style-loader',
                use: ['css-loader']
            })
        }, ]
    },
    plugins: [
        new CleanWebpackPlugin(),
        new ExtractTextPlugin('[name].css'),
        new CopyWebpackPlugin([
            { from: path.resolve(__dirname, '../src/config/*.js'), to: 'config/', toType: 'dir', flatten: true, ignore: ['*.md'] },
            { from: path.resolve(__dirname, '../src/assets/'), toType: 'dir', ignore: ['*.md'] }
        ]),
        new HtmlWebpackPlugin({
            inject: false,
            template: require('html-webpack-template'),
            title: '測試輸出',
            appMountId: 'app',
            meta: [{
                name: 'viewport',
                content: 'width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0'
            }],
            favicon: path.resolve(__dirname, '../favicon.ico'),
            headHtmlSnippet: getConfigScript('../src/config', 'config/')
        })
    ]
}

/**
 * 獲取script標簽字符串        
 * @param  {String} source    [源目標目錄]
 * @param  {[String]} targetDir [生成的文件夾]
 * @return {[String]}           [指定文件夾下的js文件的script標簽]
 */
function getConfigScript(source, targetDir) {
    let configFiles = fs.readdirSync(path.resolve(__dirname, source), {});

    let jsFiles = configFiles.filter(file => {
        return file.indexOf('.js') !== -1;
    })

    let scripts = jsFiles.map(file => {
        return `<script src="${targetDir + file}"> </script>`
    })

    return scripts.join('\n');
}
  • webpack.dev.js
const path = require('path');
const merge = require('webpack-merge');
const common = require('./webpack.base.js');
const webpack = require('webpack');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const LessFunc = require('less-plugin-functions');

module.exports = merge(common, {
    mode: 'development',
    output: {
        publicPath: '/'
    },
    devtool: 'source-map',
    devServer: {
        contentBase: './dist',
        host: '0.0.0.0',
        port: 8001,
        index: 'index.html',
        open: true,
        hot: true
    },
    module: {
        rules: [{
            test: /\.less$/,
            use: ExtractTextPlugin.extract({
                fallback: 'style-loader',
                use: ['css-loader', {
                    loader: 'less-loader',
                    options: {
                        plugins: [new LessFunc()]
                    }
                }, {
                    loader: 'style-resources-loader',
                    options: {
                        patterns: path.resolve(__dirname, '../src/style/common.less')
                    }
                }]
            })
        }, ]
    },
    plugins: [
        new webpack.HotModuleReplacementPlugin()
    ]
})
  • webpack.prod.js
const path = require('path');
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
const UglifyJsPlugin = require("uglifyjs-webpack-plugin");
const merge = require('webpack-merge');
const common = require('./webpack.base.js');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const Autoprefixer = require('autoprefixer');
const LessFunc = require('less-plugin-functions');

module.exports = merge(common, {
    mode: 'production',
    module: {
        rules: [{
            test: /\.less$/,
            use: ExtractTextPlugin.extract({
                fallback: 'style-loader',
                use: ['css-loader', {
                    loader: 'postcss-loader',
                    options: {
                        plugins: [
                            require('autoprefixer')({
                                browsers: ['last 5 versions']
                            })
                        ]
                    }
                }, {
                    loader: 'less-loader',
                    options: {
                        plugins: [new LessFunc()]
                    }
                }, {
                    loader: 'style-resources-loader',
                    options: {
                        patterns: path.resolve(__dirname, '../src/style/common.less')
                    }
                }]
            })
        }]
    },
    plugins: [
        new OptimizeCSSAssetsPlugin({
            assetNameRegExp: /\.less\.css$/g,
            cssProcessor: require('cssnano'),
            cssProcessorOptions: { discardComments: { removeAll: true } },
            canPrint: true
        }),
        Autoprefixer
    ],
    // 設(shè)置optimization.minimizer會覆蓋webpack提供的默認值枕赵,因此請務必同時指定JS minimalizer
    optimization: {
        minimizer: [
            new UglifyJsPlugin({
                cache: true,
                parallel: true,
                sourceMap: false // set to true if you want JS source maps
            }),
            new OptimizeCSSAssetsPlugin({})
        ]
    }
})
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末熬荆,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子佃扼,更是在濱河造成了極大的恐慌挽牢,老刑警劉巖豪墅,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件用狱,死亡現(xiàn)場離奇詭異贴膘,居然都是意外死亡,警方通過查閱死者的電腦和手機抬虽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進店門官觅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人阐污,你說我怎么就攤上這事休涤。” “怎么了笛辟?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵功氨,是天一觀的道長。 經(jīng)常有香客問我手幢,道長疑故,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任弯菊,我火速辦了婚禮纵势,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘管钳。我一直安慰自己钦铁,他們只是感情好,可當我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布才漆。 她就那樣靜靜地躺著牛曹,像睡著了一般。 火紅的嫁衣襯著肌膚如雪醇滥。 梳的紋絲不亂的頭發(fā)上黎比,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天,我揣著相機與錄音鸳玩,去河邊找鬼阅虫。 笑死,一個胖子當著我的面吹牛不跟,可吹牛的內(nèi)容都是我干的颓帝。 我是一名探鬼主播,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼窝革,長吁一口氣:“原來是場噩夢啊……” “哼宅静!你這毒婦竟也來了浴鸿?” 一聲冷哼從身側(cè)響起专缠,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤鹅龄,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后漆诽,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體侮攀,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡史侣,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了魏身。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片惊橱。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖箭昵,靈堂內(nèi)的尸體忽然破棺而出税朴,到底是詐尸還是另有隱情,我是刑警寧澤家制,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布正林,位于F島的核電站,受9級特大地震影響颤殴,放射性物質(zhì)發(fā)生泄漏觅廓。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一涵但、第九天 我趴在偏房一處隱蔽的房頂上張望杈绸。 院中可真熱鬧,春花似錦矮瘟、人聲如沸瞳脓。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽劫侧。三九已至,卻和暖如春哨啃,著一層夾襖步出監(jiān)牢的瞬間烧栋,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工拳球, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留审姓,地道東北人。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓醇坝,卻偏偏與公主長得像邑跪,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子呼猪,可洞房花燭夜當晚...
    茶點故事閱讀 42,722評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 熟悉 webpack 與 webpack4 配置。 webpack4 相對于 3 的最主要的區(qū)別是所謂的零配置砸琅,但...
    yichen_china閱讀 1,401評論 0 3
  • webpack 是具有打包宋距,轉(zhuǎn)化以及優(yōu)化的工具,而 webpack4.x 相比較 webpack3.x 有了較大的...
    94very閱讀 1,502評論 0 0
  • 版權(quán)聲明:本文為博主原創(chuàng)文章壶唤,未經(jīng)博主允許不得轉(zhuǎn)載雳灵。 webpack介紹和使用 一、webpack介紹 1闸盔、由來 ...
    it筱竹閱讀 11,028評論 0 21
  • GitChat技術(shù)雜談 前言 本文較長悯辙,為了節(jié)省你的閱讀時間,在文前列寫作思路如下: 什么是 webpack迎吵,它要...
    蕭玄辭閱讀 12,671評論 7 110
  • 最美好的或許不是初戀 而是暗戀 那個偷偷喜歡的女孩 在那份偷偷的喜歡里 ...
    火柴太傻了閱讀 161評論 0 0