我已将 Webpack v4 迁移到 v5,对 CLI 中的文档和弃用消息进行了相关更改,并成功构建,但在应用程序测试期间我注意到 JavaScript 无法运行并且没有错误。我得到了由 SSR 渲染的纯 html。
不知道为什么它不起作用,因为它很安静,可能是配置错误问题。
这是我的 webpack 配置:
const webpack = require('webpack');
const path = require('path');
const glob = require('glob');
// webpack plugins
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const AssetsPlugin = require('assets-webpack-plugin');
// minification plugins
const TerserJSPlugin = require('terser-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
// image optimization plugins
const ImageminPlugin = require("imagemin-webpack-plugin").default;
const imageminGifsicle = require("imagemin-gifsicle");
const imageminPngquant = require("imagemin-pngquant");
const imageminSvgo = require("imagemin-svgo");
const imageminMozjpeg = require('imagemin-mozjpeg');
const ProgressPlugin = webpack.ProgressPlugin;
const env = require('dotenv').config();
const isProd = process.env.NODE_ENV === 'production';
const isDev = !isProd;
const environment = {
NODE_ENV: process.env.NODE_ENV || 'development',
CONFIG: process.env.CONFIG || 'development',
DEBUG: process.env.DEBUG || false,
};
const plugins = () => {
const plugins = [
new CleanWebpackPlugin({
verbose: true,
cleanStaleWebpackAssets: true,
}),
new webpack.EnvironmentPlugin(Object.assign(environment, env.parsed)),
new MiniCssExtractPlugin({
filename: '[contenthash:5].[name].css',
chunkFilename: '[chunkhash:5].[name].css',
}),
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
moment: 'moment',
_: 'lodash',
}),
new webpack.ContextReplacementPlugin(/moment[\\\/]locale$/, /^\.\/(en)$/),
new AssetsPlugin({
filename: 'assets.json',
}),
new ImageminPlugin({
cacheFolder: isDev ? path.resolve(__dirname, '../dist/cachedImages') : null,
externalImages: {
context: 'src',
sources: glob.sync('src/assets/img/**/*.*'),
destination: 'dist/img/',
fileName: (filepath) => {
let name = filepath.split(/img(\/|\\)/).pop();
return name;
},
},
plugins: [
imageminGifsicle({
interlaced: false
}),
imageminMozjpeg({
progressive: true,
arithmetic: false
}),
imageminPngquant({
floyd: 0.5,
speed: 2
}),
imageminSvgo({
plugins: [
{ removeTitle: true },
{ convertPathData: false }
]
}),
],
}),
];
if (isProd) {
plugins.push(
new ProgressPlugin({
entries: true,
handler: (percentage, msg, ...args) => {
let moduleName = '';
if (args[2]) {
let sep = args[2].split(path.sep);
let dirIndex = sep.indexOf('finstead-client');
moduleName = sep.slice(dirIndex).join('/');
}
const currentWork = args[0] ? ` ${args[0]}` : '';
console.log(`[Progress${currentWork}]: (${(percentage * 100).toFixed(3)}%) ${msg} ${moduleName}`)
}
}),
new BundleAnalyzerPlugin({
analyzerMode: 'static',
reportFilename: path.resolve(__dirname, 'analysis.html'),
generateStatsFile: false,
logLevel: 'info',
})
);
}
return plugins;
};
const optimization = () => {
let optimizations = {
moduleIds: isProd ? 'deterministic' : 'named',
chunkIds: isProd ? 'deterministic' : 'named',
splitChunks: {
chunks: 'all',
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/](react|react-dom|lodash|moment)[\\/]/,
},
}
}
};
if (isProd) {
optimizations.minimizer = [
new TerserJSPlugin({
terserOptions: {
compress: {
pure_funcs: ['console.log'],
drop_console: true,
drop_debugger: true
},
warnings: false
},
parallel: true
}),
new OptimizeCSSAssetsPlugin({})
];
}
return optimizations;
};
const fontLoaders = [
{
test: /\.woff(\?v=\d+\.\d+\.\d+)?$/,
use: {
loader: 'url?limit=10000&mimetype=application/font-woff',
}
},
{
test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/,
use: {
loader: 'url?limit=10000&mimetype=application/font-woff',
}
},
{
test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/,
use: {
loader: 'url?limit=10000&mimetype=application/octet-stream',
}
},
{
test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
use: {
loader: 'url?limit=10000&mimetype=image/svg+xml',
}
},
{
test: /\.eot(\?v=\d+\.\d+\.\d+)?$/,
use: {
loader: 'file-loader',
}
}
];
const config = {
mode: process.env.NODE_ENV,
devtool: isProd ? false : 'eval-source-map',
context: path.resolve(__dirname, '../src'),
entry: {
bundle: {
import: './app.jsx',
dependOn: 'vendor',
},
styles: './sass/main.scss',
vendor: {
runtime: 'runtimecode',
import: [
'react',
'react-dom',
'redux',
'redux-saga',
'react-redux',
'react-router',
'react-tap-event-plugin',
'lodash',
'moment',
]
}
},
output: {
publicPath: '/build/',
filename: '[name].[contenthash].js',
chunkFilename: '[name].[contenthash].js',
path: path.resolve(__dirname, '../dist/build'),
},
resolve: {
extensions: ['.js', '.jsx', '.json'],
alias: {
config: path.resolve(__dirname, '../config.js'),
utils: path.resolve(__dirname, '../src/utils'),
shared: path.resolve(__dirname, '../src/components/shared'),
services: path.resolve(__dirname, '../src/services'),
store: path.resolve(__dirname, '../src/store'),
constants: path.resolve(__dirname, '../src/constants'),
actions: path.resolve(__dirname, '../src/actions'),
components: path.resolve(__dirname, '../src/components'),
},
},
optimization: optimization(),
plugins: plugins(),
module: {
rules: [
{
test: /\.m?jsx?$/,
resolve: {
fullySpecified: false
},
// ignore transpiling JavaScript from node_modules as they should be ready to go OOTB
exclude: /node_modules/,
},
{
test: /\.jsx?$/,
exclude: [/node_modules/, /libs/],
use: {
loader: path.join(__dirname, '../helpers/custom-loader.js'),
options: {
presets: ['@babel/preset-env', '@babel/preset-react'],
plugins: [
'@babel/plugin-proposal-object-rest-spread',
'@babel/plugin-proposal-class-properties',
'@babel/plugin-transform-destructuring',
'@babel/plugin-syntax-dynamic-import',
'@babel/plugin-transform-runtime',
'syntax-async-functions'
],
cacheDirectory: true
}
}
},
{
test: /\.scss$/,
use: [
{
loader: MiniCssExtractPlugin.loader
},
{
loader: 'css-loader',
options: {
url: false,
}
},
{
loader: 'sass-loader',
options: {
implementation: require('node-sass'),
}
},
]
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.(jpe?g|png|gif|svg)$/i,
loader: "url-loader",
options: {
name: "[path][name].[ext]"
},
},
...fontLoaders
]
},
watchOptions: {
ignored: /node_modules/,
},
};
module.exports = config;
这是我使用的开发依赖项
"devDependencies": {
"@babel/cli": "^7.12.10",
"@babel/core": "^7.12.10",
"@babel/node": "^7.12.10",
"@babel/plugin-proposal-class-properties": "^7.12.1",
"@babel/plugin-proposal-decorators": "^7.12.12",
"@babel/plugin-proposal-export-namespace-from": "^7.12.1",
"@babel/plugin-proposal-function-sent": "^7.12.1",
"@babel/plugin-proposal-json-strings": "^7.12.1",
"@babel/plugin-proposal-numeric-separator": "^7.12.7",
"@babel/plugin-proposal-object-rest-spread": "^7.12.1",
"@babel/plugin-proposal-throw-expressions": "^7.12.1",
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
"@babel/plugin-syntax-import-meta": "^7.10.4",
"@babel/plugin-transform-destructuring": "^7.12.1",
"@babel/plugin-transform-runtime": "^7.12.10",
"@babel/preset-env": "^7.12.11",
"@babel/preset-react": "^7.12.10",
"@babel/register": "^7.12.10",
"@babel/runtime": "^7.12.0",
"@webpack-cli/migrate": "^1.1.2",
"assets-webpack-plugin": "^7.0.0",
"babel-eslint": "^10.0.3",
"babel-loader": "^8.2.2",
"babel-plugin-dynamic-import-webpack": "1.0.1",
"babel-plugin-lodash": "^3.3.4",
"babel-plugin-syntax-async-functions": "^6.13.0",
"babel-plugin-system-import-transformer": "^3.1.0",
"babel-plugin-transform-ensure-ignore": "^0.1.0",
"babel-plugin-webpack-alias": "^2.1.2",
"babel-watch": "^7.3.0",
"clean-webpack-plugin": "^3.0.0",
"cross-env": "^5.2.0",
"css-loader": "^5.0.1",
"eslint": "^6.8.0",
"eslint-config-airbnb": "^18.2.1",
"eslint-import-resolver-alias": "^1.1.2",
"eslint-plugin-import": "^2.22.0",
"eslint-plugin-jsx-a11y": "^6.4.1",
"eslint-plugin-react": "^7.22.0",
"eslint-plugin-react-hooks": "^1.7.0",
"file-loader": "^1.1.11",
"imagemin-mozjpeg": "^9.0.0",
"imagemin-pngquant": "^9.0.1",
"imagemin-svgo": "^8.0.0",
"imagemin-webpack-plugin": "^2.4.2",
"imports-loader": "^0.7.0",
"mini-css-extract-plugin": "^1.3.3",
"nodemon": "^2.0.7",
"optimize-css-assets-webpack-plugin": "^5.0.4",
"redux-devtools": "^3.7.0",
"redux-devtools-dock-monitor": "^1.2.0",
"redux-devtools-log-monitor": "^1.2.0",
"resolve-url-loader": "^2.3.2",
"sass-loader": "^10.1.0",
"terser-webpack-plugin": "^5.0.3",
"webpack": "^5.11.1",
"webpack-bundle-analyzer": "^4.3.0",
"webpack-cli": "^4.3.1"
},
编辑
如您所见,我正在起诉 assets-webpack-plugin
,它为我提供了入口点和 block 的 JSON。我手动将 bundle.[contenthash].js
和 vendor.[contenthash].js
插入到成功加载的 index.html 中。
但是在编译的bundle的源代码中,我可以看到bundle不是运行时可执行函数,而是webpack模块。
我已经在 vendor
入口点尝试了 runtime
属性,因为主 bundle
点依赖于 vendor 包,即使它给了我可执行函数它不执行我的主捆绑代码。
最佳答案
经过更多调查,我发现,除了入口包和运行时文件之外,webpack
还创建了初始执行所需的几个文件。
因此,为了制作自定义 html 文件或使用任何其他预处理器文件,我们需要使用 html-webpack-plugin
基于可选提供的模板或 webpack-manifest-plugin 生成 html 文件
获取 json 文件中所需文件的列表。
...
plugins: [
new WebpackManifestPlugin({
fileName: path.resolve(__dirname, './manifest.json'),
filter: (file) => file.isInitial,
}),
]
...
在这里,我们使用可用的过滤器选项来处理文件。
filter
函数提供 FileDescriptor
对象作为处理的输入,其中包含 isInitial
、isChunk
以及其他一些您可以使用的字段查找 documentation
正如您已经猜到的,标记为 isInitial
的文件是运行应用程序所需的。您还可以使用此插件生成自定义 list 文件。例如,我有一个文件,其中分离了初始 css
和 js
包。
{
"css": [
"/build/c3a7e.commons.css"
],
"js": [
"/build/bundle.bf972748425cb97699f3.js",
"/build/styles.09e2bc2ae43dc685a2c5.js",
"/build/vendor.8e55b287c8b3ffc5c578.js",
"/build/runtime.d9173e4531e516f18fc8.js",
"/build/commons-app_jsx.1fc7ae7feba211c7a59d.js",
"/build/vendor-node_modules_lodash_lodash_js-node_modules_moment_moment_js-node_modules_react-dom_index_js.fc6e3899340455918b6a.js",
"/build/commons-node_modules_moment_locale_sync_recursive_en_-node_modules_react-redux_es_index_js-no-b2e9a0.a38dfe96c879814994af.js"
]
}
关于javascript - Webpack 成功构建,但 JavaScript 不执行运行时,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65625040/