#reactjs #webpack #sass
#reactjs #webpack #sass
Вопрос:
Моя конфигурация webpack в основном работает, но в производственных сборках не применяется стиль css.
Если я запускаю webpack с помощью этого скрипта узла webpack --mode development
, а затем открываю ./dist/index.html
файл в своем браузере, веб-сайт работает должным образом, и содержимое оформлено правильно.
Если я запускаю webpack с помощью этого скрипта узла webpack --mode production
, а затем открываю ./dist/index.html
файл в своем браузере, появляется содержимое веб-сайта, но к содержимому вообще не применяется стиль.
Сервер разработки webpack отлично работает со стилизованным контентом, текущими обновлениями и т. Д.
Я изменил свой webpack.config.js
, чтобы закомментировать настройки, специфичные для производства, чтобы в производственных сборках и сборках для разработки использовалась одна и та же конфигурация webpack. Мне трудно объяснить, почему сборка разработки работает, но производственная сборка не работает даже с идентичной конфигурацией. Есть идеи о том, как устранить проблему?
Мой webpack.config.js
выглядит так:
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = (env, { mode }) => {
var config = {
entry: {
app: './src/app.js',
react: 'react',
},
devtool: 'none',
devServer: {
contentBase: path.join(__dirname, 'dist'),
compress: true,
hot: true,
port: 9000,
},
resolve: {
extensions: ['.js', '.jsx', '.css', '.scss', '.sass'],
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'index.[hash].js',
chunkFilename: '[name].[contenthash].js',
},
optimization: {
runtimeChunk: 'single',
splitChunks: {
cacheGroups: {
vendor: {
test: /[\/]node_modules[\/]/,
name: 'vendors',
chunks: 'all',
},
},
},
},
plugins: [
new HtmlWebpackPlugin({
minify: false,
template: require('html-webpack-template'),
inject: true,
title: 'Octopedia',
favicon: './public/images/favicon/favicon.ico',
appMountId: 'app',
meta: [{ name: 'viewport', content: 'width=device-width, initial-scale=1.0' }],
links: [{ href: '/site.webmanifest', rel: 'manifest' }],
appMountHtmlSnippet: '<div class="app-spinner"></div>',
headHtmlSnippet:
'<style>div.app-spinner{position:fixed;top:50%;left:50%;border:16px solid #f3f3f3;border-top:16px solid #3498db;border-radius:50%;width:120px;height:120px;margin:-60px 0 0 -60px;z-index:1;animation:spin 2s linear infinite;}@keyframes spin{0%{transform:rotate(0deg);}100%{transform:rotate(360deg);}}</style >',
}),
new CopyWebpackPlugin({
patterns: [{ from: 'public' }],
}),
],
module: {
rules: [
{
test: /.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
cacheDirectory: true,
cacheCompression: false,
presets: ['@babel/preset-env', '@babel/preset-react'],
},
},
},
],
},
};
// if (mode === 'development') {
config.mode = 'development';
config.devtool = 'eval-source-map';
config.plugins.push(new webpack.HotModuleReplacementPlugin());
config.module.rules.push({
test: /.(sass|scss|css)/,
exclude: /node_modules/,
use: ['style-loader', 'css-loader', 'sass-loader'],
});
// } else {
// config.mode = 'production';
// config.devtool = 'none';
// config.plugins.push(new MiniCssExtractPlugin({ filename: 'index.css' }));
// config.module.rules.push({
// test: /.(sass|scss|css)/,
// exclude: /node_modules/,
// use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader'],
// });
// }
return config;
};
Вывод из webpack в режиме разработки выглядит следующим образом:
Asset Size Chunks Chunk Names
app.6335bee170373d18f82e.js 39.8 KiB app [emitted] [immutable] app
favicon.ico 15 KiB [emitted]
images/.DS_Store 6 KiB [emitted]
images/favicon/.DS_Store 6 KiB [emitted]
images/favicon/android-chrome-192x192.png 8.1 KiB [emitted]
images/favicon/android-chrome-512x512.png 23.3 KiB [emitted]
images/favicon/apple-touch-icon.png 7.25 KiB [emitted]
images/favicon/favicon-16x16.png 534 bytes [emitted]
images/favicon/favicon-32x32.png 1.06 KiB [emitted]
images/favicon/favicon.ico 15 KiB [emitted]
index.87da9b99395c61fa6b02.js 33.4 KiB runtime [emitted] [immutable] runtime
index.html 1.04 KiB [emitted]
react.a0fc75f932f5dd9cdda1.js 133 bytes react [emitted] [immutable] react
site.webmanifest 447 bytes [emitted]
vendors.404f5d4ed29b35b04b91.js 2.41 MiB vendors [emitted] [immutable] vendors
Entrypoint app = index.87da9b99395c61fa6b02.js vendors.404f5d4ed29b35b04b91.js app.6335bee170373d18f82e.js
Entrypoint react = index.87da9b99395c61fa6b02.js vendors.404f5d4ed29b35b04b91.js react.a0fc75f932f5dd9cdda1.js
Вывод из webpack в рабочем режиме выглядит следующим образом:
Asset Size Chunks Chunk Names
app.170943a8008bc467199f.js 2.77 KiB 2 [emitted] [immutable] app
favicon.ico 15 KiB [emitted]
images/.DS_Store 6 KiB [emitted]
images/favicon/.DS_Store 6 KiB [emitted]
images/favicon/android-chrome-192x192.png 8.1 KiB [emitted]
images/favicon/android-chrome-512x512.png 23.3 KiB [emitted]
images/favicon/apple-touch-icon.png 7.25 KiB [emitted]
images/favicon/favicon-16x16.png 534 bytes [emitted]
images/favicon/favicon-32x32.png 1.06 KiB [emitted]
images/favicon/favicon.ico 15 KiB [emitted]
index.a162f7e38dd204127c0f.js 9.03 KiB 0 [emitted] [immutable] runtime
index.html 1.04 KiB [emitted]
react.f87e0cf2654ac4f74576.js 71 bytes 3 [emitted] [immutable] react
site.webmanifest 447 bytes [emitted]
vendors.dd9d4a26464d8d880c74.js 352 KiB 1 [emitted] [immutable] [big] vendors
Комментарии:
1. Я только что обнаружил, что могу сделать ‘webpack —config webpack.config.js —производство режима —профиль —json > компиляция-stats.json’. Сгенерированный файл compilation-stats.json не содержит ошибок и много ссылок на файлы scss. Ничего не выскакивает.
2. Из прочитанного, которое я сделал сегодня, я думаю, что это может быть связано с дрожанием дерева и побочными эффектами модуля. Поскольку ничто напрямую не ссылается ни на что внутри css, возможно, webpack удаляет его в качестве оптимизации.
Ответ №1:
Поскольку вы используете ту же конфигурацию, вам необходимо дополнительно добавить MiniCssExtractPlugin:
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const { NODE_ENV } = process.env;
const inDevelopment = NODE_ENV === "development";
module.exports = {
...options,
module: {
rules: [
...rules,
{
test: /.(scss|css)/,
exclude: /node_modules/,
use: [
inDevelopment ? "style-loader" : MiniCssExtractPlugin.loader,
'css-loader',
'sass-loader'
],
},
],
},
plugins: [
...plugins,
!inDevelopment amp;amp; new MiniCssExtractPlugin({ filename: "bundle.min.css" }),
].filter(Boolean)
}
Короче говоря, если вы находитесь в разработке, он будет использоваться style-loader
. Когда вы работаете, вы будете использовать MiniCssExtractPlugin.loader
с MiniCssExtractPlugin
плагином. Вышесказанное предполагает, что вы захотите объединить в один файл css. Если вы хотите разделить его на код, вам нужно добавить optimization.splitChunks и обновить плагин для разбиения на фрагменты.
Комментарии:
1. Я боролся с этой проблемой весь день и перепробовал много вещей, включая MiniCssExtractPlugin . В вашем примере определено имя файла для плагина, которое я раньше не пробовал, поэтому я попробовал еще раз, но это не имеет никакого значения. По-прежнему нет файла css, записанного в выходные данные, и нет ссылок css, вставленных в шаблон html.
2. Вот репозиторий , который вы можете использовать в качестве ссылки. Вы можете клонировать его и запускать локально, если хотите. Сосредоточьтесь на
config
папке иwebpack.config.js
файле. Я разделил конфигурацию, чтобы ее было легче читать, и включил массу заметок.3. Спасибо, Мэтт. Я проверю это сегодня днем. Я также обновил свой вопрос своим последним кодом и результатами. В частности, если я создаю с
--mode development
помощью, содержимое./dist
папки будет работать так, как ожидалось, так что это не разница между сервером разработки, как я сначала подумал.4. Dev-сервер должен использоваться только для разработки :
"dev": "webpack-dev-server"
. При сборке для производства вы должны использовать только webpack:"build": "webpack -p"
(-p
сообщает webpack, что он создается для производства).5. Спасибо, Мэтт, я это уже знаю и использую только dev server для разработки. Чтобы помочь сузить проблему, я попытался выполнить обычную сборку (не на сервере разработки),
--mode development
и теперь вывод в./dist
папке работает.
Ответ №2:
После нескольких дней ударов головой о кирпичную стену документации я, наконец, обнаружил проблему. Я считаю это ошибкой в webpack, но другие могут не согласиться.
Проблема возникает из-за того, что в «производственном» режиме webpack выполняет оптимизацию, которая удаляет код без ссылок. Это, конечно, хорошо, но оно не различает javascript и другие типы файлов, что для меня является ошибкой.
Конечным результатом является то, что, поскольку ни один из моих Javascript не ссылается на что-либо, экспортируемое из css (как это могло случиться, css не имеет механизма импорта / экспорта ES6), webpack tree shaker удаляет весь css из производственной сборки.
Решение очень простое, но его очень сложно найти. В вашем package.json
файле вам нужно добавить:
"sideEffects": ["*.css", "*.scss", "*.sass"],
Который сообщает webpack, что он не может удалить эти модули из дерева, потому что достаточно просто загрузить модуль, чтобы модуль обеспечивал функциональность без каких-либо ссылок в модуле, на которые ссылается приложение.
Потребовалось три дня экспериментов и чтения документов и статей stackoverflow, чтобы отследить это простое изменение в 1 строку. Я надеюсь, что этот ответ может избавить кого-то другого от траты того же времени.
Комментарии:
1. Пожалуйста, опубликуйте репозиторий Github, когда сможете, потому что я думаю, что вы все равно столкнетесь с проблемами при переносе его в производство.
2. Теперь
./dist
папка содержит именно то, что я хочу. Какие проблемы вы ожидаете в производстве?3. Если вы выводите,
--mode development
значит, вы используете неоптимизированные ресурсы в производстве. Если вы предотвращаете встряхивание дерева в процессе производства (используяsideEffects
для CSS), то без пакета, передающего импорт CSS, он не сможет загрузить ваши компоненты / ресурсы. Обычной практикой является разделение импорта CSS из JS, чтобы их можно было импортировать отдельно (CSS в DOMhead
и JS в DOMbody
). Если вы хотите объединить их в единое целое, я бы предложилCSS-in-JSS
решение (например@emotion
,styled-components
, илиstyled-jsx
)4. Еще одна причина отделить CSS от JS — это если вы хотите использовать CDN или создать библиотеку пользовательского интерфейса NPM. Вместо того, чтобы заставлять разработчика использовать пакет, они могут просто импортировать скомпилированный JS и CSS отдельно. Таким образом, они не заблокированы
webpack/gulp/rollup
и не нуждаются вcss-loader/style-loader/sass
etc для обработки некомпилированных таблиц стилей.5. Я не использую
--mode development
для производственных сборок, вы неправильно поняли мой комментарий. Я сказал, что я пробовал--mode development
в качестве эксперимента, и именно результат этого эксперимента в конечном итоге привел меня к решению. Никто в здравом уме не будет развертывать сборку разработки для производства.