[Webpack4] MiniCssExtractPlugin для настройки CSS

Webpack

Адрес репозитория на гитхабе:GitHub.com/Эвелин ззз/день…

Версия:Webpack 4.39.1

Связанные зависимости:

Определите, является ли это режимом разработки или режимом производства

При настройке Webpack необходимо различать, используется ли он в режиме разработки или в рабочем режиме. Например, нам нужно только сжимать CSS в рабочем режиме, в режиме разработки мы также хотим сгенерировать Sourcemap для отладки и горячего обновления стилей. Итак, как оценить режим разработки и производства в webpack.config.js?

Обычно я определяю три файла конфигурации веб-пакета:

  • webpack.config.base.js: Общая конфигурация, такая как вход, выход, плагин, загрузчик и т. д. Следующие два файла конфигурации введут эту конфигурацию, а затем изменят и добавят другие конфигурации.
  • webapck.config.dev.js: В режиме разработки запустите webpack-dev-server.
  • webapck.config.prod.js: В рабочем режиме компилировать и упаковывать.

Затем настройте его отдельно в package.jsonstartа такжеbuildсценарий:

{
    "scripts": {
        "start": "cross-env NODE_ENV=development webpack-dev-server --config webpack.config.dev.js --open",
        "build": "cross-env NODE_ENV=production webpack --config webpack.config.prod.js --progress --colors -p"
    }
}

Обратите внимание, что переменная определяется в команде какNODE_ENV, поэтому в webpack.config.base.js вы можете передатьprocess.env.NODE_ENVПолучает свое значение, таким образом оценивая режим производства или режим разработки.

const devMode = process.env.NODE_ENV === 'development'; // 是否是开发模式

Далее переходите к делу.

Извлечь CSS в отдельный файл

До Webpack 4 мы использовалиextract-text-webpack-pluginПлагин для извлечения файлов стилей, представленных в проекте, упакованных в отдельный файл. Начиная с Webpack 4, этот плагин устарел и его необходимо использовать.MiniCssExtractPlugin.

This plugin extracts CSS into separate files. It creates a CSS file per JS file which contains CSS. It supports On-Demand-Loading of CSS and SourceMaps.

этот плагинСоздайте отдельный файл CSS для каждого файла JS, содержащего CSS.и поддерживает загрузку CSS и SourceMap по требованию.

Уведомление: Каждый JS-файл, содержащий CSS, упомянутый здесь, означает не JS-файл, соответствующий компоненту, а упакованный JS-файл! Это будет подробно объяснено далее.

Сценарий 1

Сначала рассмотрим пример базовой конфигурации.webpack.config.js:

const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
  plugins: [
    new MiniCssExtractPlugin({
      filename: '[name].css'
    }),
  ],
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader, 'css-loader','postcss-loader' // postcss-loader 可选
        ],
      },{
        test: /\.less$/,
        use: [
          MiniCssExtractPlugin.loader, 'css-loader','postcss-loader','less-loader' // postcss-loader 可选
        ],
      }
    ],
  },
};

В соответствии с приведенной выше конфигурацией, если в записи app.js есть ссылка на Root, Root вводит разделы. Ссылка на стиль main.css содержится в файле Root.js, а ссылка на файл Topics.css — в файле Topics.js.

// 入口文件 app.js
import Root from './components/Root'

// Root.js
import '../styles/main.less'
import Topics from './Topics'

// Topics.js
import "../styles/topics.less"

В этом случае Topics будут принадлежать тому же чанку, что и Root, поэтому они будут упакованы в app.js вместе, в результате main.less и Topics.less будут извлечены в один файл: app.css. вместо создания двух файлов css.

            Asset       Size  Chunks                    Chunk Names
          app.css  332 bytes       1  [emitted]         app
           app.js    283 KiB       1  [emitted]  [big]  app

Сценарий 2

Однако, если Root.js не вводит компонент Topics напрямую, а настраиваетразделение кода, например модульДинамичное введение, то результат другой:

            Asset       Size  Chunks                    Chunk Names
          app.css  260 bytes       1  [emitted]         app
           app.js    281 KiB       1  [emitted]  [big]  app
 topics.bundle.js   2.55 KiB       4  [emitted]         topics
       topics.css   72 bytes       4  [emitted]         topics

Поскольку на данный момент есть два фрагмента, соответствующие двум файлам JS, CSS из двух файлов JS будет извлечен для создания соответствующих файлов. Вот что на самом деле означает «создать отдельный файл CSS для каждого файла JS, содержащего CSS».

Сценарий третий

Но что, если вы разделите фрагменты и хотите сгенерировать только один файл CSS? Это также можно сделать. Но нужно использовать конфигурацию Webpackoptimization.splitChunks.cacheGroups.

optimization.splitChunksДля чего это? До Webpack 4 мы использовалиCommonsChunkPluginДля извлечения повторно вводимых сторонних зависимостей, таких как извлечение React и Jquery в один файл. А начиная с Webpack 4,CommonsChunkPluginодеялоoptimization.splitChunksзаменены. Из названия также видно, что он используется для разделения чанков. Почему нам нужно использовать эту конфигурацию здесь? Давайте посмотрим, как пишется конфигурация:

optimization: {
  splitChunks: {
    cacheGroups: {
      // Extracting all CSS/less in a single file
      styles: {
      	name: 'styles',
        test: /\.(c|le)ss$/,
        chunks: 'all',
        enforce: true,
      },
    }
  }
},

Результат упаковки:

            Asset       Size  Chunks                    Chunk Names
           app.js    281 KiB       2  [emitted]  [big]  app
 styles.bundle.js  402 bytes       0  [emitted]         styles
       styles.css  332 bytes       0  [emitted]         styles
 topics.bundle.js   2.38 KiB       5  [emitted]         topics

Видно, что стили действительно извлекаются в файл styles.css. Но при этом есть еще один файл style.bundle.js, которыйoptimization.splitChunks.cacheGroupsЭффект. Конкретные принципы здесь не обсуждаются, если интересно, можете изучить.

MiniCssExtractPlugin vs. style-loader

Во-первых, назначение этих двух плагинов совершенно разное: MiniCssExtractPlugin извлекает CSS, представленный в JS, и упаковывает его в отдельный файл, а затем использует теги<link>добавлено в шапку, передан style-loader<style>Теги вставляют CSS непосредственно в DOM.

Обычно базовая конфигурация CSS выглядит примерно так. Сначала загрузчик стилей, затем загрузчик css.

module: {
    rules: [
    {
        test: /\.css$/,
            use: [
                'style-loader', 'css-loader'
            ],
        },
    ],
}

Но затем, поскольку вы хотите извлечь CSS в отдельный файл, вам нужно использовать MiniCssExtractPlugin. Итак, вопрос в том, возможна ли следующая конфигурация?

{
    test: /\.css$/,
        use: [
            'style-loader', MiniCssExtractPlugin.loader, 'css-loader','postcss-loader'
        ],
}

производственный режим

согласно сДокументация по MiniCssExtractPluginКак уже упоминалось, этот плагин работает безstyle-loaderв производственном режиме и в режиме разработки, для которого требуется HMR.

This plugin should be used only on production builds without style-loader in the loaders chain, especially if you want to have HMR in development.

То есть в режиме производства одновременно используется указанная выше конфигурация.style-loaderи MiniCssExtractPlugin не подходит (пробовал,style-loaderне будет работать).

Мы можем взять только одну из них. Его также можно комбинировать следующим образом, используя загрузчик стилей в режиме разработки и MiniCssExtractPlugin в режиме производства. Каждый берет то, что ему нужно, ведь роли у двоих все же очень разные.

{
	test: /\.css$/,
	use: [
		devMode?'style-loader':MiniCssExtractPlugin.loader,'css-loader','postcss-loader'
	]
}

Горячее обновление файла стиля (HMR)

Из приведенного выше предложения также видно, что в режиме разработки мы можем использовать MiniCssExtractPlugin для реализации стиля HMR (горячая замена модуля).

Что такое HMR файла стиля? Если HMR не настроен, в режиме разработки при изменении исходных файлов CSS страница не будет автоматически обновляться для загрузки измененных стилей. Страница должна быть обновлена ​​вручную, чтобы загрузить изменения. HMR, с другой стороны, реализует горячее обновление модифицированного модуля, так что изменения сразу отображаются на странице, и больше не нужно обновлять всю страницу.

Но по фактуstyle-loaderТакже реализует интерфейс HMR, как описано в документации Wepack.In a ModuleСказано в:

HMR is an opt-in feature that only affects modules containing HMR code. One example would be patching styling through the style-loader. In order for patching to work, the style-loader implements the HMR interface; when it receives an update through HMR, it replaces the old styles with the new ones.

Поэтому в среде разработки оба плагина могут обновлять CSS в горячем режиме, но конфигурация MiniCssExtractPlugin может быть богаче. Например: style-loader только горячо обновляет стили, представленные в JS, если они переданы в index.html.<link>Файл CSS на сервере импортируется:

<link rel="stylesheet" href="/vendors/test.css">
<!-- 通过配置 copy-webpack-plugin 在打包时把 html/vendors/test.css 拷贝到服务器根目录中,因此可以这么链接 -->

Если вы измените исходный код test.css в режиме разработки, загрузчик стилей не будет оперативно обновлять измененный CSS, а должен будет обновить всю страницу, но MiniCssExtractPlugin автоматически перезагрузит все стили. Могут быть и другие отличия, которые здесь не описаны.

Плагин MiniCssExtractPlugin может настроить HMR файлов Less следующим образом:

const devMode = process.env.NODE_ENV === 'development'; // 是否是开发模式
//......
module.exports = {
    //......
    module: {
      rules:[
        {
          test: /\.less$/i,
          use:  [
            {
              loader: MiniCssExtractPlugin.loader,
              options: {
                // 只在开发模式中启用热更新
                hmr: devMode,
                // 如果模块热更新不起作用,重新加载全部样式
                reloadAll: true,
              },
            },
            'css-loader','postcss-loader','less-loader'
          ]
        },
        // ......
      ]
    }
}

эталонное чтение