前言
这是webpack学习笔记系列的第四篇文章,前文之路:
- webpack学习笔记(一):初次见面,请多指教
- webpack学习笔记(二):基本属性管理
- webpack学习笔记(三):环境配置 通过前面三篇文章,基本上可以从无到有的搭建一个 webpack 工程的开发环境。在这篇文章中,我们使用 webpack 来代替 react 脚手架搭建 react 项目。
webpack5+react配置过程
本文的配置不基于以前的任何一个工程,而是重新建立了一个工程文件。开发过程中采用的服务器为webpack-dev-server服务器。本文实现了react+webpack5的基础工程。
初始化项目
- 新建一个工程文件夹,并且将终端的目录切换到工程文件夹之下
- 运行
npm init
命令,生成package.json文件 - 安装 webpack、webpack-cli、webpack-dev-server,用于搭建webpack开发环境
npm install --save-dev webpack webpack-cli webpack-dev-server
- 安装支持css文件的依赖包
// 根据需求安装这些依赖包
npm install --save-dev css-loader sass-loader
安装 react 和react-dom
使用npm
命令,安装 react 和 react-dom 包
npm install --save react react-dom
安装 babel 相关依赖包
安装 babel-loader @babel/core @babel/preset-env @babel/preset-react
npm install --save-dev babel-loader @babel/core @babel/preset-env @babel/preset-react
安装开发中用到的插件
安装生成html文件的插件、打开浏览器的插件、从bundle中提取文本到单独文件的插件、清楚打包目录文件的插件
npm install --save-dev html-webpack-plugin open-browser-webpack4-plugin mini-css-extract-plugin happypack
添加配置文件
本文的环境是开发环境和生产环境分离的,因此,webpack相关的配置文件有三个。
- 安装 webpack-merge
npm intall --save-dev webpack-merge
- 根目录下添加三个文件,分别编写三个配置文件
- webpack.base.js
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const HappyPack = require('happypack')
const os = require('os')
const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length })
function concatPath(relatedPath) {
return path.join(__dirname, relatedPath)
}
const webpackConfBase = {
mode: "development",
entry: concatPath('../app/app.jsx'),
output: {
path: concatPath('../dist'),
filename: '[name].[fullhash:4].js',
chunkFilename: 'chunks/[name].[fullhash:4].js',
},
module: {
rules: [
{
test: /\.js[x]?$/,
use: 'babel-loader'
},
{
test: /\.(sc|sa|c)ss$/,
use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader']
}
]
},
plugins: [
new HappyPack({
//用id来标识 happypack处理那里类文件
id: 'happyBabel',
//如何处理 用法和loader 的配置一样
loaders: [{
loader: 'babel-loader',
}],
//代表共享进程池,即多个 HappyPack 实例都使用同一个共享进程池中的子进程去处理任务,以防止资源占用过多。
threadPool: happyThreadPool,
//允许 HappyPack 输出日志
verbose: true,
}),
new HappyPack({
//用id来标识 happypack处理那里类文件
id: 'happyStyle',
//如何处理 用法和loader 的配置一样
loaders: [MiniCssExtractPlugin.loader, 'css-loader?sourceMap=true', 'sass-loader?sourceMap=true'],
//代表共享进程池,即多个 HappyPack 实例都使用同一个共享进程池中的子进程去处理任务,以防止资源占用过多。
threadPool: happyThreadPool,
//允许 HappyPack 输出日志
verbose: true,
}),
// 从 bundle 中提取文本(CSS)到单独的文件
new MiniCssExtractPlugin({
// 配置项与 webpackOptions.output 相同
// 所有配置均是可选项
filename: '[name].[fullhash:4].css',
chunkFilename: '[id].[fullhash:4].css',
}),
],
}
module.exports = webpackConfBase;
- webpack.dev.js
const path = require('path');
const webpack = require('webpack');
const { merge } = require('webpack-merge');
const webpackConfigBase = require('./webpack.base.config');
// open-browser-webpack-plugin目前不知道webpack4+,因此选用了open-browser-webpack4-plugin
const OpenBrowserPlugin = require('open-browser-webpack4-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const PORT = 8888;
function concatPath(relatedPath) {
return path.join(__dirname, relatedPath);
}
const webpackConfigDev = {
plugins: [
// 定义环境变量为开发环境
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('development'),
IS_DEVELOPMETN: true,
}),
// 将打包后的资源注入到html文件内
new HtmlWebpackPlugin({
inject: 'body',
title: 'React APP',
filename: 'index.html',
template: concatPath('../app/index.html')
}),
new OpenBrowserPlugin({
url: `http://localhost:${PORT}`,
}),
],
devtool: 'source-map',
devServer: {
contentBase: concatPath('../app'),
historyApiFallback: false,
hot: false,
host: '0.0.0.0',
port: PORT,
},
}
module.exports = merge(webpackConfigBase, webpackConfigDev);
- webpack.prod.js
const webpack = require('webpack');
const path = require('path');
const { merge } = require('webpack-merge');
const webpackConfigBase = require('./webpack.base.config');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
// 用于将打包目录中上一次打包的文件清除
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
function concatPath(relatedPath) {
return path.join(__dirname, relatedPath)
}
const webpackConfigProd = {
mode: "production",
// 提取 chunks 之间共享的通用模块
optimization: {
runtimeChunk: {
name: 'manifest'
},
minimizer: [], // [new UglifyJsPlugin({...})]
splitChunks:{
chunks: 'async',
minSize: 30000,
minChunks: 1,
maxAsyncRequests: 5,
maxInitialRequests: 3,
name: false,
cacheGroups: {
vendor: {
name: 'vendor',
chunks: 'initial',
priority: -10,
reuseExistingChunk: false,
test: /node_modules\/(.*)\.js/
},
styles: {
name: 'styles',
test: /\.(scss|css)$/,
chunks: 'all',
minChunks: 1,
reuseExistingChunk: true,
enforce: true
}
}
}
},
plugins: [
// 定义环境变量为开发环境
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production'),
IS_DEVELOPMETN: false,
}),
// 将打包后的资源注入到html文件内
new HtmlWebpackPlugin({
inject: 'body',
title: 'React APP',
filename: 'index.html',
template: concatPath('../app/index.html')
}),
// 分析代码
new BundleAnalyzerPlugin({ analyzerPort: 3011 }),
new CleanWebpackPlugin({
protectWebpackAssets: true,
}),
],
}
module.exports = merge(webpackConfigBase, webpackConfigProd)
- 创建
.babelrc
文件来进行babel相关配置
{
"compact": false,
"presets": [
"@babel/preset-env", // babel 启动插件
"@babel/preset-react" // 编译react语法插件
],
"plugins": [
"@babel/transform-runtime"
]
}
添加scripts命令
"scripts": {
"dev": "webpack-dev-server --config webpack.dev.js",
"build": "webpack --config webpack.prod.js"
}
前方敲黑板,划重点
一定要注意babel-loader的版本问题,小编表示在此处踩坑踩了很久:
默认安装的是babel 8.x版本的,和这个版本配套的是@babel/core @babel/preset-env @babel/preset-react这些安装包
而babel 7.x之前的版本bable-core和babel-preset-env等。