前俩天刚刚搭建了一个react的开发环境,记录一下踩坑之路。

主要实现的功能有:

1、支持es6的语法
2、支持sass,css
3、支持css自动加浏览器前缀,eg:display:flex 会自动加上各个浏览器前缀
4、自动生成html模板,然后把编译好的js文件自动引入
5、 react的热更新
6、 压缩js
7、 把嵌入js代码中的css提取出来
8、 提取公共模块的代码
———-待验证的,不确定的—————————–
9、 压缩图片和字体文件

配置记录

热更新

配置过程中,耗时最长的就是在没有浏览器刷新的状态下,实现修改了文件后浏览器自刷新,也叫热加载。
因为用了一个react-hot-loader的这个作者早就不更新了,所以一直没配置成功,后来谷歌才知道,真坑,现在选择了一个新的plugin:

1
babel-plugin-react-transgorm

还要安装其他的:

1
2
react-transform-hmr 安装这个才能实现热加载
babel-preset-react-hmre 让Babel知道HMR

这俩个是为了让错误直接显示在页面上,和react-native的提醒错误的方式一样

1
2
react-transform-catch-errors
redbox-react

其他的配置还有就是,

提取公共模块

webpack用插件CommonsChunkPlugin进行打包的时候,将符合引用次数(minChunks)的模块打包到name参数的数组的第一个块里(chunk),然后数组后面的块依次打包(查找entry里的key,没有找到相关的key就生成一个空的块),最后一个块包含webpack生成的在浏览器上使用各个块的加载代码,所以页面上使用的时候最后一个块必须最先加载

css自动补全前缀的,这用了
1
2
postcss
autoprefixer

还要新建一个postcss.config.js用了这个css热更新不能用了,只有改jsx,可以使用,只有使用了,不兼容的属性才会添加前缀名

html模板创建的

配置的时候
htmlWebpackPlugin 插件名字必须这么写html小写,我刚开始写的HtmlwebpackPlugin不对
要是使用模板的话还需要新安装
ejs-loader

图片加载和压缩的 这个还需要验证

文件目录

文件目录

贴出自己的配置 webpack.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
const webpack = require('webpack');
const path = require('path');
const production = process.env.NODE_ENV === 'production' ? true : false;
const CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkplugin');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const htmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const autoprefixer = require('autoprefixer');
const CleanPlugin = require('clean-webpack-plugin');

const webpackPlugin = [
//定义全局为开发环境
new webpack.DefinePlugin({
'process.env':{
"NODE_ENV":JSON.stringify('dev')
}
}),
//在编译最终的静态资源前,清理output文件夹
// new CleanPlugin('./output/'),

new htmlWebpackPlugin({
title: production ? 'react-app' : 'dev-react-app',
date: new Date(),
template: './template/index.html',
inject: true,
filename: 'index.html',
minify:{
removeComments: production ? true : false, //生产环境中移除html中的注释
collapseWhitespace: production ? true : false, //生产环境中删除空白符与换行符
},
}),
//开启全局的模块热替换
new webpack.HotModuleReplacementPlugin(),
//当模块热替换时在浏览器控制台输出对用户更友好的模块名字信息
new webpack.NamedModulesPlugin(),
new webpack.BannerPlugin({
banner:'Autor: tiakia',
raw:false,
}),
//用于提取公共模块common是公共的模块,vender是第三方库,load是webpack打包的再各个浏览器上加载的代码,必须先加载
new CommonsChunkPlugin({
name:['common','vendor','load'],
filename: "[name].js",
minChunks: 2,
}),
//css提取
new ExtractTextPlugin({
filename:'style.css',
allChunks: true,
}),
];

if(production){
webpackPlugin.push([
//对最终的js进行 Uglify 压缩
new UglifyJsPlugin({
compress: true,
test: /\.jsx?$/i,
parallel:{ //使用多进程并行和文件换成提高构建速度
cache: true,
workers:2,
},
warnings: false,
}),
])
}
module.exports = {
devtool: production ? "eval" : "cheap-module-eval-source-map",
entry:{
main:[

'webpack-dev-server/client?http://localhost:9000',
'webpack/hot/only-dev-server',
'./src/index.js',
],
//第三方库
vendor:['react','react-dom','redux','react-redux'],
},
output:{
path:__dirname + '/output/',
filename: '[name].bundle.js',
publicPath: '',
},
module:{
loaders:[
{
test: /\.jsx?$/,
exclude: /(node_modules|bower_components)/,
loader:'babel-loader',
query:{
presets:['react','es2015']
}
},
{
test:/\.css$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: ['css-loader', 'postcss-loader'],
publicPath: './output/'
})
},
{
// scss 加载
test: /\.scss$/i,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use:["css-loader","postcss-loader","sass-loader"],
publicPath: './output/'
})
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
//loader: 'url-loader',
// options: {
// limit: 10000, // 10k
// name: image/[name]-[hash:5].[ext]
// },
// loaders:[
// 'url-loader?limit=10000&name=image/[name]-[hash:5].[ext]',
// 'image-webpack-loader'
// ],
use:[
{
loader: 'url-loader',
options: {
limit: 10000,
name: 'image/[name]-[hash:5].[ext]',
},
},
'image-webpack-loader'
]
},
{
test: /\.(woff|ttf|eot)$/,
loader: 'url-loader',
query:{
limit: 20480,//20k
},
},
]
},
plugins: webpackPlugin,
devServer:{
// contentBase: [path.join(__dirname)],
historyApiFallback: true,
// compress: true,
hot: true,
port: 9000,
inline: true,
publicPath: '/', //和output 的‘publicPath'一致
// progress: true,
},

}
postcss.config.js
1
2
3
4
5
module.exports = {
plugins:[
require('autoprefixer'),
]
}
.babelrc
1
2
3
4
5
6
7
8
{
presets:["es2015","react"],
"env":{
"development":{
"presets":["react-hmre"]
}
}
}
package.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
{
"name": "reduxDemo",
"version": "1.0.0",
"main": "index.js",
"author": "tiakia <www.tiankai.party>",
"license": "MIT",
"dependencies": {
"babel-loader": "^7.1.2",
"babel-preset-es2015": "^6.24.1",
"babel-preset-react": "^6.24.1",
"bable-loader": "^0.0.1-security",
"css-loader": "^0.28.5",
"extract-text-webpack-plugin": "^2.1.2",
"html-webpack-plugin": "^2.30.1",
"react": "^15.6.1",
"react-dom": "^15.6.1",
"react-redux": "^5.0.6",
"react-router": "^4.1.2",
"redux": "^3.7.2",
"redux-thunk": "^2.2.0",
"sass-loader": "^6.0.6",
"style-loader": "^0.18.2",
"uglify-js-plugin": "^0.0.6",
"uglifyjs-webpack-plugin": "^0.4.6",
"webpack": "^3.5.5",
"webpack-dev-server": "^2.7.1"
},
"devDependencies": {
"autoprefixer": "^7.1.2",
"autoprefixer-loader": "^3.2.0",
"babel-core": "^6.26.0",
"babel-plugin-react-transform": "^2.0.2",
"babel-polyfill": "^6.26.0",
"babel-preset-react-hmre": "^1.1.1",
"clean-webpack-plugin": "^0.1.16",
"cross-env": "^5.0.5",
"extract-text-webpack-plugin": "^2.1.2",
"file-loader": "^0.11.2",
"html-webpack-plugin": "^2.30.1",
"image-webpack-loader": "^3.3.1",
"img-loader": "^2.0.0",
"node-sass": "^4.5.3",
"postcss-loader": "^2.0.6",
"react-transform-catch-errors": "^1.0.2",
"react-transform-hmr": "^1.0.4",
"redbox-react": "^1.5.0",
"url-loader": "^0.5.9",
"webpack-dev-server": "^2.7.1"
},
"scripts": {
"dev": "cross-env NODE_ENV=dev && webpack-dev-server --open --progress --colors --config ./webpack.config.js",
"build": "webpack --config webpack.config.js",
"deploy": "cross-env NODE_ENV=production && webpack --config ./webpack.config.js"
}
}

template/index.html

1
2
3
4
5
6
7
8
9
10
11
12
<!DOCType html>
<html>
<head>
<meta charset='utf-8'/>
<title><%= htmlWebpackPlugin.options.title %></title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<div>页面生成时间:<%= htmlWebpackPlugin.options.date %></div>
<div id='root'></div>
</body>
</html>

再次申明,图片压缩和字体的没有验证,仅供参考,有错误的地方欢迎及时联系我,有交流才有进步。