用生命谱写代码的赞歌

0%

升级 Webpack2 爬坑之旅

Webpack1 升级 Webpack2

Webpack1 相比, Webpack2 拥有更强大的功能以及更简便的配置,是时候升级一波了。

下面就简要介绍我在升级过程中遇到的一些坑。o(╯□╰)o

entry 入口

webpack1 devServer 配置双服务器热替换模式

1
2
3
4
5
entry:[
'webpack/hot/dev-server', // 配合 HMR 实现模块热加载
'webpack-dev-server/client?http://localhost:8080', //资源服务器地址
path.resolve(__dirname,'src/js/app.js')
]

webpack2 ,其中 context 指定了开发环境项目入口文件的目录

1
2
3
4
context: __dirname + '/src', // 必须指定开发环境目录
entry: {
app: './index.jsx'
},

module

webpack1 采用 loaders 属性加载模块, preLoaders 提前加载。

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
module: {
preLoaders: [
{ test: /\.js$/, loader: "eslint-loader", exclude: /node_modules/ }
],
loaders: [
// 处理jsx语法和ES6语法,加载器可以省略后缀
{
test: /\.jsx?$/, // 用正则来匹配文件路径,这段意思是匹配 js 或者 jsx
loader: 'babel',// 加载模块 "babel" 是 "babel-loader" 的缩写
},
// 使用 eslint 检查 js 文件
// {
// test: /\.js$/,
// loader: "eslint",
// exclude: /node_modules/
// },
// 处理在 js中引用的 css文件
{
test: /\.css$/, // Only .css files
loader: 'style!css' // 如果有多个加载器,中间作用感叹号隔开,多个加载器是从右往左去执行
},
// {
// test: /\.css$/,
// loader: 'style!css',
// include: /node_modules/,
// loader: 'style!css?modules&localIdentName=[name]__[local]-[hash:base64:5]'
// },
{
test: /\.scss$/,
loader: 'style!css!sass'
},
// 1kb=1024b 1b=8bit 25000bit~3kb
{
test: /\.(png|jpg|jpeg|gif)$/,
loader: 'url?limit=25000'
},
{
test: /\.(eot|woff|ttf|woff2|svg)$/,
loader: 'url'
}

]
},

webpack2 使用 rules 加载模块,在内部使用 enfore: 'pre' 提前检测 js 文件,而且目前还兼容 webpack1loader 写法。

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
module: {
rules: [
{
test: /\.js$/,
enforce: 'pre', // 在babel-loader对源码进行编译前进行lint的检查
include: /src/, // src文件夹下的文件需要被lint
use: [{
loader: 'eslint-loader',
options: {
formatter: require('eslint-friendly-formatter') // 编译后错误报告格式
}
}]
// exclude: /node_modules/ 可以不用定义这个字段的属性值,eslint会自动忽略 node_modules 和 bower_components
},
{
test: /\.jsx?$/,
exclude: /node_modules/,
loaders: [
'babel-loader',
'eslint-loader'
]
},

{
test: /\.css$/,
loaders: [
'style-loader',
'css-loader'
]
},
{
test: /\.scss$/,
use: [
'style-loader',
'css-loader',
'sass-loader'
]
},
{
test: /\.(png|jpg|jpeg|gif)$/,
loader: 'url-loader?limit=25000'
},
{
test: /\.(eot|woff|ttf|woff2|svg)$/,
loader: 'url-loader?limit=50000'
}
]
},

resolve

webpack1

1
2
3
resolve: {
extensions: ['', '.js', '.json', '.scss', '.jsx'] // 第一项为空,对应不需要后缀的情况
},

webpack2

1
2
3
4
resolve: {
extensions: ['.js', '.json', '.scss', '.jsx'], // 第一项不能为空
enforceExtension: false // 对应 webpack1后缀名为空选项
},

webpack2 生产环境配置遇到的坑

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
155
156
157
158
159
var pkg = require('./package.json')
var path = require('path')
var webpack = require('webpack')
var HtmlWebpackPlugin = require('html-webpack-plugin')
var ExtractTextPlugin = require('extract-text-webpack-plugin')
// css 文件抽取配置项
var ExtractCSS = new ExtractTextPlugin({
filename: "/css/[name].[contenthash].css",
disable: process.env.NODE_ENV === "dev"
})
// less文件抽取配置项
var ExtractLess = new ExtractTextPlugin({
filename: "/css/[name].[contenthash].css",
disable: process.env.NODE_ENV === "dev"
})

module.exports = {
entry: {
app: path.resolve(__dirname, 'app/index.jsx'),
// 将 第三方依赖(node_modules中的) 单独打包
vendor: Object.keys(pkg.dependencies)
},
output: {
path: __dirname + "/build",
filename: "js/[name].[chunkhash:8].js"
},
resolve: {
extensions: ['.js', '.jsx']
},
module: {
rules: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
loader: 'babel-loader'
},
{
test: /\.css$/,
exclude: /node_modules/,
use: ExtractCSS.extract({
fallback: 'style-loader',
use: [
{ loader: 'css-loader', options: { importLoaders: 1 } },
{
loader: 'postcss-loader', // 加载 postcss-loader的第一种方式
options: {
plugins: () => [
require('autoprefixer')() // 调用autoprefixer插件,例如display: flex
]
}
}
]
})
},
{
test: /\.less$/,
exclude: /node_modules/,
use: ExtractLess.extract({
use: [
{ loader: "css-loader" },
{
loader: 'postcss-loader',
options: {
plugins: () => [
require('autoprefixer')() // 调用autoprefixer插件,例如display: flex
]
}
},
{ loader: "less-loader" }
],
// use style-loader in development
fallback: "style-loader"
})
},
{
test: /\.(png|gif|jpg|jpeg|bmp)$/i,
use: [
{
loader: 'url-loader',
options: {
limit: 5000,
name: 'images/[name].[hash:8].[ext]' // 使用 chunkhash报错,因为图片不是chunk片段
}
}
]
},
{
test: /\.(png|woff|woff2|svg|ttf|eot)($|\?)/i,
use: [
{
loader: 'url-loader',
options: {
limit: 5000,
name: 'fonts/[name].[hash:8].[ext]' // 不能使用 chunkhash
}
}
]
}
]
},
plugins: [
// webpack1 迁移 webpack2,postcss-loader配置,加载 postcss-loader的第二种方式
// https://webpack.js.org/guides/migrating/#complex-options
/* new webpack.LoaderOptionsPlugin({
options: {
postcss: function () {
return [
require("autoprefixer")({
browsers: ['ie>=8', '>1% in CN']
})
]
}
}
}), */
// webpack 内置的 banner-plugin
new webpack.BannerPlugin("Copyright hushiking@github.com."),

// html 模板插件
new HtmlWebpackPlugin({
template: __dirname + '/app/template.html'
}),

// 定义为生产环境,编译 React 时压缩到最小
new webpack.DefinePlugin({
'process.env': {
// 'NODE_ENV': JSON.stringify(process.env.NODE_ENV)
'NODE_ENV': JSON.stringify('production')
}
}),

// 为组件分配ID,通过这个插件webpack可以分析和优先考虑使用最多的模块,并为它们分配最小的ID
// new webpack.optimize.OccurenceOrderPlugin(),

new webpack.optimize.UglifyJsPlugin({
compress: {
//supresses warnings, usually from module minification
warnings: false
}
}),

// 如果没有 less文件,可以单独抽取 css文件
// new ExtractTextPlugin('/css/[name].[chunkhash:8].css'),

// 有 less文件则分别抽取 css与 less文件到不同文件
ExtractCSS,
ExtractLess,

// 提供公共代码
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
filename: '/js/[name].[chunkhash:8].js'
}),

// 可在业务 js 代码中使用 __DEV__ 判断是否是dev模式(dev模式下可以提示错误、测试报告等, production模式不提示)
new webpack.DefinePlugin({
__DEV__: JSON.stringify(JSON.parse((process.env.NODE_ENV == 'dev') || 'false'))
})
]
}

另外, postcss-loader 还有第三种配置方式,即引用外部 postcss.config.js 配置文件:

1
2
3
4
5
module.exports = {
plugins: [
require('autoprefixer')
]
}

webpack.production.js 文件无需其它设置,直接使用 postcss-loader

1
2
3
4
5
6
7
8
9
10
11
{
test: /\.css$/,
exclude: /node_modules/,
use: ExtractCSS.extract({
fallback: 'style-loader',
use: [
{ loader: 'css-loader' },
{ loader: 'postcss-loader' }
]
})
},