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最強(qiáng)大之處在于它有著很多的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文件進(jìn)行壓縮
需要用到

  • 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提供的默認(rèn)值
//因此請務(wù)必同時指定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預(yù)編譯器以及一些插件

  • postcss-loader
  • autoprefixer :用于自動補(bǔ)充前綴
  • 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
    ],
  • 這一部分功能代碼重復(fù)了,而且到現(xiàn)在我們才添加了處理less的,webpack的配置文件代碼就變得有點多了,我們可以使用webpack-chain對現(xiàn)如今的配置進(jì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標(biāo)簽字符串        
 * @param  {String} source    [源目標(biāo)目錄]
 * @param  {[String]} targetDir [生成的文件夾]
 * @return {[String]}           [指定文件夾下的js文件的script標(biāo)簽]
 */
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提供的默認(rèn)值,因此請務(wù)必同時指定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)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

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

友情鏈接更多精彩內(nèi)容