说人话就是我这个是用来回馈社会的,一直用的create-react-app但是每次有啥特殊需求就可能需要eject,让我觉得很不爽,因为我就是不想eject。
写这个主要是两个原因, 第一个是因为网上关于react + webpack的配置真的很杂,而且有的是老版本的,几篇文章看下来都不知道应该用哪个,越配越杂,我这个写出来就是为了让新手少走弯路,因为我就是一直在走弯路,我这个就是2020年最新版的。
第二个原因呢就是一直用creat-react-app然后里面打包出来的分包啊,都很好用,但是网上的react+ webpack 都是最基本的, 我就想做一个能满足基本需求并且打包出来长得像create-react-app的打包效果的东西。
- 首先是创建一个空文件夹,然后通过cmd输入 :
npm init
- 然后一直按回车,这个时候文件夹就会出现一个package.json的文件,里面一些都是关于你个人和项目基本信息, 有了这个package.json 文件,你下载的那些依赖包就会添加到package.json里面。然后根据个人爱好选择npm或者yarn来作为你的包安装器,我这里就用yarn为例子作为包下载器,在cmd中输入:
yarn add -D path webpack webpack-cli webpack-merge webpack-dev-server html-webpack-plugin mini-css-extract-plugin rimraf
- path是文件路径工具; webpack和webpack-cli是你打包react需要的工具; webpack-merge 是你整合webpack基础配置和dev/production的单独配置所需要的依赖包,这个是可选项,你可以吧自己的所有config写在一个文件中,然后通过环境判断当前属于development还是production; webpack-dev-server是本地测试的服务器,里面有自动刷新等功能; html-webpack-plugin 是用来 生成一个index.html模板,以后所有打包的js,css等文件都会attach到这个模板上; mini-css-extract-plugin 是用来分离打包css,然后css和js在客户发出request的时候是可以并行的,就是打包成两个文件,你可以加快效率返回文件,当然前提是多一个http request. 你不用他也行,但是我为了能够和create-react-app一样所有的css放在一个包内,我就用了他; rimraf 存在的意义就是当我打包的时候我想吧原来在dist目录下的dev或者debug的文件全部删了然后保持一个最新的 继续cmd输入:
yarn add -D @babel/core @babel/preset-env @babel/preset-react babel-loader
- 这个是通过babel这个编译器把react代码转移成ES5常规代码,然后在项目根目录下创建.babelrc文件,文件内容配置,这些配置就是为了能够让babel去编译react代码
{
"presets": [
"@babel/preset-env",
"@babel/preset-react"
]
}

- 然后是加入各种loader, 我这里只是放了几个基本的,你要需要其他的,可以自己去webpack上找loader然后自己加就行了
yarn add -D url-loader file-loader style-loader css-loader
- 现在我们要开始配置webpack里,在根目录下创建一个config目录,里面存放webpack配置文件们。 进入config目录,创建一个webpack.base.conf.js 文件,作为production和development的公用配置
const path = require('path');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const HtmlWebpackPlugin = require('html-webpack-plugin'); // 生成html模板
const resolve = (dir) => path.join(__dirname, '..', dir); //写一个路径解析的功能,给后面的路径调用
module.exports = {
entry: {
app: resolve("/src/index.js") //webpack 的文件入口,获取寻找这个文件然后开始需要所有的dependencies
},
output: {
path: resolve("dist/static"), //打包后输出的目录,意思就是在根目录下有一个dist目录,下面有一个static目录
// path: path.join(__dirname,"../../src/main/resources/static/static"), //直接写入springboot resource下面的static 目录下
filename: "js/bundle.[hash].js", //打包后的输出文件名, output中的path和filename联合起来就是文件打包地址 : react-app-root-folder/dist/static/js/bundle.ha23j3kdf3kso3.js
publicPath: "/static" // 这个是用来路径映射,dist 目录作为打包后的资源总目录,我们希望需要他下面的/static/js/bundle.ha23j3kdf3kso3.js, 如果不放这个publicPath,那么在打包后的index.html script路径就会变成 /js/bundle.ha23j3kdf3kso3.js
},
module: {
rules: [
// 使用Eslint才需要配置这个
// {
// test: /\.(jsx|js)$/,
// enforce: 'pre',
// include: resolve('src'),
// exclude:[
// resolve('node_modules')
// ],
// loader: 'eslint-loader',
// },
{
test: /\.(jsx|js)$/, //所有以jsx或者js结尾的文件
include: resolve('src'), //包括当前项目下的src,其他的目录不管,主要目的是为了避开node_modules,因为nod_modules下面全是js文件,我们不想编译那些文件
loader: 'babel-loader', // 采用何种编译器去编译上面限制的这些文件, 总而言之就是 使用babel-loader去编译src目录下带有jsx或者js结尾的文件
},
{
test: /\.css$/, //所有以css结尾的文件,
use: [
"style-loader",
MiniCssExtractPlugin.loader,
"css-loader",
],
include: resolve('src')
},
{
test: /\.(eot|woff2?|ttf|svg)$/,
use: [
{
loader: "url-loader",
options: {
name: "[name]-[hash:5].min.[ext]",
limit: 5000, // 当文件小于5000的时候会吧文件转成dataUrl,如果大于就是直接使用file-loader的那一套了
outputPath: 'media/'
// publicPath: "fonts/",
// outputPath: "assets/fonts"
}
}
]
},
{
test: /\.(jpg|svg|png|gif|pdf)$/,
use: {
loader: "file-loader",
options: {
name: '[name].[ext]',
outputPath: 'media/'
}
}
}
]
},
resolve:{
extensions:['.js','.jsx','.json','.css'] //这个就是当你引用其他的组件或者文件的时候不需要输入后缀webpack也能识别的功能,你不用这个,然后每次在code里面引入外部组件的时候记得加后缀也可以的
},
plugins: [
new HtmlWebpackPlugin({
filename: resolve("/dist/index.html"), // index。html 模板生成的地址
// filename: path.join(__dirname,"../../src/main/resources/static/index.html"), //如果直接build到spring下
template: resolve("/src/index.html"), // 模板 的源文件 地址
}),
new MiniCssExtractPlugin({
filename: "css/[name].[hash].css", //css文件分离打包的地址和文件名
chunkFilename: "css/[id].[hash].css" //当css文件特别大的时候可以把一个包打成多个小包
}),
]
};
- 再接着我们在config这个目录下再创建一个webpack.dev.conf.js
const path = require('path');
const webpack = require('webpack');
const webpackMerge = require("webpack-merge"); //用来合并在webpack.base.conf.js定义的配置
const baseConfig = require("./webpack.base.conf");
const resolve = (dir) => path.join(__dirname, '..', dir);
module.exports = webpackMerge(baseConfig, {
mode: "development",
devtool: 'cheap-module-eval-source-map', //这个是为了debug好定位code出错在哪一行设置的,有其他的选项我不细讲,网上好多
devServer: {
contentBase: resolve("dist"), // 指定dev-server去哪个目录下读取编译的文件
// contentBase: path.join(__dirname,"../../src/main/resources/static"), //如果base config直接build 到springboot resource下面的static 目录下, 那么dev直接去那里读取
port: 3000, //开一个port 3000的端口,这个端口可以自定义,我跟随这create-react-app的节奏走,就定义的3000
// open: true, //开启dev server之后会自动开启浏览器,但是第一次compile之后写入dist目录需要一段时间,所以一般是不自动开启
hot: true, //打开hot就可以局部更新并且临时状态保存,但是单独的css文件更新会捕捉不到
writeToDisk: true, //dev server开启自动编译文件然后编译文件存在于内存中,但是我们指定了读取位置contentBase实际存储地址,所以内存中地址和实际存储地址不同,所以需要把内存中文件写入磁盘就可以
inline: true, //实时刷新
watchContentBase:true, //一直看着我们指定的contentBase是否有变化,有变化就刷新,主要是要解决css文件更新后没有自动刷新浏览器的问题,如果哪位大神有答案我就可以修改修改
publicPath: '/static', //要保持和webpack.base.conf.js 里面配置的publicPath一直,因为dev-serve存在的意义就是为了模拟服务器读取打包的文件
historyApiFallback: {
index: '/index.html' // 当request路径找不到或者不存在的时候就会去读取index.html
}
},
plugins: [
new webpack.HotModuleReplacementPlugin() //启动热更替
]
});
- 再然后创建一个webpack.prod.conf.js
const baseConf = require("./webpack.base.conf");
const webpackMerge = require("webpack-merge");
module.exports = webpackMerge(baseConf, {
mode: 'production'
});
- 三个webpack的配置文件就配置好了,dev就是本地测试的时候跑的,prod就是你要打包上传的时候跑的,现在就是进入正题了,我们需要导入我们的react包,我这里多放了个react-router-dom, 你们不想要就不要加,顺便提一句前面所有的add都有加-D 是把它们放到dev环境,打包不会把这些包编译进去
yarn add react react-dom react-router-dom
- 这个时候基本上已经可以开始玩了, 你接着在根目录下创建src的目录,然后在下面新建一个index.js文件,这个是webpack去打包的入口,必须叫这个名字,因为你webpack里面是这么配的,不然的话你自己可以去改webpack里面的entry, 接着在src目录下写一个index.html 的模板页, 当然名字不能改,要改就是webpack和你的html名字一起改。
- 写好了组件后,我们一起去package.json文件,替换一下它里面的scripts 代码
"scripts": {
"clear": "rimraf dist",
"build": "npm run clear && webpack --config ./config/webpack.prod.conf.js",
"start": "webpack-dev-server --config ./config/webpack.dev.conf.js"
},
- 剩下的我也没什么好说的里,就在src目录下面各种建立component和你的组件。写好了之后就直接npm run start了。 这个时候你应该会在根目录下产生一个dist目录,然后目录下面的结构跟我的图类似

- 自此也算是基本的react和webpack完成了,当然我没有加入eslint检查,如果这篇文章有人喜欢的话,我下一张就讲讲如何整合eslint和mobx好了