【问题标题】:Webpack - How do I configure Webpack to bundle global and modularized CSS differently?Webpack - 如何配置 Webpack 以不同方式捆绑全局和模块化 CSS?
【发布时间】:2017-07-31 13:28:56
【问题描述】:

我目前正在开发一个 Angular 2 项目,并使用 Webpack 2 来捆绑代码和资产。 Angular 应用程序使用 Bootstrap 和一个包含一些全局样式的 styles.css。

此外,Angular 组件本身在单独的 .css 文件中都有自己的样式表。以下面的文件结构为例:

app/index.html
app/styles.css
app/component/component.js
app/component/component.html
app/component/component.css

目前,Webpack 将所有这些文件同时包含 .css 和 .js 并将它们捆绑在一起,因此我有以下输出(将 CSS 合并到 javascript 中):

dist/index.html
dist/js/app.bundle.js

我遇到的主要问题是这会创建一个 FOUC(无样式内容的 Flash)——据我了解,修复它的方法是将 CSS 的代码拆分更改为将 CSS 输出到它自己的文件中,这样它就可以被异步加载浏览器 - 使用 Extract-Text-Webpack-Plugin。

这很好用,除了使用这种方法我还有另一个问题。我失去了拥有 Angular 通过 component.css 文件提供的模块化 CSS 的能力...

所以我有以下2个问题:

1.) 我如何配置 Webpack 以将 Bootstrap 和我的 styles.css 提取到例如“dist/styles.css”,但将其余“非全局”css 文件捆绑为 Webpack 创建的 JavaScript 捆绑包的一部分?

2.) 是否可以将提取的 CSS 注入 index.html 的 HEAD 部分,就像通过 HTML-webpack-plugin 注入捆绑包一样?

new HtmlWebpackPlugin({
    filename: 'index.html',
    inject: 'body',
    template: 'ngApp/index.html'
})

(这样我就不必硬编码 href 以指向如果有人更改 Webpack 配置可能会改变的输出路径。)

我目前对这个问题的解决方案是使用 copy-webpack-plugin 将 bootstrap.min.cssstyles.css 移动到 dist 并在 index.html 中使用硬编码链接 - 但我不喜欢这种设置。欢迎提出任何建议。

谢谢。

【问题讨论】:

  • 如果我理解正确,我试图回答一个类似的问题here。我也在寻找更好的解决方案,但这个现在对我有用。

标签: angular webpack


【解决方案1】:

以防万一这仍然相关: 当我试图为 Spring Boot 应用程序构建 Angular 4 前端并面临完全相同的问题时,我设法找到了您的第一个问题的解决方案。这是我的 webpack.config.js:

// Helper: root() is defined at the bottom
var path = require('path');
var webpack = require('webpack');

// Webpack Plugins
var CommonsChunkPlugin = webpack.optimize.CommonsChunkPlugin;
var autoprefixer = require('autoprefixer');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
var OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');

/**
 * Env
 * Get npm lifecycle event to identify the environment
 */
var ENV = process.env.npm_lifecycle_event;
var isProd = ENV === 'build';

module.exports = function makeWebpackConfig() {
    /**
     * Config
     * Reference: http://webpack.github.io/docs/configuration.html
     * This is the object where all configuration gets set
     */
    var config = {};

    /**
     * Devtool
     * Reference: http://webpack.github.io/docs/configuration.html#devtool
     * Type of sourcemap to use per build type
     */
    if (isProd) {
        config.devtool = 'source-map';
    }
    else {
        config.devtool = 'eval-source-map';
    }

    /**
     * Entry
     * Reference: http://webpack.github.io/docs/configuration.html#entry
     */
    config.entry = {
        'polyfills': './src/main/frontend/polyfills.ts',
        'vendor': './src/main/frontend/vendor.ts',
        'style': './src/main/frontend/style/global.scss',   // global styles
        'app': './src/main/frontend/main.ts'                // angular app
    };

    /**
     * Output
     * Reference: http://webpack.github.io/docs/configuration.html#output
     */
    config.output = {
        path: root('src/main/webapp'),
        publicPath: isProd ? '/' : 'http://localhost:8080/',
        filename: 'js/[name].js',
        chunkFilename: '[id].chunk.js'
    };

    /**
     * Resolve
     * Reference: http://webpack.github.io/docs/configuration.html#resolve
     */
    config.resolve = {
        // only discover files that have those extensions
        extensions: ['.ts', '.js', '.json', '.css', '.scss', '.html']
    };

    /**
     * Loaders
     * Reference: http://webpack.github.io/docs/configuration.html#module-loaders
     * List: http://webpack.github.io/docs/list-of-loaders.html
     * This handles most of the magic responsible for converting modules
     */
    config.module = {
        rules: [

            // Support for .ts files.
            {
                test: /\.ts$/,
                use: ['awesome-typescript-loader', 'angular2-template-loader', '@angularclass/hmr-loader', 'angular2-router-loader'],
                exclude: [/\.(spec|e2e)\.ts$/, /node_modules\/(?!(ng2-.+))/]
            },

            // copy those assets to output
            {
                test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
                use: 'file-loader?name=fonts/[name].[ext]?'
            },

            // Support for *.json files.
            {
                test: /\.json$/,
                loader: 'json-loader'
            },

            // build global css bundle from src/main/frontend/style/global.css
            {
                test: /global\.(css|scss)/,
                use: ExtractTextPlugin.extract(['css-loader', 'sass-loader'])
            },

            // all css required in src/main/frontend files will be merged in js files
            {
                test: /\.(css|scss|sass)$/,
                loader: 'raw-loader!postcss-loader!sass-loader',
                exclude: [/global\.(css|scss)/]
            },

            // support for .html as raw text
            // todo: change the loader to something that adds a hash to images
            {
                test: /\.html$/,
                use: 'raw-loader'
            }
        ]
    };

    /**
     * Plugins
     * Reference: http://webpack.github.io/docs/configuration.html#plugins
     * List: http://webpack.github.io/docs/list-of-plugins.html
     */
    config.plugins = [
        // Define env variables to help with builds
        // Reference: https://webpack.github.io/docs/list-of-plugins.html#defineplugin
        new webpack.DefinePlugin({
            // Environment helpers
            'process.env': {
                ENV: JSON.stringify(ENV)
            }
        }),


        // Workaround needed for angular 2 angular/angular#11580
        new webpack.ContextReplacementPlugin(
            // The (\\|\/) piece accounts for path separators in *nix and Windows
            /angular(\\|\/)core(\\|\/)(esm(\\|\/)src|src)(\\|\/)linker/,
            root('./src/main/frontend') // location of your src
        ),

        new webpack.LoaderOptionsPlugin({
            options: {
                /**
                 * PostCSS
                 * Reference: https://github.com/postcss/autoprefixer-core
                 * Add vendor prefixes to your css
                 */
                postcss: [
                    autoprefixer({
                        browsers: ['last 2 version']
                    })
                ]
            }
        }),

        // Generate common chunks if necessary
        // Reference: https://webpack.github.io/docs/code-splitting.html
        // Reference: https://webpack.github.io/docs/list-of-plugins.html#commonschunkplugin
        new CommonsChunkPlugin({
            name: ['app', 'vendor', 'polyfills']
        }),

        // Extract css files
        // Reference: https://github.com/webpack/extract-text-webpack-plugin
        // Disabled when in test mode or not in build mode
        new ExtractTextPlugin({
            filename: 'css/[name].min.css'
        })
    ];

    // Add build specific plugins
    if (isProd) {
        config.plugins.push(
            // Reference: http://webpack.github.io/docs/list-of-plugins.html#noerrorsplugin
            // Only emit files when there are no errors
            new webpack.NoEmitOnErrorsPlugin(),

            // Reference: http://webpack.github.io/docs/list-of-plugins.html#uglifyjsplugin
            // Minify all javascript, switch loaders to minimizing mode
            new webpack.optimize.UglifyJsPlugin({sourceMap: true, mangle: {keep_fnames: true}}),


            // optimize CSS assets
            // Reference: https://github.com/NMFR/optimize-css-assets-webpack-plugin
            new OptimizeCssAssetsPlugin({
                assetNameRegExp: /\.css$/g,
                cssProcessor: require('cssnano'),
                cssProcessorOptions: {
                    discardComments:
                        {
                            removeAll: true
                        }
                },
                canPrint: true
            }),

            // // Reference: https://github.com/webpack-contrib/webpack-bundle-analyzer
            new BundleAnalyzerPlugin({
                // Can be `server`, `static` or `disabled`.
                // In `server` mode analyzer will start HTTP server to show bundle report.
                // In `static` mode single HTML file with bundle report will be generated.
                // In `disabled` mode you can use this plugin to just generate Webpack Stats JSON file by setting `generateStatsFile` to `true`.
                analyzerMode: 'static',
                // Host that will be used in `server` mode to start HTTP server.
                analyzerHost: '127.0.0.1',
                // Port that will be used in `server` mode to start HTTP server.
                analyzerPort: 8888,
                // Path to bundle report file that will be generated in `static` mode.
                // Relative to bundles output directory.
                reportFilename: root('./report.html'),
                // Module sizes to show in report by default.
                // Should be one of `stat`, `parsed` or `gzip`.
                // See "Definitions" section for more information.
                defaultSizes: 'parsed',
                // Automatically open report in default browser
                openAnalyzer: false,
                // If `true`, Webpack Stats JSON file will be generated in bundles output directory
                generateStatsFile: false,
                // Name of Webpack Stats JSON file that will be generated if `generateStatsFile` is `true`.
                // Relative to bundles output directory.
                statsFilename: 'stats.json',
                // Options for `stats.toJson()` method.
                // For example you can exclude sources of your modules from stats file with `source: false` option.
                // See more options here: https://github.com/webpack/webpack/blob/webpack-1/lib/Stats.js#L21
                statsOptions: null,
                // Log level. Can be 'info', 'warn', 'error' or 'silent'.
                logLevel: 'info'
            })
        );
    }

    return config;
}();

// Helper functions
function root(args) {
    args = Array.prototype.slice.call(arguments, 0);
    return path.join.apply(path, [__dirname].concat(args));
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-01-04
    • 2022-01-03
    • 2020-01-10
    • 1970-01-01
    • 2018-05-27
    • 1970-01-01
    • 1970-01-01
    • 2017-04-02
    相关资源
    最近更新 更多