Замена горячего модуля webpack: css без обновления

До сих пор я использовал функцию livereload, так что всякий раз, когда я меняю JS или шаблоны, страница обновляется, и когда я меняю CSS, это будет hotswap новый CSS без обновления.

Сейчас я тестирую webpack и почти добрался до одного и того же поведения, за одним исключением: при изменении CSS он обновляет все окно. Возможно ли сделать это hotswap CSS без обновления?

Конфигурация до сих пор:

var webpackConfig = {
    entry: ["webpack/hot/dev-server", __dirname + '/app/scripts/app.js'],
    debug: true,
    output: {
        path: __dirname + '/app',
        filename: 'scripts/build.js'
    },
    devtool: 'source-map',
    plugins: [
        new webpack.HotModuleReplacementPlugin(),
        new htmlWebpackPlugin({
            template: __dirname + '/app/index.html',
            inject: 'body',
            hash: true,
            config: config
        }),
        new webpack.ProvidePlugin({
            'angular': 'angular'
        }),
        new ExtractTextPlugin("styles.css")
    ],
    module: {
        loaders: [
            {
                test: /\.scss$/,
                loader: "style!css!sass?includePaths[]=" + __dirname + "/app/bower_components/compass-mixins/lib&includePaths[]=" + __dirname + '/instance/sth/styles&includePaths[]=' + __dirname + '/app/scripts'
            }
        ]
    }
};

Ответы

Ответ 1

Это один из недостатков использования ExtractTextPlugin, как указано в README проекта. Вы можете решить проблему, разделив конфигурацию. То есть. имеют отдельную конфигурацию для разработки без нее и одну для производства с ней.

Ответ 2

Теперь можно использовать angular2, webpack с горячей заменой модуля, sass sourcemaps и загружаемый извне css. Мне понадобилось несколько дней, чтобы играть с ним, но я получил работу!

Зависимости style-loader, css-loader и sass-loader (при использовании sass, если нет, загрузчик sass можно удалить)

Я использую ExtractTextPlugin для режима производства, чтобы испускать фактические .css файлы.

ПРИМЕЧАНИЕ. Чтобы это сработало, я не использую свойство stylesUrl, вместо этого я импортирую файл .scss за пределы декоратора @Component, чтобы стили загружались в глобальном контексте, а не в области с помощью компонента.

Эта конфигурация позволяет заменять Hot Module с помощью файлов SCSS с помощью сервера webpack dev и extracttextplugin для режима производства для извлечения фактических файлов .css.

Здесь моя рабочая конфигурация

{
        test: /\.(scss)$/,
        use:
          isDevServer ? [
              {
                loader: 'style-loader',
              },             
              {
                loader: 'css-loader',
                options: { sourceMap: true }
              },
              {
                loader: 'postcss-loader',
                options: { postcss: [AutoPrefixer(autoPrefixerOptions)], sourceMap: true }
              },
              {
                loader: 'sass-loader',
                options: { sourceMap: true }
              },
              {
                loader: 'sass-resources-loader',
                options: {
                  resources: [
                    './src/assets/styles/variables.scss',
                    './src/assets/styles/mixins.scss']
                }
              }, 
              /**
               * The sass-vars-loader will convert the 'vars' property or any module.exports of 
               * a .JS or .JSON file into valid SASS and append to the beginning of each 
               * .scss file loaded.
               *
               * See: https://github.com/epegzz/sass-vars-loader
               */
              {
                loader: '@epegzz/sass-vars-loader?',
                options: querystring.stringify({
                  vars: JSON.stringify({
                    susyIsDevServer: susyIsDevServer
                  })
                })
              }] : // dev mode
          ExtractTextPlugin.extract({
            fallback: "css-loader",
            use: [
              {
                loader: 'css-loader',
                options: { sourceMap: true }
              },
              {
                loader: 'postcss-loader',
                options: { postcss: [AutoPrefixer(autoPrefixerOptions)], sourceMap: true }
              },
              {
                loader: 'sass-loader',
                options: { sourceMap: true }
              },
              {
                loader: 'sass-resources-loader',
                options: {
                  resources: [
                    './src/assets/styles/variables.scss',
                    './src/assets/styles/mixins.scss']
                }
              }, {
                loader: '@epegzz/sass-vars-loader?',
                options: querystring.stringify({
                  vars: JSON.stringify({
                    susyIsDevServer: susyIsDevServer
                  })
                  // // Or use 'files" object to specify vars in an external .js or .json file
                  // files: [
                  //    path.resolve(helpers.paths.appRoot + '/assets/styles/sass-js-variables.js')
                  // ],
                })
              }],
            publicPath: '/' // 'string' override the publicPath setting for this loader
          })
      },

Затем в вашем компоненте, например, app.component.ts, вам понадобится файл app.style.scss ВНЕШНЕГО декодера @Component.

Это трюк. Это не будет работать, если вы загрузите стили "angular" с помощью stylesUrl. Выполнение этого способа позволит вам ленить нагрузку .css таблиц стилей для компонентов, которые ленивы загружены, что делает начальное время загрузки еще быстрее.

app.component.css

/*
 * THIS IS WHERE WE REQUIRE CSS/SCSS FILES THAT THIS COMPONENT NEEDS
 *
 * Function: To enable so-called "Lazy Loading" CSS/SCSS files "on demand" as the app views need them.
 * Do NOT add styles the "Angular2 Way" in the @Component decorator ("styles" and "styleUrls" properties)
 */
    import './app.style.scss'

/**
 * AppComponent Component
 * Top Level Component
 */
@Component({
   selector: 'body',
   encapsulation: ViewEncapsulation.None,
   host: { '[class.authenticated]': 'appState.state.isAuthenticated' },
   templateUrl: './app.template.html'
})

У меня не было проблем с этой настройкой. Здесь вы идете!

sass_sourcemaps _hmr _wds

Обновлено 08/2017: Улучшена конфигурация для требований к схеме webpack 3+ и для работы с Angular 4 компиляцией AOT.

Ответ 3

На самом деле это простой способ сделать это. Я использую sass-loader с extract-text-plugin, который создает css файлы.

Что вам нужно сделать, это добавить id в ваш css include

  <link id="js-style" type="text/css" rel="stylesheet" href="/static/main.css">

Теперь вам нужно убедиться, что когда HMR происходит, вы обновляете url с текущей версией/меткой времени. Вы можете сделать это следующим образом:

import '../style/main.scss'
if (module.hot) {
  module.hot.accept('../style/main.scss', () => {
    const baseStyle = window.document.getElementById('js-style')
    baseStyle.setAttribute('href', '/static/main.css?v=' + new Date().valueOf)
  })
}

Таким образом, всякий раз, когда изменяется css, мы исправляем URL-адрес css include, чтобы перезагрузить его.

Ответ 4

Хотя ExtractTextPlugin заявляет "No Hot Module Replacement" в разделе README, Я исправил это, вручную обновив файлы CSS через API BrowserSync.

Я использовал gaze для прослушивания любых изменений в моих файлах CSS, а затем использовал BrowserSync для их обновления.

    npm install gaze

Вы также можете легко сделать это, отредактировав свою сборку script следующим образом:

  const { Gaze } = require('gaze');

  // Your own BrowserSync init
  browserSync.init({
    ...
  }, resolve);

  // Create a watcher:
  // You can watch/reload specific files according to your build/project structure
  const gaze = new Gaze('**/*.css');
  gaze.on('all', () => bs.reload('*.css'));

Надеюсь, что это поможет.