Игнорировать таблицы стилей компонента Angular2 с ExtractTextPlugin в webpack

#angular #webpack

#angular #webpack

Вопрос:

Я использую плагин html-webpack-plugin для вставки <script> тегов в файл шаблона для фрагментов, которые генерирует webpack. У меня также есть несколько глобальных таблиц стилей, которые я хочу динамически добавлять в <head> раздел моего HTML-шаблона. Для этого я также использую 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 выполняет поиск объявлений templateUrl и styleUrls внутри метаданных компонента Angular 2 и заменяет пути соответствующим оператором require.

Проблема заключается в том, что помимо двух таблиц стилей, которые добавляются в точку app.ts входа, webpack также включает в себя все таблицы стилей, на которые ссылаются мои компоненты Angular2. Потому что он просматривает зависимости приложения и находит require инструкции, добавленные angular2-template-loader. Это нежелательно, потому что я хочу, чтобы эти таблицы стилей были локальными для компонентов, которым они принадлежат, т. Е. Были встроенными в JS.

Поэтому мне нужен способ включать только те таблицы стилей, которые я require использую непосредственно в своих точках входа, возможно, каким-то образом исключая таблицы стилей компонентов. Проблема в том, что в обоих случаях файл имеет .css расширение, поэтому они оба проходят тест на extract-text-plugin . Я просмотрел документацию загрузчиков / плагинов, но не смог найти ничего, что решило бы мою проблему.

Итак, я хочу добавить app.[contenthash].css файл в мой файл шаблона (эта часть работает), но исключая таблицы стилей, на которые ссылаются в моих компонентах Angular2 (включены из-за angular2-template-loader).

Как я могу предотвратить включение этих таблиц стилей компонента Angular2 в таблицу стилей, добавляемую в мой HTML-шаблон?

Ответ №1:

Допустим, у вас следующая структура папок:

введите описание изображения здесь

Итак, все мои компоненты расположены внутри папки приложения.

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
}
  

Комментарии:

1. Ах, это выглядит довольно хорошо! Я как можно скорее проверю это и сообщу вам, как это происходит. Спасибо!

2. Я попытался поиграть с этим, но не смог заставить его работать. Прежде всего, мои CSS-файлы расположены по адресу public/css , где расположены таблицы стилей для компонентов public/css/components/**/*.css . Я попытался исключить /public/css/components/ в первом загрузчике и использовал raw для второго, наряду с множеством различных тестов и исключений, с различными ошибками. Я не уверен, что я путаюсь с путями / регулярным выражением или в чем проблема. Если бы вы могли привести пример с вышеупомянутыми путями, это было бы действительно здорово, потому что я, похоже, не могу заставить его работать.

3. Попробуйте /public\css\components/ или /css\components/

4. После опробования тонны комбинаций мне удалось заставить его работать. Хотя я не добился успеха именно с тем, что вы предложили, это было достаточно близко, чтобы направить меня на правильный путь, так что большое спасибо за это! Я добавил ответ с моим решением для всех, кому это может понадобиться. Приветствия!

Ответ №2:

Вы могли бы дать webpack подсказку, чтобы различать файлы CSS, используя два отдельных шаблона имен. Допустим, вы называете все файлы, которые должны быть включены в angular2-template-loader, с помощью суффикса .tl (сокращение от template loader, например mycss.tl.css ), а другие — с помощью суффикса .pt (сокращение от platform, например 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 по-прежнему обертывает пути css require вызовами, но в зависимости от вашей конфигурации у вас может быть другой набор загрузчиков для файлов css.

Комментарии:

1. На самом деле я думал о том, чтобы сделать что-то подобное, но мне это показалось взломом, поэтому я подумал, есть ли лучшее решение. Я немного поиграю с этим и посмотрю, что я могу придумать. Спасибо за ваш ответ! 🙂

Ответ №3:

Для всех, кому интересно, мне удалось решить проблему, вдохновленный ответом @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');
  

Я надеюсь, что это кому-то поможет.