如何让 webpack 只为特定构建导入 polyfills?
how to make webpack import polyfills only for a specific build?
我刚刚设法让 webpack 创建两个单独的构建,一个用于 es5,另一个用于 es6。
请参阅下面的配置文件:
const path = require("path");
const common = require("./webpack.common");
const merge = require("webpack-merge");
const CleanWebpackPlugin = require("clean-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const OptimizeCssAssetsPlugin = require("optimize-css-assets-webpack-plugin");
const TerserPlugin = require("terser-webpack-plugin");
var HtmlWebpackPlugin = require("html-webpack-plugin");
const es5Config = merge(common,{
mode: "production",
output: {
filename: "[name].[contentHash].bundle.es5.js",
path: path.resolve(__dirname, "dist")
},
optimization: {
minimizer: [
new OptimizeCssAssetsPlugin(),
new TerserPlugin(),
new HtmlWebpackPlugin({
template: "./src/template.html",
minify: {
removeAttributeQuotes: true,
collapseWhitespace: true,
removeComments: true
}
}),
]
},
plugins: [
new MiniCssExtractPlugin({ filename: "[name].[contentHash].css" }),
new CleanWebpackPlugin(),
],
module: {
rules: [
{
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader, //3. Extract css into files
"css-loader", //2. Turns css into commonjs
"sass-loader" //1. Turns sass into css
]
},
{
test: /\.m?js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: [
['@babel/preset-env', {
modules: false,
useBuiltIns: 'entry',
targets: {
browsers: [
"IE 11"
],
},
}],
],
},
},
}],
},
});
const es6Config = merge(common, {
mode: "production",
output: {
filename: "[name].[contentHash].bundle.es6.js",
path: path.resolve(__dirname, "dist")
},
optimization: {
minimizer: [
new OptimizeCssAssetsPlugin(),
new TerserPlugin(),
new HtmlWebpackPlugin({
template: "./src/template.html",
minify: {
removeAttributeQuotes: true,
collapseWhitespace: true,
removeComments: true
}
}),
]
},
plugins: [
new MiniCssExtractPlugin({ filename: "[name].[contentHash].css" }),
new CleanWebpackPlugin(),
],
module: {
rules: [
{
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader, //3. Extract css into files
"css-loader", //2. Turns css into commonjs
"sass-loader" //1. Turns sass into css
]
},
{
test: /\.m?js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: [
['@babel/preset-env', {
modules: false,
useBuiltIns: "usage",
targets: {
browsers: [
'Chrome >= 60',
'Safari >= 10.1',
'iOS >= 10.3',
'Firefox >= 54',
'Edge >= 15',
],
},
}],
],
},
},
}],
},
});
module.exports = [es5Config, es6Config];
现在的问题是我希望 webpack 只为 es5 构建导入 polyfill。并使用 usebuilins 设置为 usage 无法填充任何内容。
我是不是用错了可能与 node_modules 包有关??
所以我只是在主文件中导入 polyfill,例如:
import "core-js/stable";
import "regenerator-runtime/runtime";
我怎样才能让这些导入只为 es5 构建添加?或者如何从 webpack 中包含 polyfills/imports?
还有一个额外的问题,如果有人知道,我怎样才能正确使用带有“usage”的 usebuiltins?因为到目前为止,即使我认为为我的主文件添加了 polifyll,我仍然在 IE11 中遇到符号错误,例如。
我明白了。这是 webpack 配置:
常见:
const path = require("path");
module.exports = {
entry: {
main: "./src/index.js",
vendor: "./src/vendor.js"
},
module: {
rules: [
{
test: /\.html$/,
use: ["html-loader"]
},
{
test: /\.(svg|png|jpg|gif)$/,
use: {
loader: "file-loader",
options: {
name: "[name].[hash].[ext]",
outputPath: "imgs"
}
}
}
]
}
};
产品:
const path = require("path");
const common = require("./webpack.common");
const merge = require("webpack-merge");
const CleanWebpackPlugin = require("clean-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const OptimizeCssAssetsPlugin = require("optimize-css-assets-webpack-plugin");
const TerserPlugin = require("terser-webpack-plugin");
var HtmlWebpackPlugin = require("html-webpack-plugin");
const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer");
const es5Config = merge(common,{
mode: "production",
output: {
filename: "[name].[contentHash].bundle.es5.js",
path: path.resolve(__dirname, "dist")
},
optimization: {
runtimeChunk: 'single',
splitChunks: {
chunks: 'all',
maxInitialRequests: Infinity,
minSize: 0,
cacheGroups: {
vendor: {
test: /[\/]node_modules[\/]/,
name: (module) => {
// get the name. E.g. node_modules/packageName/not/this/part.js
// or node_modules/packageName
const packageName = module.context.match(/[\/]node_modules[\/](.*?)([\/]|$)/)[1];
// npm package names are URL-safe, but some servers don't like @ symbols
return `npm.${packageName.replace('@', '')}`;
},
},
},
},
minimizer: [
new OptimizeCssAssetsPlugin(),
new TerserPlugin(),
new HtmlWebpackPlugin({
filename: 'main.aspx',
template: "./src/main.html",
// minify: {
// removeAttributeQuotes: true,
// collapseWhitespace: true,
// removeComments: true
// }
}),
]
},
plugins: [
new MiniCssExtractPlugin({ filename: "[name].[contentHash].css" }),
new CleanWebpackPlugin(),
new BundleAnalyzerPlugin(),
],
module: {
rules: [
{
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader, //3. Extract css into files
"css-loader", //2. Turns css into commonjs
"sass-loader" //1. Turns sass into css
]
},
{
test: /\.m?js$/,
exclude: /node_modules/,
//exclude: /node_modules\/(?!(\@pnp)\/).*/,
use: {
loader: 'babel-loader',
options: {
//configFile : './es5.babel.config.json',
presets: [
['@babel/preset-env', {
modules: false,
useBuiltIns: false,
targets: {
browsers: [
"IE 11"
],
},
}],
],
},
},
}],
},
});
const es6Config = merge(common, {
mode: "production",
output: {
filename: "[name].[contentHash].bundle.es6.js",
path: path.resolve(__dirname, "dist")
},
optimization: {
runtimeChunk: 'single',
splitChunks: {
chunks: 'all',
maxInitialRequests: Infinity,
minSize: 0,
cacheGroups: {
vendor: {
test: /[\/]node_modules[\/]/,
name: (module) => {
// get the name. E.g. node_modules/packageName/not/this/part.js
// or node_modules/packageName
const packageName = module.context.match(/[\/]node_modules[\/](.*?)([\/]|$)/)[1];
// npm package names are URL-safe, but some servers don't like @ symbols
return `npm.${packageName.replace('@', '')}`;
},
},
},
},
minimizer: [
new OptimizeCssAssetsPlugin(),
new TerserPlugin(),
new HtmlWebpackPlugin({
filename: 'main_es6.html',
template: "./src/main.html",
// minify: {
// removeAttributeQuotes: true,
// collapseWhitespace: true,
// removeComments: true
// }
}),
]
},
plugins: [
new MiniCssExtractPlugin({ filename: "[name].[contentHash].css" }),
new CleanWebpackPlugin(),
],
module: {
rules: [
{
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader, //3. Extract css into files
"css-loader", //2. Turns css into commonjs
"sass-loader" //1. Turns sass into css
]
},
],
},
});
module.exports = [ es5Config, es6Config];
babel.config.json:
{
"plugins": [
[
"@babel/plugin-transform-runtime",
{
"absoluteRuntime": true,
"helpers": true,
"regenerator": true,
"useESModules": false
}
]
]
}
因此,这与 cdn polyfill 相结合,可以仅为 IE11 加载 polifyll。它也有自己的构建。
这里唯一的问题是生成的输出将包含单独的文件,并且 es5 构建的所有脚本中都应该没有模块。同样对于 es6 都应该有模块。
然后我必须手动添加那些我可以轻松制作自定义模板来处理的内容。
但是 polyfill 的脚本被删除了,我仍然需要手动合并 html 文件。有人知道如何处理吗?
编辑:
1 - 对于属性,您可以使用 HtmlWebpackPlugin hooks () 或 ScriptExtHtmlWebpackPlugin 将属性放置在标签中。
在这里找到一些带钩子的代码:
const HtmlWebpackPlugin = require('html-webpack-plugin');
class es5TagTransformerHook {
apply (compiler) {
compiler.hooks.compilation.tap('MyPlugin', (compilation) => {
HtmlWebpackPlugin.getHooks(compilation).alterAssetTagGroups.tapAsync(
'es5TagTransformerHook', stacktraces
async (data, cb) => {
// Manipulate the content
// data.html += 'The Magic Footer'
// Tell webpack to move on
data.bodyTags.forEach(t=>t.tagName === 'script'?t.attributes.nomodule = true:null)
cb(null, data)
}
)
})
}
}
module.exports = es5TagTransformerHook
2 - 为了合并结果 html 我最终使用了这个要点:
https://gist.github.com/agoldis/21782f3b9395f78d28dce23e3b6ddd56
我刚刚设法让 webpack 创建两个单独的构建,一个用于 es5,另一个用于 es6。 请参阅下面的配置文件:
const path = require("path");
const common = require("./webpack.common");
const merge = require("webpack-merge");
const CleanWebpackPlugin = require("clean-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const OptimizeCssAssetsPlugin = require("optimize-css-assets-webpack-plugin");
const TerserPlugin = require("terser-webpack-plugin");
var HtmlWebpackPlugin = require("html-webpack-plugin");
const es5Config = merge(common,{
mode: "production",
output: {
filename: "[name].[contentHash].bundle.es5.js",
path: path.resolve(__dirname, "dist")
},
optimization: {
minimizer: [
new OptimizeCssAssetsPlugin(),
new TerserPlugin(),
new HtmlWebpackPlugin({
template: "./src/template.html",
minify: {
removeAttributeQuotes: true,
collapseWhitespace: true,
removeComments: true
}
}),
]
},
plugins: [
new MiniCssExtractPlugin({ filename: "[name].[contentHash].css" }),
new CleanWebpackPlugin(),
],
module: {
rules: [
{
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader, //3. Extract css into files
"css-loader", //2. Turns css into commonjs
"sass-loader" //1. Turns sass into css
]
},
{
test: /\.m?js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: [
['@babel/preset-env', {
modules: false,
useBuiltIns: 'entry',
targets: {
browsers: [
"IE 11"
],
},
}],
],
},
},
}],
},
});
const es6Config = merge(common, {
mode: "production",
output: {
filename: "[name].[contentHash].bundle.es6.js",
path: path.resolve(__dirname, "dist")
},
optimization: {
minimizer: [
new OptimizeCssAssetsPlugin(),
new TerserPlugin(),
new HtmlWebpackPlugin({
template: "./src/template.html",
minify: {
removeAttributeQuotes: true,
collapseWhitespace: true,
removeComments: true
}
}),
]
},
plugins: [
new MiniCssExtractPlugin({ filename: "[name].[contentHash].css" }),
new CleanWebpackPlugin(),
],
module: {
rules: [
{
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader, //3. Extract css into files
"css-loader", //2. Turns css into commonjs
"sass-loader" //1. Turns sass into css
]
},
{
test: /\.m?js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: [
['@babel/preset-env', {
modules: false,
useBuiltIns: "usage",
targets: {
browsers: [
'Chrome >= 60',
'Safari >= 10.1',
'iOS >= 10.3',
'Firefox >= 54',
'Edge >= 15',
],
},
}],
],
},
},
}],
},
});
module.exports = [es5Config, es6Config];
现在的问题是我希望 webpack 只为 es5 构建导入 polyfill。并使用 usebuilins 设置为 usage 无法填充任何内容。
我是不是用错了可能与 node_modules 包有关??
所以我只是在主文件中导入 polyfill,例如:
import "core-js/stable";
import "regenerator-runtime/runtime";
我怎样才能让这些导入只为 es5 构建添加?或者如何从 webpack 中包含 polyfills/imports?
还有一个额外的问题,如果有人知道,我怎样才能正确使用带有“usage”的 usebuiltins?因为到目前为止,即使我认为为我的主文件添加了 polifyll,我仍然在 IE11 中遇到符号错误,例如。
我明白了。这是 webpack 配置: 常见:
const path = require("path");
module.exports = {
entry: {
main: "./src/index.js",
vendor: "./src/vendor.js"
},
module: {
rules: [
{
test: /\.html$/,
use: ["html-loader"]
},
{
test: /\.(svg|png|jpg|gif)$/,
use: {
loader: "file-loader",
options: {
name: "[name].[hash].[ext]",
outputPath: "imgs"
}
}
}
]
}
};
产品:
const path = require("path");
const common = require("./webpack.common");
const merge = require("webpack-merge");
const CleanWebpackPlugin = require("clean-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const OptimizeCssAssetsPlugin = require("optimize-css-assets-webpack-plugin");
const TerserPlugin = require("terser-webpack-plugin");
var HtmlWebpackPlugin = require("html-webpack-plugin");
const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer");
const es5Config = merge(common,{
mode: "production",
output: {
filename: "[name].[contentHash].bundle.es5.js",
path: path.resolve(__dirname, "dist")
},
optimization: {
runtimeChunk: 'single',
splitChunks: {
chunks: 'all',
maxInitialRequests: Infinity,
minSize: 0,
cacheGroups: {
vendor: {
test: /[\/]node_modules[\/]/,
name: (module) => {
// get the name. E.g. node_modules/packageName/not/this/part.js
// or node_modules/packageName
const packageName = module.context.match(/[\/]node_modules[\/](.*?)([\/]|$)/)[1];
// npm package names are URL-safe, but some servers don't like @ symbols
return `npm.${packageName.replace('@', '')}`;
},
},
},
},
minimizer: [
new OptimizeCssAssetsPlugin(),
new TerserPlugin(),
new HtmlWebpackPlugin({
filename: 'main.aspx',
template: "./src/main.html",
// minify: {
// removeAttributeQuotes: true,
// collapseWhitespace: true,
// removeComments: true
// }
}),
]
},
plugins: [
new MiniCssExtractPlugin({ filename: "[name].[contentHash].css" }),
new CleanWebpackPlugin(),
new BundleAnalyzerPlugin(),
],
module: {
rules: [
{
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader, //3. Extract css into files
"css-loader", //2. Turns css into commonjs
"sass-loader" //1. Turns sass into css
]
},
{
test: /\.m?js$/,
exclude: /node_modules/,
//exclude: /node_modules\/(?!(\@pnp)\/).*/,
use: {
loader: 'babel-loader',
options: {
//configFile : './es5.babel.config.json',
presets: [
['@babel/preset-env', {
modules: false,
useBuiltIns: false,
targets: {
browsers: [
"IE 11"
],
},
}],
],
},
},
}],
},
});
const es6Config = merge(common, {
mode: "production",
output: {
filename: "[name].[contentHash].bundle.es6.js",
path: path.resolve(__dirname, "dist")
},
optimization: {
runtimeChunk: 'single',
splitChunks: {
chunks: 'all',
maxInitialRequests: Infinity,
minSize: 0,
cacheGroups: {
vendor: {
test: /[\/]node_modules[\/]/,
name: (module) => {
// get the name. E.g. node_modules/packageName/not/this/part.js
// or node_modules/packageName
const packageName = module.context.match(/[\/]node_modules[\/](.*?)([\/]|$)/)[1];
// npm package names are URL-safe, but some servers don't like @ symbols
return `npm.${packageName.replace('@', '')}`;
},
},
},
},
minimizer: [
new OptimizeCssAssetsPlugin(),
new TerserPlugin(),
new HtmlWebpackPlugin({
filename: 'main_es6.html',
template: "./src/main.html",
// minify: {
// removeAttributeQuotes: true,
// collapseWhitespace: true,
// removeComments: true
// }
}),
]
},
plugins: [
new MiniCssExtractPlugin({ filename: "[name].[contentHash].css" }),
new CleanWebpackPlugin(),
],
module: {
rules: [
{
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader, //3. Extract css into files
"css-loader", //2. Turns css into commonjs
"sass-loader" //1. Turns sass into css
]
},
],
},
});
module.exports = [ es5Config, es6Config];
babel.config.json:
{
"plugins": [
[
"@babel/plugin-transform-runtime",
{
"absoluteRuntime": true,
"helpers": true,
"regenerator": true,
"useESModules": false
}
]
]
}
因此,这与 cdn polyfill 相结合,可以仅为 IE11 加载 polifyll。它也有自己的构建。
这里唯一的问题是生成的输出将包含单独的文件,并且 es5 构建的所有脚本中都应该没有模块。同样对于 es6 都应该有模块。 然后我必须手动添加那些我可以轻松制作自定义模板来处理的内容。
但是 polyfill 的脚本被删除了,我仍然需要手动合并 html 文件。有人知道如何处理吗?
编辑: 1 - 对于属性,您可以使用 HtmlWebpackPlugin hooks () 或 ScriptExtHtmlWebpackPlugin 将属性放置在标签中。
在这里找到一些带钩子的代码:
const HtmlWebpackPlugin = require('html-webpack-plugin');
class es5TagTransformerHook {
apply (compiler) {
compiler.hooks.compilation.tap('MyPlugin', (compilation) => {
HtmlWebpackPlugin.getHooks(compilation).alterAssetTagGroups.tapAsync(
'es5TagTransformerHook', stacktraces
async (data, cb) => {
// Manipulate the content
// data.html += 'The Magic Footer'
// Tell webpack to move on
data.bodyTags.forEach(t=>t.tagName === 'script'?t.attributes.nomodule = true:null)
cb(null, data)
}
)
})
}
}
module.exports = es5TagTransformerHook
2 - 为了合并结果 html 我最终使用了这个要点: https://gist.github.com/agoldis/21782f3b9395f78d28dce23e3b6ddd56