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

2019-03-22  本文已影响0人  Homary

前言

这一篇主要开始介绍使用webpack4打包css以及less(stylus和sass同理)

开始之前

经过上一篇Webpack4.X重修之路 --- 基础篇
现在我们拥有的项目结构如下:

├── package.json
├── scripts                                   // webpack脚本
│   ├── webpack.common.js      // 公用配置
│   ├── webpack.dev.js              // 开发环境下配置
│   └── webpack.prod.js            // 生产环境下配置
└── src
    ├── assets                              // 全局静态文件
    │   └── 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在打包时会出现警告, 解决方法: 安装 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: 在生产模式下我们希望对css文件进行压缩
需要用到

// 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
        }),
    ],
// 设置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

实际开发中大多数情况会使用css预编译器以及一些插件

我们希望在开发过程中只需要编译less文件,生产模式下才需要添加前缀并且压缩

// 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参数即为需要定义为共同样式的文件路径,定义之后可以在其他文件中直接使用次less文件中的变量以及函数

// 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
    ],

先放重构前的代码

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');
}
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()
    ]
})
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
    ],
    // 设置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({})
        ]
    }
})
上一篇 下一篇

猜你喜欢

热点阅读