那是我的
webpack.config.js
:
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const fs = require('fs');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');
const TerserPlugin = require('terser-webpack-plugin');
const generateHtmlPlugin = (chunks, filename, inject, template, templateParameters) => {
return new HtmlWebpackPlugin({
chunks,
filename,
inject,
template,
templateParameters
});
}
const generateSingleBundleEntryFilePaths = async () => {
const distDir = __dirname.split('\\') + '\\dist';
return await fs.promises.access(distDir, fs.constants.R_OK | fs.constants.W_OK)
.then((success) => fs.promises.readdir(distDir));
};
const multipleModulesTemplatesArray = [
{
chunks: ['egm_mm_wrapper_bottom_banner'],
filename: 'egm_mm_wrapper_bottom_banner.html',
inject: 'head',
template: 'src/html/template.ejs',
templateParameters: {
bodyClass: 'egm_mm_wrapper_bottom_banner',
htmlClass: 'egm_mm_wrapper_bottom_banner',
initConsoleLog: 'MMWrapperBottom start'
}
},
{
chunks: ['egm_mm_wrapper_overhead_display'],
filename: 'egm_mm_wrapper_overhead_display.html',
inject: 'head',
template: 'src/html/template.ejs',
templateParameters: {
bodyClass: 'egm_mm_wrapper_overhead_display',
htmlClass: 'egm_mm_wrapper_overhead_display',
initConsoleLog: 'MMWrapperOverhead start'
}
},
{
chunks: ['ilink_media_scheduler'],
filename: 'ilink_media_scheduler.html',
inject: 'head',
template: 'src/html/template.ejs',
templateParameters: {
bodyClass: 'ilink_media_scheduler',
htmlClass: 'ilink_media_scheduler',
initConsoleLog: 'WMiLink.MediaScheduler start'
}
},
{
chunks: ['ilink_window_manager'],
filename: 'ilink_window_manager.html',
inject: 'head',
template: 'src/html/template.ejs',
templateParameters: {
bodyClass: 'ilink_window_manager',
htmlClass: 'ilink_window_manager',
initConsoleLog: 'WMiLink.WindowManager start'
}
}
];
const populateHtmlPlugins = (pagesArray) => {
const result = [];
pagesArray.forEach(page => {
result.push(generateHtmlPlugin(page.chunks, page.filename, page.inject, page.template, page.templateParameters));
})
return result;
}
const templates = populateHtmlPlugins(multipleModulesTemplatesArray);
const commonsAndVendorsConfig = {
context: path.resolve(__dirname, '.'),
devServer: {
client: {
logging: 'verbose',
overlay: true,
},
static: {
directory: path.join(__dirname, './dist'),
},
compress: true,
port: 9001,
},
entry: [
'../shared_libs/modules/vendors.js',
'../shared_libs/modules/commons.js',
'./src/js/egm_mm_wrapper_commons.js',
'./src/js/ilink_commons.js',
],
mode: 'production',
module: {
rules: [
{
exclude: /node_modules/,
loader: 'babel-loader',
options: {
presets: [
'@babel/preset-env'
]
},
test: /\.js$/
}
]
},
name: 'commons-and-vendors',
optimization: {
minimize: true,
minimizer: [
new TerserPlugin({
extractComments: false,
terserOptions: {
ecma: 5,
format: {
comments: false,
},
mangle: {
toplevel: true
},
module: true
},
}),
]
},
output: {
filename: 'commons_and_vendors.[contenthash].js',
path: path.resolve(__dirname, 'dist'),
publicPath: '/'
},
performance: {
maxEntrypointSize: 512000,
maxAssetSize: 512000
},
target: 'web'
};
const multipleModulesConfig = {
context: path.resolve(__dirname, '.'),
devServer: {
client: {
logging: 'verbose',
overlay: true,
},
static: {
directory: path.join(__dirname, './dist'),
},
compress: true,
port: 9001,
},
entry: {
egm_mm_wrapper_bottom_banner: './modules/egm_mm_wrapper_bottom_banner.js',
egm_mm_wrapper_overhead_display: './modules/egm_mm_wrapper_overhead_display.js',
ilink_media_scheduler: './modules/ilink_media_scheduler.js',
ilink_window_manager: './modules/ilink_window_manager.js'
},
mode: 'production',
module: {
rules: [
{
exclude: /node_modules/,
loader: 'babel-loader',
options: {
presets: [
['@babel/preset-env']
]
},
test: /\.js$/
},
{
exclude: /node_modules/,
test: /\.s[ac]ss$/i,
use: [
'css-loader',
'sass-loader'
]
},
{
test: /\.html$/i,
loader: 'html-loader',
options: {
minimize: true,
},
},
]
},
name: 'multiple-modules',
optimization: {
minimize: true,
minimizer: [
new CssMinimizerPlugin({
test: /\.s[ac]ss$/i
}),
new TerserPlugin({
extractComments: false,
terserOptions: {
ecma: 5,
format: {
comments: false,
},
mangle: {
toplevel: true
},
module: true
},
}),
]
},
output: {
filename: '[name].[contenthash].js',
path: path.resolve(__dirname, 'dist'),
publicPath: '/'
},
performance: {
maxEntrypointSize: 512000,
maxAssetSize: 512000
},
plugins: templates,
target: 'web'
};
const singleModuleConfig = {
context: path.resolve(__dirname, '.'),
devServer: {
client: {
logging: 'verbose',
overlay: true,
},
static: {
directory: path.join(__dirname, './dist'),
},
compress: true,
port: 9001,
},
entry: generateSingleBundleEntryFilePaths(),
mode: 'production',
module: {
rules: [
{
exclude: /node_modules/,
loader: 'babel-loader',
options: {
presets: [
['@babel/preset-env']
]
},
test: /\.js$/
},
{
test: /\.html$/i,
loader: 'html-loader',
options: {
minimize: true,
},
},
]
},
name: 'mma',
optimization: {
minimize: true,
minimizer: [
new TerserPlugin({
extractComments: false,
terserOptions: {
ecma: 5,
format: {
comments: false,
},
mangle: {
toplevel: true
},
module: true
},
}),
]
},
output: {
clean: true,
filename: 'mma.[contenthash].js',
path: path.resolve(__dirname, 'dist'),
publicPath: '/'
},
performance: {
maxEntrypointSize: 512000,
maxAssetSize: 512000
},
target: 'web'
};
module.exports = [commonsAndVendorsConfig, multipleModulesConfig, singleModuleConfig];
它使用三种配置。
首先创建供应商和通用类捆绑包,第二个创建带有注入样式的脚本,并基于
.html
模板渲染 .ejs
文件,第三个应该获取所有创建的 .html
文件和脚本并将其捆绑到单个 .js
文件中.
问题是,最后一个配置的
entry
是一个方法,它应该检查 ./dist
目录是否已经创建,然后列出该目录中的所有文件,并将列表作为 string[]
返回,即数组字符串,作为 entry
. 的值
但是看起来有很多方法:
const generateSingleBundleEntryFilePaths = async () => {
const distDir = __dirname.split('\\') + '\\dist';
return await fs.promises.access(distDir, fs.constants.R_OK | fs.constants.W_OK)
.then((success) => fs.promises.readdir(distDir));
};
未按预期工作:
(node:5680) UnhandledPromiseRejectionWarning: Error: ENOENT: no such file or directory, access 'C:\Users\ar-IGT\Projects\bundles\,Users,ar-IGT,Projects,bundles\dist'
(Use `node --trace-warnings ...` to show where the warning was created)
(node:5680) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a
promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api
/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
(node:5680) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a
non-zero exit code.
此警告开始时的路径甚至是错误的:
C:\Users\ar-IGT\Projects\bundles\,Users,ar-IGT,Projects,bundles\dist
不知道是什么原因造成的。
Node 没有 Observables,所以我能想到的不多,等待
./dist
目录被创建,然后用我可以用作最后一个配置条目的文件填充它。
提前致谢!
你可以尝试现代的 html-bundler-webpack-plugin。
此插件允许使用条目作为模板的路径。
使用简单:
const HtmlBundlerPlugin = require('html-bundler-webpack-plugin');
module.exports = {
plugins: [
new HtmlBundlerPlugin({
// relative or absolute path to templates
entry: 'src/views/',
}),
preprocessor: 'ejs', // specify the template engine
],
// ...
};
捆绑器插件会自动查找并渲染所有模板。
该插件支持许多开箱即用的流行模板引擎。只需安装所需的 npm 包并指定
preprocessor
与模板引擎的名称:ejs
、eta
、handlebars
、nunjucks
。
参见在浏览器中工作的示例: