在Webpack中使用ExtractTextPlugin忽略Angular2组件样式表

5

我正在使用 html-webpack-plugin 插件将 webpack 生成的代码块注入到模板文件中。此外,我还有一些全局样式表,希望能够动态地添加到 HTML 模板的 <head> 部分中。为此,我也使用了 html-webpack-plugin 插件(在这种情况下,app 入口/代码块是相关的)。

module.exports = {
    entry: {
        'polyfills': './ts/polyfills.ts',
        'vendor': './ts/vendor.ts',
        'app': './ts/app.ts'
    },

    module: {
        loaders: [
            {
                test: /\.ts$/,
                loaders: ['ts', 'angular2-template-loader']
            },
            {
                test: /\.html$/,
                loader: 'html-loader'
            },
            {
                test: /\.css$/,
                loaders: ['to-string-loader', ExtractTextPlugin.extract('style-loader', 'css-loader')]
            }
        ]
    },

    plugins: [
        new ExtractTextPlugin('css/[name].[contenthash].css'),

        new webpack.ProvidePlugin({
            bootstrap: 'bootstrap'
        }),

        new webpack.optimize.CommonsChunkPlugin({
            name: 'app',
            filename: 'js/[name].[chunkhash].min.js',
            chunks: ['app']
        }),

        new webpack.optimize.CommonsChunkPlugin({
            name: 'vendor',
            filename: 'js/[name].[chunkhash].min.js',
            chunks: ['vendor']
        }),

        new webpack.optimize.CommonsChunkPlugin({
            name: 'polyfills',
            filename: 'js/[name].[chunkhash].min.js',
            chunks: ['polyfills']
        }),

        new HtmlWebpackPlugin({
            template: 'my-template.html',
            filename: 'my-template.html',
            inject: 'body',
            hash: false
        }),
    ]
};

以下是 app.ts 文件,我在其中添加了require语句以引入我想要包含在模板中的每个样式表(它们被合并为一个单独的样式表)。

require('main.css');
require('misc.css');

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app.module';

platformBrowserDynamic().bootstrapModule(AppModule);

基本上这个方法很好用,但它有一个限制,因为我使用的是 Angular2 应用程序,该应用程序由组件组成,这些组件可能具有相关联的样式表,就像这个例子中。
@Component({
    styleUrls: [
        'my-stylesheet.css'
    ]
})
export class MyComponent { ... }

通常情况下,构建应用程序会导致为组件的样式表进行大量的HTTP调用,因此为了解决这个问题,我使用angular2-template-loader将样式表直接拉入组件中。该加载器的工作原理如下:

angular2-template-loader在Angular 2组件元数据中搜索templateUrl和styleUrls声明,并将路径替换为相应的require语句。

但是这样做的问题在于,除了添加到app.ts入口点的两个样式表之外,webpack还包括从我的Angular2组件引用的每个样式表。因为它通过应用程序的依赖项并找到由angular2-template-loader添加的require语句。这是不可取的,因为我希望这些样式表是属于它们所属的组件的本地样式表,即内联在JS中。
因此,我需要一种方法来仅包含我在入口点中直接require的样式表,也许是通过某种方式排除组件样式表。问题在于,在这两种情况下,文件都具有.css扩展名,因此它们都通过了extract-text-plugin的测试。我查看了加载器/插件的文档,但找不到可以解决我的问题的东西。
因此,我想要的是在模板文件中添加app.[contenthash].css文件(这部分有效),但是排除由于angular2-template-loader而引用的样式表
我该如何防止将这些Angular2组件样式表包含在添加到我的HTML模板中的样式表中?
3个回答

5
假设你有以下文件夹结构:

enter image description here

所以我的所有组件都位于app文件夹中。

main.ts

require('main.css');
require('misc.css');

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app.module';

platformBrowserDynamic().bootstrapModule(AppModule);

/app/app.component.ts

@Component({
  selector: 'my-app',
  template: `<h1>My Angular 2 App</h1>`,
  styleUrls: ['my-stylesheet.css']
})
export class AppComponent {}

那么你的解决方案可能是这样的:

webpack.config.js

{
  test: /\.css$/,
  exclude: /app\\.+\.css$/, <== add this (exclude all styles from app folder)
  loaders: ['to-string-loader', ExtractTextPlugin.extract('style-loader', 'css-loader')]
},
{
  test: /app\\.+\.css$/,
  loader: 'raw'  <== use raw loader for these files
}

啊,看起来不错!我会尽快试一下并告诉你效果如何。谢谢! - ba0708
我尝试着调整了一下,但是没能让它正常工作。首先,我的CSS文件位于public/css,而组件的样式表位于public/css/components/**/*.css。我尝试在第一个加载器中排除/public\/css\/components/,并在第二个加载器中使用raw,以及进行了许多不同的测试和排除,但出现了各种不同的错误。我不确定是路径/正则表达式出了问题还是其他问题。如果您能提供一个带有上述路径的示例,那真的太棒了,因为我似乎无法让它正常工作。 - ba0708
尝试使用/public\\css\\components/或者/css\\components/ - yurzui
经过尝试了大量的组合,我终于让它工作了。虽然我没有成功地实现你所建议的方法,但它足够接近以至于让我找到了正确的方向,所以非常感谢你!我已经添加了一个答案,附上我的解决方案,供需要的人参考。干杯! - ba0708

1
你可以通过使用两种不同的名称模式来提示webpack区分CSS文件。假设你将所有应该由angular2-template-loader包含的文件命名为带有后缀.tl(模板加载器的简写),例如mycss.tl.css,而将另一个文件命名为带有后缀.pt(平台的简写),例如main.pt.css,则可以按照以下方式编写webpack配置:
module: {
        loaders: [
            {
                test: /\.ts$/,
                loaders: ['ts', 'angular2-template-loader']
            },
            {
                test: /\.pt\.css$/,
                loaders: ['to-string-loader', ExtractTextPlugin.extract('style-loader', 'css-loader')]
            },
            {
                test: /\.tl\.css$/,
                loaders: ['style-loader', 'css-loader']
            }
        ]
    }

你的组件定义看起来像这样:

@Component({
    styleUrls: [
        'my-stylesheet.tl.css'
    ]
})
export class MyComponent { ... }

而在 app.ts 中,您放置了

require('main.pt.css');
require('misc.pt.css');

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app.module';

platformBrowserDynamic().bootstrapModule(AppModule);

angular2-template-loader仍然使用require调用对CSS路径进行包装,但是基于您的配置,您可以为CSS文件指定不同的加载器。

我其实也考虑过这样做,但在我看来似乎有些取巧,所以我想知道是否有更好的解决方案。我会试着玩一下,看看能不能想出什么办法。谢谢你的回答! :-) - ba0708

0

对于任何感兴趣的人,我成功解决了这个问题,受到@yurzui答案的启发。

module: {
    loaders: [
        {
            test: /\.ts$/,
            loaders: ['ts', 'angular2-template-loader']
        },
        {
            test: /\.css$/,
            exclude: [helpers.root('public', 'css', 'components')],
            loaders: ['to-string-loader', ExtractTextPlugin.extract('style-loader', 'css-loader')]
        },
        {
            test: /\.css$/,
            include: [helpers.root('public', 'css', 'components')],
            loaders: ['raw-loader']
        }
    ]
}

在上面的例子中,我使用了自己的自定义助手来组装目录路径。你也可以用其他方法来实现,但这是我的助手(取自Angular2的文档)。
var path = require('path');
var _root = path.resolve(__dirname, '..'); // This file is stored within a webpack subdirectory in my case

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

exports.root = root;

要使用它,只需在您的webpack配置中引用它。

var helpers = require('./helpers');

希望这能帮助到某个人。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接