一、文件目录:
二、webpack.config.js
// 随着项目需求的增长,我们必须要找到更有效地管理配置文件的方法。
// 分离配置文件:
// webpack.base.config.js 公共环境的配置文件
// webpack.dev.config.js 开发环境下的配置文件
// webpack.prod.config.js 生产环境下的配置文件
const merge = require('webpack-merge');
// webpack-merge: 如果配置文件被分成了许多不同的部分,那么必须以某种方式来组合他们,
// 通常就是合并数组和对象,webpack-merge就是用来合并配置文件的
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
// webpack Bundle Analyzer是一个可视化的工具,能够分析和生成打包输出后文件大小互动缩放树形图。
const devConfig = require('./build/webpack.dev.config.js'); // 开发环境下的配置文件
const prodConfig = require('./build/webpack.prod.config.js'); // 生产环境下的配置文件
const ip = require('ip').address();
// 获取局域网IP
const portFinder = require('portfinder');
// 获取port
const isDev = process.env.NODE_ENV === 'development';
const devWebpackConfig = merge(devConfig,
{
devServer: {
host: ip, // 指定使用一个 host。默认是 localhost
proxy: {
'/api': {
target: 'http://192.168.2.90:8080',
changeOrigin: true,
pathRewrite: { // 不传递/api
'^/api': ''
}
}
}
}
}
);
if (isDev) {
module.exports = new Promise((resolve, reject) => {
portFinder.getPort({ port: 9000, stopPort: 9999 }, function (err, port) {
if (err) {
reject(err);
} else {
devWebpackConfig.devServer.port = port;
resolve(devWebpackConfig);
}
});
});
} else {
if (process.env.BUILD_REPORT) {
module.exports = merge(prodConfig, {
plugins: [
new BundleAnalyzerPlugin()
]
})
} else {
module.exports = prodConfig;
}
}
二、webpack.base.config.js
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
//打包后自动生成index.html文件,并把打包生成的js自动引入到这个html文件中
const CleanWebpackPlugin = require('clean-webpack-plugin');
//每次打包之前都会自动将dist文件中的内容删除
const appConfig = require('./app.config.js');
const srcPath = '../assets/';
const staticPrefix = process.env.NODE_ENV=='development'?'':appConfig.relativePrefix
module.exports = {
entry: { // 要打包的文件
app: [
'react-hot-loader/patch', // webpack-dev-server 的热加载是开发人员修改了代码,代码经过打包,重新刷新了整个页面。而 react-hot-loader 不会刷新整个页面,
//它只替换了修改的代码,做到了页面的局部刷新。但它需要依赖 webpack 的 HotModuleReplacement 热加载插件。
path.join(__dirname, srcPath, 'index.js')
]
},
resolve: { // 配置模块如何解析
extensions: ['.js', '.jsx'], // 自动解析确定的扩展,引入的文件可以不加.js或者.jsx后缀
alias: { // 创建 import 或 require 的别名,来确保模块引入变得更简单
// 例如:import A from '../../components/A' === import A from 'components/A'
components: path.join(__dirname, srcPath, 'components'),
router: path.join(__dirname, srcPath, 'router'),
store: path.join(__dirname, srcPath, 'store'),
pages: path.join(__dirname, srcPath, 'pages'),
api: path.join(__dirname, srcPath, 'api'),
}
},
module: { // 如何处理项目中的不同类型的模块
rules: [ // 创建模块时,匹配请求的规则数组。这些规则能够对模块(module)应用 loader,或者修改解析器(parser)。
{
enforce: 'pre', // 通常rules是按照从下往上执行的,在对象中添加了enforce: 'pre'表示优先处理这个loader
test: /.(js|jsx)$/, // 匹配以.js或者.jsx结尾的文件
loader: 'eslint-loader', // 语法规则和代码风格的检查工具
exclude: [ // 排除node_modules文件下的.js/.jsx文件
path.resolve(__dirname, '../node_modules')
],
options: {
fix: true // 如果存在很浅显的不规范代码,eslint会自动帮你修复掉
}
},
{
test: /.(js|jsx)$/,
loader: 'babel-loader', // Babel是一个JavaScript编译器,能够让我们放心的使用新一代JS语法。
exclude: [
path.join(__dirname, '../node_modules')
],
options: {
cacheDirectory: true // 通过使用 cacheDirectory 选项,将 babel-loader 提速至少两倍。 这会将转译的结果缓存到文件系统中。
}
},
{
test: /\.(png|jpg|gif|svg|pdf)$/,
use: {
loader: 'url-loader', // 为了减少请求,我们可以通过base64编码的方法来展示图片,url-loader就是用来处理这个的
options: {
name: staticPrefix + 'imgs/[name].[contenthash:8].[ext]',
limit: 10240 // 图片大小小于10240才会处理成base64编码的方式
}
}
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: {
loader: 'file-loader', // 处理文件
options: {
name: staticPrefix + 'fonts/[name].[contenthash:8].[ext]'
}
}
},
{
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
use: {
loader: 'file-loader',
options: {
name: staticPrefix + 'media/[name].[contenthash:8].[ext]'
}
}
}
]
},
plugins: [
new webpack.DefinePlugin({ // DefinePlugin插件使得前端项目更加工程化,说清晰点就是如何使用这个插件,在编译阶段根据NODE_ENV自动切换配置文件,提升前端开发效率。
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
'process.env.APP_PREFIX': JSON.stringify(appConfig.absolutePrefix)
}),
new HtmlWebpackPlugin({ // //打包后自动生成index.html文件,并把打包生成的js自动引入到这个html文件中
template: 'assets/index.html',
templateParameters: {
appPrefix: process.env.NODE_ENV=='development'?'/':appConfig.absolutePrefix
}
}),
new CleanWebpackPlugin() // //每次打包之前都会自动将dist文件中的内容删除
],
performance: false, // 配置如何展示性能提示,false不显示提示
output: { // 打包后文件的存放地址等信息
path: path.resolve(__dirname, '../dist') // 打包后文件所存放的位置
}
}
三、webpack.dev.config.js
const webpack = require('webpack');
const merge = require('webpack-merge');
// webpack-merge: 如果配置文件被分成了许多不同的部分,
// 那么必须以某种方式来组合他们,通常就是合并数组和对象,webpack-merge就是用来合并配置文件的
const commonConfig = require('./webpack.base.config.js');
const appConfig = require('./app.config.js');
const devConfig = {
mode: 'development', // 配置是开发环境还是生产环境
devtool: 'cheap-module-eval-source-map', // 用作调试,原始源代码
devServer: {
historyApiFallback: {
index: appConfig.absolutePrefix + 'index.html'
},
open: true,
openPage: appConfig.relativePrefix,
port: 9000,
hot: true,
overlay: {
errors: true
}
},
resolve: { // 配置模块如何解析
alias: { // 别名
'react-dom': '@hot-loader/react-dom'
}
},
module: {
rules: [{
test: /\.less$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 2 // 用postcss-loaders和less-loader加载器
}
},
'postcss-loader',
'less-loader'
]
}, {
test: /\.css$/,
use: [
'style-loader',
'css-loader',
'postcss-loader'
// 使用postcss-import插件,遵循@import规则,你可以将reset.css样式
// 合并到你的主样式表中,减少http请求。
]
}]
},
plugins: [
new webpack.HotModuleReplacementPlugin() // 热更新
],
output: {
filename: '[name].[hash:8].js',
publicPath: appConfig.absolutePrefix
},
}
module.exports = merge(commonConfig, devConfig);
四、webpack.prod.config.js
const path = require('path');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
// 可以把css文件从打包后的js文件中剥离出来。减少了主bundle的页面文件大小,加快加载速度。
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
// 压缩单独的css文件
const TerserJSPlugin = require('terser-webpack-plugin');
// 压缩js
const CopyWebpackPlugin = require('copy-webpack-plugin')
// 拷贝和压缩文件
const merge = require('webpack-merge');
const appConfig = require('./app.config.js');
const commonConfig = require('./webpack.base.config.js');
const prodConfig = module.exports = {
mode: 'production',
devtool: 'cheap-module-source-map', // 用作调试
module: {
rules: [
{
test: /\.less$/,
use: [
MiniCssExtractPlugin.loader, // 可以把css文件从打包后的js文件中剥离出来
{
loader: 'css-loader',
options: {
importLoaders: 2
}
},
'postcss-loader',
'less-loader'
]
}, {
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader, // 可以把css文件从打包后的js文件中剥离出来
'css-loader',
'postcss-loader'
]
}
]
},
optimization: { // 优化
minimizer: [ // 是否进行代码压缩
new OptimizeCSSAssetsPlugin({}), // 压缩单独的css文件
new TerserJSPlugin({ // 压缩js
cache: true,
sourceMap: true,
parallel: 4
})
],
usedExports: true,
splitChunks: { // 进行代码的拆分
chunks: 'all',
automaticNameDelimiter: '.',
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10
},
commons: {
name: 'commons',
chunks: 'initial',
minChunks: 2, // //模块出现2次就会被抽离到公共模块
priority: -20,
reuseExistingChunk: true
}
}
},
runtimeChunk: { // 提取webpack的runtime代码
name: 'runtime'
}
},
plugins: [
new MiniCssExtractPlugin({ // 可以把css文件从打包后的js文件中剥离出来
filename: appConfig.relativePrefix + 'css/[name].[contenthash:8].css'
}),
new CopyWebpackPlugin([ // 拷贝和压缩文件
{
from: path.resolve(__dirname, '../static'),
to: appConfig.relativePrefix + 'static',
ignore: ['.*']
},
{
from: path.resolve(__dirname, '../styles'),
to: appConfig.relativePrefix + 'styles',
ignore: ['.*']
}
])
],
output: {
filename: appConfig.relativePrefix + 'js/[name].[chunkhash:8].js',
chunkFilename: appConfig.relativePrefix + 'js/[name].[chunkhash:8].js',
publicPath: '/'
}
};
module.exports = merge(commonConfig, prodConfig);
五、app.config.js
const absolutePrefix = '/app/demo/';
module.exports = {
absolutePrefix : absolutePrefix,
relativePrefix: absolutePrefix.substring(1)
}