我一直在使用React 16.x.x和Jqwidgets 6.2.0和React Boilerplate 3.7.0。我的整个项目都以自己的webpack配置安装在React Boilerplate支架上。我将jqwigets-scripts
设置为npm依赖项,并尝试通过在某些github中阅读的正则表达式排除node_modules
,但包括jqwidgets-scripts/jqwidgets-react
。这是我当前的webpack.base.babel.js
文件:
...
module: {
rules: [
{
test: /\.js$/, // Transform all .js files required somewhere with Babel
exclude: /node_modules\/(?!(jqwidgets-scripts\/jqwidgets-react)\/).*/,
use: {
loader: 'babel-loader',
options: options.babelQuery,
},
},
...
在这种配置下,webpack编译时不会发出警告,也不会出现任何错误(例如超级按钮),即npm start
。如果我说npm run-script build
,那么大的事情就登台了,如果这样的话,它会抱怨:
Hash: 7b0feef0f8bbac0c9421
Version: webpack 4.27.1
Time: 41489ms
Built at: 2019-01-24 08:13:05
251 assets
Entrypoint main = runtime~main.43b100be02c6a9dbfc5a.js vendor.4d1e1f30cd3a7c98cfd8.chunk.js main.5ce13ffd96704eeb6b0d.chunk.js
ERROR in ./node_modules/jqwidgets-scripts/jqwidgets-react/react_jqxgrid.js 1216:12
Module parse failed: Unexpected token (1216:12)
You may need an appropriate loader to handle this file type.
| render() {
| return (
> <div id={this.state.id}>{this.props.value}{this.props.children}</div>
| )
| };
@ ./app/modules/MyModule_1/index.js 43:0-70 954:37-44
@ ./app/modules/MyModule_2/Loadable.js
@ ./app/modules/MyModule_3/_nav.js
@ ./app/modules/MyModule_4/index.js
@ ./app/modules/MyModule_5/Loadable.js
@ ./app/containers/MainLayout/_nav.js
@ ./app/containers/MainLayout/index.js
@ ./app/containers/MainLayout/Loadable.js
@ ./app/containers/App/index.js
@ ./app/app.js
@ multi ./node_modules/react-app-polyfill/ie11.js ./app/app.js
我尝试按照官方jqwidgets页面(https://www.jqwidgets.com/community/topic/webpack-module-parse-failed/)中的建议更改Webpack配置,如下所示:
module: {
rules: [
{
test: /\.js$/,
exclude: '/node_modules',
include: '/node_modules/jqwidgets-scripts/jqwidgets-react',
use: {
loader: 'babel-loader',
options: options.babelQuery
}
},
然后在运行npm start
时抱怨的是这样:
webpack built be60767d46a99fe48e6b in 1353ms
✖ 「wdm」:
ERROR in ./app/app.js 80:4
Module parse failed: Unexpected token (80:4)
You may need an appropriate loader to handle this file type.
| const render = messages => {
| ReactDOM.render(
> <AppContainer>
| <Provider store={store}>
| <LanguageProvider messages={messages}>
@ multi ./node_modules/react-app-polyfill/ie11.js webpack-hot-middleware/client?reload=true ./app/app.js main[2]
另外,这是我的babel.config.js
module.exports = {
presets: [
[
'@babel/preset-env',
{
modules: false,
},
],
'@babel/preset-react',
],
plugins: [
'styled-components',
'@babel/plugin-proposal-export-namespace-from',
'@babel/plugin-proposal-class-properties',
'@babel/plugin-syntax-dynamic-import',
],
env: {
production: {
only: ['app'],
plugins: [
'lodash',
'transform-react-remove-prop-types',
'@babel/plugin-transform-react-inline-elements',
'@babel/plugin-transform-react-constant-elements',
],
},
test: {
plugins: [
'@babel/plugin-transform-modules-commonjs',
'dynamic-import-node',
],
},
},
};
我的package.json
:
{
"name": "react-boilerplate",
"version": "3.7.0",
"description": "A highly scalable, offline-first foundation with the best DX and a focus on performance and best practices",
"repository": {
"type": "git",
"url": "git://github.com/react-boilerplate/react-boilerplate.git"
},
"engines": {
"npm": ">=5",
"node": ">=8.10.0"
},
"author": "Max Stoiber",
"license": "MIT",
"scripts": {
"analyze:clean": "rimraf stats.json",
"preanalyze": "npm run analyze:clean",
"analyze": "node ./internals/scripts/analyze.js",
"extract-intl": "node ./internals/scripts/extract-intl.js",
"npmcheckversion": "node ./internals/scripts/npmcheckversion.js",
"preinstall": "npm run npmcheckversion",
"prebuild": "npm run build:clean",
"build": "cross-env NODE_ENV=production webpack --config internals/webpack/webpack.prod.babel.js --color -p --progress --hide-modules --display-optimization-bailout",
"build:clean": "rimraf ./build",
"start": "cross-env NODE_ENV=development env-cmd .env node server",
"start:tunnel": "cross-env NODE_ENV=development ENABLE_TUNNEL=true node server",
"start:production": "npm run test && npm run build && npm run start:prod",
"start:prod": "cross-env NODE_ENV=production node server",
"presetup": "npm i chalk shelljs",
"setup": "node ./internals/scripts/setup.js",
"clean": "shjs ./internals/scripts/clean.js",
"clean:all": "npm run analyze:clean && npm run test:clean && npm run build:clean",
"generate": "plop --plopfile internals/generators/index.js",
"lint": "npm run lint:js",
"lint:css": "stylelint './app/**/*.js'",
"lint:eslint": "eslint --ignore-path .gitignore --ignore-pattern internals/scripts",
"lint:eslint:fix": "eslint --ignore-path .gitignore --ignore-pattern internals/scripts --fix",
"lint:js": "npm run lint:eslint -- . ",
"lint:staged": "lint-staged",
"pretest": "npm run test:clean && npm run lint",
"test:clean": "rimraf ./coverage",
"test": "cross-env NODE_ENV=test jest --coverage",
"test:watch": "cross-env NODE_ENV=test jest --watchAll",
"coveralls": "cat ./coverage/lcov.info | coveralls",
"prettify": "prettier --write"
},
"lint-staged": {
"*.js": [
"npm run lint:eslint:fix",
"git add --force"
],
"*.json": [
"prettier --write",
"git add --force"
]
},
"pre-commit": "lint:staged",
"resolutions": {
"babel-core": "7.0.0-bridge.0"
},
"dependencies": {
"@coreui/coreui": "^2.1.4",
"@coreui/coreui-plugin-chartjs-custom-tooltips": "^1.2.0",
"@coreui/icons": "^0.3.0",
"@coreui/react": "^2.1.1",
"ajv": "^6.6.1",
"babel-polyfill": "^6.26.0",
"block-ui": "^2.70.1",
"bootstrap": "^4.1.3",
"chalk": "^2.4.1",
"chart.js": "^2.7.3",
"classnames": "^2.2.6",
"compression": "^1.7.3",
"connected-react-router": "^4.5.0",
"core-js": "^2.6.0",
"env-cmd": "^8.0.2",
"exports-loader": "^0.7.0",
"flag-icon-css": "^3.2.1",
"font-awesome": "^4.7.0",
"fontfaceobserver": "2.0.13",
"history": "^4.7.2",
"hoist-non-react-statics": "3.0.1",
"immutable": "^3.8.2",
"intl": "^1.2.5",
"invariant": "^2.2.4",
"ip": "^1.1.5",
"is-url-external": "^1.0.3",
"isnumeric": "^0.3.3",
"jquery": "^3.3.1",
"jqwidgets-scripts": "^6.2.0",
"loadable-components": "^2.2.3",
"lodash": "^4.17.11",
"minimist": "1.2.0",
"moment": "^2.22.2",
"numeral": "^2.0.6",
"prop-types": "^15.6.2",
"react": "^16.6.3",
"react-chartjs-2": "^2.7.4",
"react-dom": "^16.6.3",
"react-helmet": "^5.2.0",
"react-hot-loader": "^4.6.3",
"react-intl": "^2.7.2",
"react-loadable": "^5.5.0",
"react-redux": "^5.1.1",
"react-router-dom": "^4.3.1",
"react-sizeme": "^2.5.2",
"reactstrap": "^6.5.0",
"redux": "^4.0.1",
"redux-immutable": "^4.0.0",
"redux-saga": "^0.16.2",
"reselect": "4.0.0",
"resize-sensor": "0.0.6",
"sanitize.css": "4.1.0",
"simple-line-icons": "^2.4.1",
"styled-components": "^4.1.2",
"warning": "^4.0.2"
},
"devDependencies": {
"@babel/cli": "7.1.2",
"@babel/core": "7.1.2",
"@babel/plugin-proposal-class-properties": "7.1.0",
"@babel/plugin-proposal-export-namespace-from": "^7.2.0",
"@babel/plugin-syntax-dynamic-import": "7.0.0",
"@babel/plugin-transform-modules-commonjs": "7.1.0",
"@babel/plugin-transform-react-constant-elements": "7.0.0",
"@babel/plugin-transform-react-inline-elements": "7.0.0",
"@babel/polyfill": "^7.0.0",
"@babel/preset-env": "7.1.0",
"@babel/preset-react": "7.0.0",
"@babel/register": "7.0.0",
"add-asset-html-webpack-plugin": "3.1.1",
"babel-core": "7.0.0-bridge.0",
"babel-eslint": "10.0.1",
"babel-loader": "8.0.4",
"babel-plugin-dynamic-import-node": "2.2.0",
"babel-plugin-lodash": "3.3.4",
"babel-plugin-react-intl": "3.0.1",
"babel-plugin-react-transform": "3.0.0",
"babel-plugin-styled-components": "1.8.0",
"babel-plugin-transform-react-remove-prop-types": "0.4.19",
"circular-dependency-plugin": "5.0.2",
"compare-versions": "3.4.0",
"compression-webpack-plugin": "2.0.0",
"copy-webpack-plugin": "^4.6.0",
"coveralls": "3.0.2",
"cross-env": "^5.2.0",
"css-loader": "1.0.0",
"enzyme": "3.7.0",
"enzyme-adapter-react-16": "1.6.0",
"enzyme-to-json": "3.3.4",
"eslint": "5.7.0",
"eslint-config-airbnb": "17.1.0",
"eslint-config-airbnb-base": "13.1.0",
"eslint-config-prettier": "3.1.0",
"eslint-import-resolver-webpack": "0.10.1",
"eslint-plugin-import": "^2.14.0",
"eslint-plugin-jsx-a11y": "6.1.2",
"eslint-plugin-prettier": "3.0.0",
"eslint-plugin-react": "7.11.1",
"eslint-plugin-redux-saga": "0.9.0",
"express": "^4.16.4",
"file-loader": "2.0.0",
"html-loader": "0.5.5",
"html-webpack-plugin": "3.2.0",
"http-proxy-middleware": "^0.19.1",
"image-webpack-loader": "^4.6.0",
"imports-loader": "0.8.0",
"jest-cli": "23.6.0",
"jest-styled-components": "6.2.2",
"lint-staged": "7.3.0",
"ngrok": "3.1.0",
"node-plop": "0.16.0",
"node-sass": "^4.11.0",
"null-loader": "0.1.1",
"offline-plugin": "5.0.5",
"optimize-css-assets-webpack-plugin": "^5.0.1",
"plop": "2.1.0",
"pre-commit": "1.2.2",
"prettier": "1.14.3",
"react-app-polyfill": "0.1.3",
"react-test-renderer": "16.6.0",
"rimraf": "2.6.2",
"sass-loader": "^7.1.0",
"shelljs": "^0.8.3",
"style-loader": "0.23.1",
"stylelint": "9.6.0",
"stylelint-config-recommended": "2.1.0",
"stylelint-config-styled-components": "0.1.1",
"stylelint-processor-styled-components": "1.5.0",
"svg-url-loader": "2.3.2",
"terser-webpack-plugin": "1.1.0",
"uglifyjs-webpack-plugin": "^2.0.1",
"url-loader": "1.1.2",
"webpack": "^4.27.1",
"webpack-cli": "^3.1.2",
"webpack-dev-middleware": "3.4.0",
"webpack-hot-middleware": "2.24.3 ",
"webpack-pwa-manifest": "3.7.1",
"whatwg-fetch": "3.0.0"
}
}
我的webpack.prod.babel.js
:
// Important modules this config uses
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const WebpackPwaManifest = require('webpack-pwa-manifest');
const OfflinePlugin = require('offline-plugin');
const { HashedModuleIdsPlugin } = require('webpack');
const TerserPlugin = require('terser-webpack-plugin');
const CompressionPlugin = require('compression-webpack-plugin');
module.exports = require('./webpack.base.babel')({
mode: 'production',
// In production, we skip all hot-reloading stuff
entry: [
require.resolve('react-app-polyfill/ie11'),
path.join(process.cwd(), 'app/app.js'),
],
// Utilize long-term caching by adding content hashes (not compilation hashes) to compiled assets
output: {
filename: '[name].[chunkhash].js',
chunkFilename: '[name].[chunkhash].chunk.js',
},
optimization: {
minimize: true,
minimizer: [
new TerserPlugin({
terserOptions: {
warnings: false,
compress: {
comparisons: false,
},
parse: {},
mangle: true,
output: {
comments: false,
ascii_only: true,
},
},
parallel: true,
cache: true,
sourceMap: true,
}),
],
nodeEnv: 'production',
sideEffects: true,
concatenateModules: true,
splitChunks: {
chunks: 'all',
minSize: 30000,
minChunks: 1,
maxAsyncRequests: 10,
maxInitialRequests: 3,
name: true,
cacheGroups: {
commons: {
test: /[\\/]node_modules[\\/]/,
name: 'vendor',
chunks: 'all',
},
main: {
chunks: 'all',
minChunks: 2,
reuseExistingChunk: true,
enforce: true,
},
},
},
runtimeChunk: true,
},
plugins: [
// Minify and optimize the index.html
new HtmlWebpackPlugin({
template: 'app/index.html',
minify: {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true,
},
inject: true,
}),
// Put it in the end to capture all the HtmlWebpackPlugin's
// assets manipulations and do leak its manipulations to HtmlWebpackPlugin
new OfflinePlugin({
relativePaths: false,
publicPath: '/',
appShell: '/',
// No need to cache .htaccess. See http://mxs.is/googmp,
// this is applied before any match in `caches` section
excludes: ['.htaccess'],
caches: {
main: [':rest:'],
// All chunks marked as `additional`, loaded after main section
// and do not prevent SW to install. Change to `optional` if
// do not want them to be preloaded at all (cached only when first loaded)
additional: ['*.chunk.js'],
},
// Removes warning for about `additional` section usage
safeToUseOptionalCaches: true,
}),
new CompressionPlugin({
algorithm: 'gzip',
test: /\.js$|\.css$|\.html$/,
threshold: 10240,
minRatio: 0.8,
}),
new WebpackPwaManifest({
name: 'React Boilerplate',
short_name: 'React BP',
description: 'My React Boilerplate-based project!',
background_color: '#fafafa',
theme_color: '#b1624d',
inject: true,
ios: true,
icons: [
{
src: path.resolve('app/images/icon-512x512.png'),
sizes: [72, 96, 128, 144, 192, 384, 512],
},
{
src: path.resolve('app/images/icon-512x512.png'),
sizes: [120, 152, 167, 180],
ios: true,
},
],
}),
new HashedModuleIdsPlugin({
hashFunction: 'sha256',
hashDigest: 'hex',
hashDigestLength: 20,
}),
],
performance: {
assetFilter: assetFilename =>
!/(\.map$)|(^(main\.|favicon\.))/.test(assetFilename),
},
});
我的webpack.dev.babel.js
:
/**
* DEVELOPMENT WEBPACK CONFIGURATION
*/
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CircularDependencyPlugin = require('circular-dependency-plugin');
module.exports = require('./webpack.base.babel')({
mode: 'development',
// Add hot reloading in development
entry: [
require.resolve('react-app-polyfill/ie11'),
'webpack-hot-middleware/client?reload=true',
path.join(process.cwd(), 'app/app.js'), // Start with js/app.js
],
// Don't use hashes in dev mode for better performance
output: {
filename: '[name].js',
chunkFilename: '[name].chunk.js',
},
optimization: {
splitChunks: {
chunks: 'all',
},
},
// Add development plugins
plugins: [
new webpack.HotModuleReplacementPlugin(), // Tell webpack we want hot reloading
new HtmlWebpackPlugin({
inject: true, // Inject all files that are generated by webpack, e.g. bundle.js
template: 'app/index.html',
}),
new CircularDependencyPlugin({
exclude: /a\.js|node_modules/, // exclude node_modules
failOnError: false, // show a warning when there is a circular dependency
}),
],
// Emit a source map for easier debugging
// See https://webpack.js.org/configuration/devtool/#devtool
devtool: 'eval-source-map',
performance: {
hints: false,
},
});
最后是我的webpack.base.babel.js
:
/**
* COMMON WEBPACK CONFIGURATION
*/
const path = require('path');
const webpack = require('webpack');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const config = require('../../server/config');
// Remove this line once the following warning goes away (it was meant for webpack loader authors not users):
// 'DeprecationWarning: loaderUtils.parseQuery() received a non-string value which can be problematic,
// see https://github.com/webpack/loader-utils/issues/56 parseQuery() will be replaced with getOptions()
// in the next major version of loader-utils.'
process.noDeprecation = true;
module.exports = options => ({
mode: options.mode,
entry: options.entry,
output: Object.assign(
{
// Compile into js/build.js
path: path.resolve(process.cwd(), 'build'),
publicPath: '/',
},
options.output,
), // Merge with env dependent settings
optimization: options.optimization,
module: {
rules: [
{
test: /\.js$|\.jsx$/, // Transform all .js files required somewhere with Babel
exclude: /node_modules\/(?!(jqwidgets-scripts\/jqwidgets-react)\/).*/,
use: {
loader: 'babel-loader',
options: options.babelQuery,
},
},
// {
// test: /\.js$|\.jsx$/,
// exclude: '/node_modules',
// include: '/jqwidgets-scripts/jqwidgets-react',
// use: {
// loader: 'babel-loader',
// options: options.babelQuery
// }
// },
{
// Preprocess our own .css files
// This is the place to add your own loaders (e.g. sass/less etc.)
// for a list of loaders, see https://webpack.js.org/loaders/#styling
test: /\.scss$/,
exclude: /node_modules/,
use: ['style-loader', 'css-loader', 'sass-loader'],
},
{
// Preprocess 3rd party .css files located in node_modules
test: /\.css$/,
include: /node_modules/,
use: ['style-loader', 'css-loader'],
},
{
test: /\.(eot|otf|ttf|woff|woff2)$/,
use: 'file-loader',
},
{
test: /\.svg$/,
use: [
{
loader: 'svg-url-loader',
options: {
// Inline files smaller than 10 kB
limit: 10 * 1024,
noquotes: true,
},
},
],
},
{
test: /\.(jpg|png|gif)$/,
use: [
{
loader: 'url-loader',
options: {
// Inline files smaller than 10 kB
limit: 10 * 1024,
},
},
{
loader: 'image-webpack-loader',
options: {
mozjpeg: {
enabled: false,
// NOTE: mozjpeg is disabled as it causes errors in some Linux environments
// Try enabling it in your environment by switching the config to:
// enabled: true,
// progressive: true,
},
gifsicle: {
interlaced: false,
},
optipng: {
optimizationLevel: 7,
},
pngquant: {
quality: '65-90',
speed: 4,
},
},
},
],
},
{
test: /\.html$/,
use: 'html-loader',
},
{
test: /\.(mp4|webm)$/,
use: {
loader: 'url-loader',
options: {
limit: 10000,
},
},
},
],
},
plugins: options.plugins.concat([
// Always expose NODE_ENV to webpack, in order to use `process.env.NODE_ENV`
// inside your code for any environment checks; Terser will automatically
// drop any unreachable code.
/* new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
}), */
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify(config.env),
HOST: JSON.stringify(config.server_host),
PORT: JSON.stringify(config.server_port)
},
}),
new webpack.NamedModulesPlugin(),
new CopyWebpackPlugin([{ from: 'static' }]),
]),
resolve: {
modules: ['node_modules', 'app'],
extensions: ['.js', '.jsx', '.react.js'],
mainFields: ['browser', 'jsnext:main', 'main'],
alias: {
moment$: 'moment/moment.js',
},
},
devtool: options.devtool,
target: 'web', // Make web variables accessible to webpack, e.g. window
performance: options.performance || {},
});
我的问题是,如何配置webpack以便在生产模式下正常工作?