webpack 多入口构建(基础版)
config/webpack.dev.js
const HtmlWebpackPlugin = require('html-webpack-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const { resolveApp, getEntryApp } = require('./tools')
const { entry, htmlWebpackPlugins } = getEntryApp()
module.exports = {
target: 'web',
mode: 'development',
entry: {
main: './src/main.js',
...entry,
},
output: {
filename: 'js/[name].[chunkhash:6].build.js',
path: resolveApp('dist'),
},
devServer: { hot: true },
resolve: {
extensions: ['.js', '.html', '.json'],
alias: { '@': resolveApp('src') },
},
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: { importLoaders: 1, esModule: false },
},
'postcss-loader',
],
},
{
test: /\.less$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: { importLoaders: 1, esModule: false },
},
'postcss-loader',
'less-loader',
],
},
{
test: /\.(png|svg|gif|jpe?g)$/,
type: 'asset',
generator: { filename: 'images/[name].[hash:4][ext]' },
parser: { dataUrlCondition: { maxSize: 30 * 1024 } },
},
{
test: /\.(ttf|woff2?)$/,
type: 'asset/resource',
generator: { filename: 'fonts/[name].[hash:3][ext]' },
},
{
test: /\.js$/,
exclude: /node_modules/,
use: ['babel-loader'],
},
],
},
plugins: [
new HtmlWebpackPlugin({ title: 'WEBPACK-CONFIG', template: './public/index.html', chunks: ['main'] }),
new CopyWebpackPlugin({ patterns: [{ from: 'public', globOptions: { ignore: ['**/index.html'] } }] }),
...htmlWebpackPlugins,
],
}
config/webpack.prod.js
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const { resolveApp, getEntryApp } = require('./tools')
const { entry, htmlWebpackPlugins } = getEntryApp()
module.exports = {
mode: 'production',
entry: {
main: './src/main.js',
...entry,
},
output: {
filename: 'js/[name].[chunkhash:6].build.js',
path: resolveApp('dist'),
},
devServer: { hot: true },
resolve: {
extensions: ['.js', '.html', '.json'],
alias: { '@': resolveApp('src') },
},
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: { importLoaders: 1, esModule: false },
},
'postcss-loader',
],
},
{
test: /\.less$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: { importLoaders: 1, esModule: false },
},
'postcss-loader',
'less-loader',
],
},
{
test: /\.(png|svg|gif|jpe?g)$/,
type: 'asset',
generator: { filename: 'images/[name].[hash:4][ext]' },
parser: { dataUrlCondition: { maxSize: 30 * 1024 } },
},
{
test: /\.(ttf|woff2?)$/,
type: 'asset/resource',
generator: { filename: 'fonts/[name].[hash:3][ext]' },
},
{
test: /\.js$/,
exclude: /node_modules/,
use: ['babel-loader'],
},
],
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({ title: 'WEBPACK-CONFIG', template: './public/index.html', chunks: ['main'] }),
new CopyWebpackPlugin({ patterns: [{ from: 'public', globOptions: { ignore: ['**/index.html'] } }] }),
...htmlWebpackPlugins,
],
}
config/tools.js
const HtmlWebpackPlugin = require('html-webpack-plugin')
const glob = require('glob')
const path = require('path')
const appDir = process.cwd()
/**
* @description 获取文件路径
*/
const resolveApp = relativePath => {
return path.resolve(appDir, relativePath)
}
/**
* @description 自动获取 html 下的 js、html 文件
*/
const getEntryApp = () => {
const entry = {}
const htmlWebpackPlugins = []
const entryFiles = glob.sync(resolveApp('./src/html/**/index.js'))
entryFiles.forEach(item => {
const match = item.match(/src\/(.*)\/index\.js$/)
const pageName = match?.[1]
const name = pageName.split('/')[1]
entry[name] = item
htmlWebpackPlugins.push(
new HtmlWebpackPlugin({
filename: `${pageName}.html`,
template: resolveApp(`src/${pageName}/index.html`),
chunks: [name],
}),
)
})
return { entry, htmlWebpackPlugins }
}
module.exports = {
resolveApp,
getEntryApp,
}
package.json
{
"name": "webpack-build",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"scripts": {
"start": "webpack serve --config config/dev.config.js",
"build": "webpack --config config/pro.config.js"
},
"devDependencies": {
"@babel/core": "^7.21.4",
"@babel/preset-env": "^7.21.5",
"autoprefixer": "^10.4.14",
"babel-loader": "^9.1.2",
"clean-webpack-plugin": "^4.0.0",
"copy-webpack-plugin": "^11.0.0",
"css-loader": "^6.7.3",
"file-loader": "^6.2.0",
"html-webpack-plugin": "^5.5.1",
"less": "^4.1.3",
"less-loader": "^11.1.0",
"postcss": "^8.4.23",
"postcss-loader": "^7.2.4",
"postcss-preset-env": "^8.3.2",
"style-loader": "^3.3.2",
"url-loader": "^4.1.1",
"webpack": "^5.79.0",
"webpack-cli": "^5.0.1",
"webpack-dev-server": "^4.13.3"
},
"dependencies": {
"core-js": "^3.30.1",
"regenerator-runtime": "^0.13.11"
}
}
.browserslistrc
> 1%
last 2 version
not dead
babel.config.js
module.exports = {
presets: [
[
'@babel/preset-env',
{
// false: 不对当前的 JS 处理做 polyfill 填充
// usage: 依据用户源代码中所使用到的新语法进行填充
// entry: 依据我们当前筛选出来的浏览器来决定填充什么
useBuiltIns: 'usage',
corejs: 3,
},
],
],
}
webpack 多入口构建(完整版)
package.json
{
"name": "myzony-effect",
"version": "1.0.1",
"description": "js、css 交互效果",
"main": "index.ts",
"author": "myzony@qq.com",
"license": "MIT",
"scripts": {
"dev": "npm run start",
"start": "webpack serve --config config/webpack.common.js --env development",
"build": "webpack --config config/webpack.common.js --env production"
},
"dependencies": {
"core-js": "^3.30.1",
"regenerator-runtime": "^0.13.11"
},
"devDependencies": {
"@babel/core": "^7.21.4",
"@babel/preset-env": "^7.21.5",
"autoprefixer": "^10.4.14",
"babel-loader": "^9.1.2",
"clean-webpack-plugin": "^4.0.0",
"compression-webpack-plugin": "^10.0.0",
"copy-webpack-plugin": "^11.0.0",
"css-loader": "^6.7.3",
"css-minimizer-webpack-plugin": "^5.0.0",
"dart-sass": "^1.25.0",
"file-loader": "^6.2.0",
"html-loader": "^5.0.0",
"html-webpack-plugin": "^5.5.1",
"mini-css-extract-plugin": "^2.7.6",
"postcss": "^8.4.23",
"postcss-loader": "^7.2.4",
"postcss-preset-env": "^8.3.2",
"sass": "^1.63.2",
"sass-loader": "^13.3.1",
"style-loader": "^3.3.2",
"terser-webpack-plugin": "^5.3.8",
"url-loader": "^4.1.1",
"webpack": "^5.79.0",
"webpack-cli": "^5.0.1",
"webpack-dev-server": "^4.13.3",
"webpack-merge": "^5.8.0"
}
}
postcss.config.js
const autoprefixer = require('autoprefixer')
const postcssENV = require('postcss-preset-env')
module.exports = {
plugins: [postcssENV, autoprefixer],
}
babel.config.js
module.exports = {
presets: [
[
'@babel/preset-env',
{
// false: 不对当前的 JS 处理做 polyfill 填充
// usage: 依据用户源代码中所使用到的新语法进行填充
// entry: 依据我们当前筛选出来的浏览器来决定填充什么
useBuiltIns: 'usage',
corejs: 3,
},
],
],
}
tools.js
// config/tools.js
const HtmlWebpackPlugin = require('html-webpack-plugin')
const glob = require('glob')
const path = require('path')
const appDir = process.cwd()
/**
* @description 获取文件路劲
*/
const resolveApp = relativePath => {
return path.resolve(appDir, relativePath)
}
const getEntryApp = () => {
const entry = {}
const htmlWebpackPlugins = []
const entryFiles = glob.sync(resolveApp('./src/html/**/index.js'))
entryFiles.forEach(item => {
const match = item.match(/src\/(.*)\/index\.js$/)
const pageName = match?.[1]
const name = pageName.split('/')[1]
entry[name] = item
htmlWebpackPlugins.push(
new HtmlWebpackPlugin({
filename: `${pageName}.html`,
template: resolveApp(`src/${pageName}/index.html`),
chunks: [name],
}),
)
})
return { entry, htmlWebpackPlugins }
}
module.exports = {
resolveApp,
getEntryApp,
}
webpack.common.js
// config/webpack.common.js
const HtmlWebpackPlugin = require('html-webpack-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin') // css 压缩抽离
const { merge } = require('webpack-merge')
const { resolveApp, getEntryApp } = require('./tools')
const { entry, htmlWebpackPlugins } = getEntryApp()
const prodConfig = require('./webpack.prod')
const devConfig = require('./webpack.dev')
// console.log(entry, htmlWebpackPlugins)
const commonConfig = isProduction => ({
entry,
output: {
filename: 'js/[name].[chunkhash:6].build.js',
path: resolveApp('dist'),
},
devServer: { hot: true },
resolve: {
extensions: ['.js', '.html', '.json'],
alias: { '@': resolveApp('src') },
},
optimization: {
runtimeChunk: true, // 多入口打包设置为 single https://www.webpackjs.com/configuration/optimization/#optimizationruntimechunk
},
module: {
rules: [
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: { importLoaders: 1, esModule: false },
},
'postcss-loader',
],
},
{
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: { importLoaders: 1, esModule: false },
},
'postcss-loader',
{
loader: 'sass-loader',
options: { additionalData: '@import "~@/assets/styles/global.scss";' },
},
],
},
{
test: /\.(png|svg|gif|jpe?g)$/,
type: 'asset',
generator: { filename: 'images/[name].[hash:4][ext]' },
parser: { dataUrlCondition: { maxSize: 30 * 1024 } },
},
{
test: /\.(ttf|woff2?)$/,
type: 'asset/resource',
generator: { filename: 'fonts/[name].[hash:3][ext]' },
},
{
test: /\.(cur)$/,
use: [
{
loader: 'url-loader',
options: { name: 'images/[name].[hash:6].[ext]', limit: 25 * 1024 },
},
],
},
{
test: /\.js$/,
exclude: /node_modules/,
use: ['babel-loader'],
},
{
test: /.html$/,
loader: 'html-loader',
},
],
},
plugins: [
new HtmlWebpackPlugin({ title: '前端效果', template: './public/index.html', chunks: ['main'] }),
new CopyWebpackPlugin({ patterns: [{ from: 'public', globOptions: { ignore: ['**/index.html'] } }] }),
new MiniCssExtractPlugin({ filename: 'css/[name].[hash:8].css' }), // css 压缩抽离
...htmlWebpackPlugins,
],
})
module.exports = env => {
const isProduction = env.production
process.env.NODE_ENV = isProduction ? 'production' : 'development'
const config = isProduction ? prodConfig : devConfig
const mergeConfig = merge(commonConfig(isProduction), config)
// console.log('mergeConfig =', mergeConfig)
return mergeConfig
}
webpack.dev.js
// config/webpack.dev.js
module.exports = {
target: 'web',
mode: 'development',
devtool: 'source-map',
entry: { main: './src/main.js' },
devServer: {
hot: 'only', // 开启热更新,报错刷新不会刷新整个页面
port: 3399, // 端口号
open: false, // 自动打开浏览器
compress: true, // 启用 gzip 压缩
// proxy: {
// '/api': {
// target: 'https://api.github.com',
// pathRewrite: {'^/api': ''}, // 将 https:/api.github.com/api 重写为 https:/api.github.com
// changeOrigin: true, // 默认情况下,代理时会保留主机头的来源,可以将 changeOrigin 设置为 true 以覆盖此行为
// }
// }
},
}
webpack.prod.js
// config/webpack.prod.js
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin') // 对抽离的 css 进行压缩
const CompressionPlugin = require('compression-webpack-plugin') // 对资源进行 gzip 压缩处理
const TerserPlugin = require('terser-webpack-plugin') // 用于处理 js 的压缩和混淆,通过 webpack plugin 的方式对代码进行处理
module.exports = {
mode: 'production',
devtool: false,
performance: { hints: false },
entry: { main: './src/main.js' },
optimization: {
minimize: true, // 告知 webpack 使用 TerserPlugin 或其它在 optimization.minimizer定义的插件压缩 bundle
usedExports: true, // js 树摇
minimizer: [
new CssMinimizerPlugin(), // 对抽离的 css 进行压缩
new TerserPlugin({ extractComments: false }), // extractComments 是否需要生成 liscens 文件,将注释剥离到单独的文件中
],
},
plugins: [
new CleanWebpackPlugin(),
new CopyWebpackPlugin({ patterns: [{ from: 'public', globOptions: { ignore: ['**/index.html'] } }] }),
new CompressionPlugin({ test: /\.(css|js)$/ }), // 正则匹配进行压缩
],
}
注意
- border
- index.html
- index.js
- index.scss
由于 webpack 打包是从 js 开始编译,所以 scss 需要在 js 中引入,否则不会编译成 css。多个模块需要建立多个文件夹,每个都有一个 index.html,index.js 中引入 index.scss。