Webpack、React、postCSS 和其他可能正确注入的 JS 代码存在产品构建问题
Having a prod build issue with Webpack, React, postCSS, and possibly other JS code being injected properly
我的 webpack.prod.config 正确构建我的资产时遇到问题,或者我的 JS Babel 配置可能有问题。
我可以让它与内联我的 CSS 的开发版本一起工作,但是当我试图将它合并到一个 CSS 文件中进行生产时它不起作用。无论发生什么,它都可以在 Dev 中工作,CSS 通过 import './filename.css' 直接注入到每个相应的组件中。它也可能是 JS,但无论哪种方式,当我为产品构建时,CSS 都无法正常工作,JS 也无法正常工作。不会显示所有 React JS 组件和其他 JS,仅显示来自 CDN 导入的静态 HTML 和 CSS 样式。当我单击由 Webpack 注入的脚本标签中的 URL 时,它们只会将我带到同一页面,而不是我觉得奇怪的 JS 或 CSS 源。 JS 和 CSS 在 Build > Static > JS + CSS 输出中看起来是正确的。有时我收到一个 text/html MIME,而它应该是控制台中的其他消息。
要么是那个,要么是我的 JS 坏了,没有正确构建页面。不在我的生产构建中(与部署到 Heroku 时的构建相同)。我从一个 create-react-app(弹出)开始,添加了 Express、preCSS(用于像预处理这样的 SASS)、react-bootstrap 和其他一些东西。
这个项目有点乱,因为它是我用作新 Web 开发人员从使用静态 HTML+CSS 过渡到使用 React、JS 和Bootstrap(jQuery 暂时在那里,因为我们将东西一起转换成纯 React)。它以前构建没有问题,但自从我开始使用 postCSS + preCSS,但它不再工作了。
以下是我正在使用的一些主要 packages/libraries。
- jQuery(CDN 脚本标签)
- BS3(CDN 脚本标签、.js、.css)
- 反应-Bootstrap
- 反应覆盖
- 通天塔
- postCSS
- 前CSS
- ExtractTextPlugin
预先感谢您的帮助。
HTML + 我进入控制台时出错
[![HTML正在生成][1]][1]
[![控制台错误][2]][2]
Webpack.config.prod.js
var path = require('path');
var precss = require('precss');
var autoprefixer = require('autoprefixer');
var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var url = require('url');
var paths = require('./paths');
var homepagePath = require(paths.appPackageJson).homepage;
var publicPath = homepagePath ? url.parse(homepagePath).pathname : '/';
if (!publicPath.endsWith('/')) {
// Prevents incorrect paths in file-loader
publicPath += '/';
}
module.exports = {
bail: true,
devtool: 'source-map',
entry: [
require.resolve('./polyfills'),
path.join(paths.appSrc, 'index')
],
output: {
path: paths.appBuild,
filename: 'static/js/[name].[chunkhash:8].js',
chunkFilename: 'static/js/[name].[chunkhash:8].chunk.js',
publicPath: publicPath
},
resolve: {
extensions: ['', '.js', '.json']
},
resolveLoader: {
root: paths.ownNodeModules,
moduleTemplates: ['*-loader']
},
module: {
preLoaders: [
{
test: /\.js$/,
loader: 'eslint',
include: paths.appSrc
}
],
loaders: [
{
test: /\.js$/,
include: paths.appSrc,
loader: 'babel',
query: require('./babel.prod')
},
{
test: /\.css$/,
include: [paths.appSrc, paths.appNodeModules],
// Disable autoprefixer in css-loader itself:
// https://github.com/webpack/css-loader/issues/281
// We already have it thanks to postcss.
loader: ExtractTextPlugin.extract('style', 'css?-autoprefixer!postcss!sass')
},
{
test: /\.json$/,
include: [paths.appSrc, paths.appNodeModules],
loader: 'json'
},
{
test: /\.(jpg|png|gif|eot|svg|ttf|woff|woff2)(\?.*)?$/,
include: [paths.appSrc, paths.appNodeModules],
loader: 'file',
query: {
// name: 'static/media/[name].[hash:8].[ext]'
name: 'static/media/[name].[ext]'
}
},
{
test: /\.(mp4|webm)(\?.*)?$/,
include: [paths.appSrc, paths.appNodeModules],
loader: 'url',
query: {
limit: 10000,
name: 'static/media/[name].[hash:8].[ext]'
}
}
]
},
eslint: {
// TODO: consider separate config for production,
// e.g. to enable no-console and no-debugger only in prod.
configFile: path.join(__dirname, 'eslint.js'),
useEslintrc: false
},
postcss: function() {
return [precss, autoprefixer];
},
plugins: [
new HtmlWebpackPlugin({
inject: true,
template: paths.appHtml,
favicon: paths.appFavicon,
minify: {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true
}
}),
new webpack.DefinePlugin({ 'process.env.NODE_ENV': '"production"' }),
new webpack.optimize.OccurrenceOrderPlugin(),
new webpack.optimize.DedupePlugin(),
new webpack.optimize.UglifyJsPlugin({
compress: {
screw_ie8: true,
warnings: false
},
mangle: {
screw_ie8: true
},
output: {
comments: false,
screw_ie8: true
}
}),
new ExtractTextPlugin('static/css/[name].[contenthash:8].css')
]
};
Build.js
process.env.NODE_ENV = 'production';
var chalk = require('chalk');
var fs = require('fs');
var path = require('path');
var filesize = require('filesize');
var gzipSize = require('gzip-size').sync;
var rimrafSync = require('rimraf').sync;
var webpack = require('webpack');
var config = require('../config/webpack.config.prod');
var paths = require('../config/paths');
var express = require('express');
var app = express();
// Remove all content but keep the directory so that
// if you're in it, you don't end up in Trash
rimrafSync(paths.appBuild + '/*');
console.log('Creating an optimized production build...');
webpack(config).run(function(err, stats) {
if (err) {
console.error('Failed to create a production build. Reason:');
console.error(err.message || err);
process.exit(1);
}
console.log(chalk.green('Compiled successfully.'));
console.log();
console.log('File sizes after gzip:');
console.log();
var assets = stats.toJson().assets
.filter(asset => /\.(js|css)$/.test(asset.name))
.map(asset => {
var fileContents = fs.readFileSync(paths.appBuild + '/' + asset.name);
var size = gzipSize(fileContents);
return {
folder: path.join('build', path.dirname(asset.name)),
name: path.basename(asset.name),
size: size,
sizeLabel: filesize(size)
};
});
assets.sort((a, b) => b.size - a.size);
var longestSizeLabelLength = Math.max.apply(null,
assets.map(a => a.sizeLabel.length)
);
assets.forEach(asset => {
var sizeLabel = asset.sizeLabel;
if (sizeLabel.length < longestSizeLabelLength) {
var rightPadding = ' '.repeat(longestSizeLabelLength - sizeLabel.length);
sizeLabel += rightPadding;
}
console.log(
' ' + chalk.green(sizeLabel) +
' ' + chalk.dim(asset.folder + path.sep) + chalk.cyan(asset.name)
);
});
console.log();
if (process.env.NODE_ENV === 'production') {
// Serve the static HTML file from paths.appBuild directory
app.use(express.static(paths.appBuild));
console.log('Static build directory now being served, paths.appBuild: ', paths.appBuild);
// Serve the static HTML file from express
console.log('Adding static path to Express routing...');
app.get('*', function(req, res) {
res.sendFile(paths.appHtml);
console.log('Path serving HTML at paths.appHTML: ', paths.appHtml);
});
// List out which port is being used and listen for changes on the server
app.listen(process.env.PORT || 9004, function(){
console.log('Express server listening on port %d in %s mode', (process.env.PORT || 9004), app.settings.env);
});
}
console.log();
});
在 webpack.config.prod.js 文件中,输出:publicPath: value 被设置为变量 publicPath
,它是从 create-react-app 中弹出的代码的一部分。我在没有完全理解 publicPath 变量将解析为什么的情况下重复使用了它。当我需要的是 '/'
的相对路径时,它吐出了 github-username/repo-name 的路径。
我没有发现在 index.html 中注入的 css + js 标签是不正确的路径,因为控制台中报告的错误仅表示语法错误,所以它必须是CSS 和 JS 没有加载到页面中。我更正了使用“/”作为所有静态资产所在和提供的 paths.appBuild 文件夹的相对路径的路径。
这个堆栈 post 也有帮助:Webpack publicPath
// variables from the create-react-app eject, will be removing these from my webpack.config.prod.js file
var homepagePath = require(paths.appPackageJson).homepage;
var publicPath = homepagePath ? url.parse(homepagePath).pathname : '/';
if (!publicPath.endsWith('/')) {
// Prevents incorrect paths in file-loader
publicPath += '/';
}
console.log('homepagePath: ', homepagePath); // Resolves to git repo url
console.log('publicPath: ', publicPath); // Resolves to /retrospct/tellus-aerospace/
module.exports = {
bail: true,
devtool: 'source-map',
entry: [
require.resolve('./polyfills'),
path.join(paths.appSrc, 'index')
],
output: {
path: paths.appBuild,
filename: 'static/js/[name].[chunkhash:8].js',
chunkFilename: 'static/js/[name].[chunkhash:8].chunk.js',
publicPath: '/' // Previously this was the variable publicPath set above
},
我的 webpack.prod.config 正确构建我的资产时遇到问题,或者我的 JS Babel 配置可能有问题。
我可以让它与内联我的 CSS 的开发版本一起工作,但是当我试图将它合并到一个 CSS 文件中进行生产时它不起作用。无论发生什么,它都可以在 Dev 中工作,CSS 通过 import './filename.css' 直接注入到每个相应的组件中。它也可能是 JS,但无论哪种方式,当我为产品构建时,CSS 都无法正常工作,JS 也无法正常工作。不会显示所有 React JS 组件和其他 JS,仅显示来自 CDN 导入的静态 HTML 和 CSS 样式。当我单击由 Webpack 注入的脚本标签中的 URL 时,它们只会将我带到同一页面,而不是我觉得奇怪的 JS 或 CSS 源。 JS 和 CSS 在 Build > Static > JS + CSS 输出中看起来是正确的。有时我收到一个 text/html MIME,而它应该是控制台中的其他消息。
要么是那个,要么是我的 JS 坏了,没有正确构建页面。不在我的生产构建中(与部署到 Heroku 时的构建相同)。我从一个 create-react-app(弹出)开始,添加了 Express、preCSS(用于像预处理这样的 SASS)、react-bootstrap 和其他一些东西。
这个项目有点乱,因为它是我用作新 Web 开发人员从使用静态 HTML+CSS 过渡到使用 React、JS 和Bootstrap(jQuery 暂时在那里,因为我们将东西一起转换成纯 React)。它以前构建没有问题,但自从我开始使用 postCSS + preCSS,但它不再工作了。
以下是我正在使用的一些主要 packages/libraries。 - jQuery(CDN 脚本标签) - BS3(CDN 脚本标签、.js、.css) - 反应-Bootstrap - 反应覆盖 - 通天塔 - postCSS - 前CSS - ExtractTextPlugin
预先感谢您的帮助。
HTML + 我进入控制台时出错
[![HTML正在生成][1]][1] [![控制台错误][2]][2]
Webpack.config.prod.js
var path = require('path');
var precss = require('precss');
var autoprefixer = require('autoprefixer');
var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var url = require('url');
var paths = require('./paths');
var homepagePath = require(paths.appPackageJson).homepage;
var publicPath = homepagePath ? url.parse(homepagePath).pathname : '/';
if (!publicPath.endsWith('/')) {
// Prevents incorrect paths in file-loader
publicPath += '/';
}
module.exports = {
bail: true,
devtool: 'source-map',
entry: [
require.resolve('./polyfills'),
path.join(paths.appSrc, 'index')
],
output: {
path: paths.appBuild,
filename: 'static/js/[name].[chunkhash:8].js',
chunkFilename: 'static/js/[name].[chunkhash:8].chunk.js',
publicPath: publicPath
},
resolve: {
extensions: ['', '.js', '.json']
},
resolveLoader: {
root: paths.ownNodeModules,
moduleTemplates: ['*-loader']
},
module: {
preLoaders: [
{
test: /\.js$/,
loader: 'eslint',
include: paths.appSrc
}
],
loaders: [
{
test: /\.js$/,
include: paths.appSrc,
loader: 'babel',
query: require('./babel.prod')
},
{
test: /\.css$/,
include: [paths.appSrc, paths.appNodeModules],
// Disable autoprefixer in css-loader itself:
// https://github.com/webpack/css-loader/issues/281
// We already have it thanks to postcss.
loader: ExtractTextPlugin.extract('style', 'css?-autoprefixer!postcss!sass')
},
{
test: /\.json$/,
include: [paths.appSrc, paths.appNodeModules],
loader: 'json'
},
{
test: /\.(jpg|png|gif|eot|svg|ttf|woff|woff2)(\?.*)?$/,
include: [paths.appSrc, paths.appNodeModules],
loader: 'file',
query: {
// name: 'static/media/[name].[hash:8].[ext]'
name: 'static/media/[name].[ext]'
}
},
{
test: /\.(mp4|webm)(\?.*)?$/,
include: [paths.appSrc, paths.appNodeModules],
loader: 'url',
query: {
limit: 10000,
name: 'static/media/[name].[hash:8].[ext]'
}
}
]
},
eslint: {
// TODO: consider separate config for production,
// e.g. to enable no-console and no-debugger only in prod.
configFile: path.join(__dirname, 'eslint.js'),
useEslintrc: false
},
postcss: function() {
return [precss, autoprefixer];
},
plugins: [
new HtmlWebpackPlugin({
inject: true,
template: paths.appHtml,
favicon: paths.appFavicon,
minify: {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true
}
}),
new webpack.DefinePlugin({ 'process.env.NODE_ENV': '"production"' }),
new webpack.optimize.OccurrenceOrderPlugin(),
new webpack.optimize.DedupePlugin(),
new webpack.optimize.UglifyJsPlugin({
compress: {
screw_ie8: true,
warnings: false
},
mangle: {
screw_ie8: true
},
output: {
comments: false,
screw_ie8: true
}
}),
new ExtractTextPlugin('static/css/[name].[contenthash:8].css')
]
};
Build.js
process.env.NODE_ENV = 'production';
var chalk = require('chalk');
var fs = require('fs');
var path = require('path');
var filesize = require('filesize');
var gzipSize = require('gzip-size').sync;
var rimrafSync = require('rimraf').sync;
var webpack = require('webpack');
var config = require('../config/webpack.config.prod');
var paths = require('../config/paths');
var express = require('express');
var app = express();
// Remove all content but keep the directory so that
// if you're in it, you don't end up in Trash
rimrafSync(paths.appBuild + '/*');
console.log('Creating an optimized production build...');
webpack(config).run(function(err, stats) {
if (err) {
console.error('Failed to create a production build. Reason:');
console.error(err.message || err);
process.exit(1);
}
console.log(chalk.green('Compiled successfully.'));
console.log();
console.log('File sizes after gzip:');
console.log();
var assets = stats.toJson().assets
.filter(asset => /\.(js|css)$/.test(asset.name))
.map(asset => {
var fileContents = fs.readFileSync(paths.appBuild + '/' + asset.name);
var size = gzipSize(fileContents);
return {
folder: path.join('build', path.dirname(asset.name)),
name: path.basename(asset.name),
size: size,
sizeLabel: filesize(size)
};
});
assets.sort((a, b) => b.size - a.size);
var longestSizeLabelLength = Math.max.apply(null,
assets.map(a => a.sizeLabel.length)
);
assets.forEach(asset => {
var sizeLabel = asset.sizeLabel;
if (sizeLabel.length < longestSizeLabelLength) {
var rightPadding = ' '.repeat(longestSizeLabelLength - sizeLabel.length);
sizeLabel += rightPadding;
}
console.log(
' ' + chalk.green(sizeLabel) +
' ' + chalk.dim(asset.folder + path.sep) + chalk.cyan(asset.name)
);
});
console.log();
if (process.env.NODE_ENV === 'production') {
// Serve the static HTML file from paths.appBuild directory
app.use(express.static(paths.appBuild));
console.log('Static build directory now being served, paths.appBuild: ', paths.appBuild);
// Serve the static HTML file from express
console.log('Adding static path to Express routing...');
app.get('*', function(req, res) {
res.sendFile(paths.appHtml);
console.log('Path serving HTML at paths.appHTML: ', paths.appHtml);
});
// List out which port is being used and listen for changes on the server
app.listen(process.env.PORT || 9004, function(){
console.log('Express server listening on port %d in %s mode', (process.env.PORT || 9004), app.settings.env);
});
}
console.log();
});
在 webpack.config.prod.js 文件中,输出:publicPath: value 被设置为变量 publicPath
,它是从 create-react-app 中弹出的代码的一部分。我在没有完全理解 publicPath 变量将解析为什么的情况下重复使用了它。当我需要的是 '/'
的相对路径时,它吐出了 github-username/repo-name 的路径。
我没有发现在 index.html 中注入的 css + js 标签是不正确的路径,因为控制台中报告的错误仅表示语法错误,所以它必须是CSS 和 JS 没有加载到页面中。我更正了使用“/”作为所有静态资产所在和提供的 paths.appBuild 文件夹的相对路径的路径。
这个堆栈 post 也有帮助:Webpack publicPath
// variables from the create-react-app eject, will be removing these from my webpack.config.prod.js file
var homepagePath = require(paths.appPackageJson).homepage;
var publicPath = homepagePath ? url.parse(homepagePath).pathname : '/';
if (!publicPath.endsWith('/')) {
// Prevents incorrect paths in file-loader
publicPath += '/';
}
console.log('homepagePath: ', homepagePath); // Resolves to git repo url
console.log('publicPath: ', publicPath); // Resolves to /retrospct/tellus-aerospace/
module.exports = {
bail: true,
devtool: 'source-map',
entry: [
require.resolve('./polyfills'),
path.join(paths.appSrc, 'index')
],
output: {
path: paths.appBuild,
filename: 'static/js/[name].[chunkhash:8].js',
chunkFilename: 'static/js/[name].[chunkhash:8].chunk.js',
publicPath: '/' // Previously this was the variable publicPath set above
},