转眼间,时间来到了2018年的四月,webpack也在前不久发布了它的 4.0 版本,而在写这篇文章时,它的版本已然更新到 4.5 了。似乎永远也赶不上它们的脚步,果然,这世界上不变的,永远是变化。

来看看 webpack4.x 有哪些变化。

基本

webpack && webpack-cli

貌似从 4.0 开始,webpack 拆分成 webpack 和 webpack-cli,如果只安装 webpack,会有报错提示:

cnpm i -g webpack@4.5.0 webpack-cli

全局安装后,还要本地安装。否者,当你结合插件运行时,提示如下错误:

> webpack-dev-server -o --mode development

module.js:549
throw err;
^

Error: Cannot find module 'webpack'

这说明需要本地安装 webpack。

cnpm i -D webpack@4.5.0 webpack-cli

当使用 npm i ... 安装出现失败时,尝试着使用 cnpm i ...

升级 node 和 npm 版本

当尝试安装最新版本的webpack时,提示node版本过低,于是先升级,在写本篇文章时,node的LTS版本为 8.11.1

$ cnpm i webpack webpack-cli -D

WARN node unsupported "node@v6.10.3" is incompatible with webpack@*, expected node@>=6.11.5

WARN node unsupported "node@v6.10.3" is incompatible with webpack-cli@*, expected node@>=6.11.5

WARN node unsupported "node@v6.10.3" is incompatible with webpack@4.5.0 › enhanced-resolve@^4.0.0, expected node@>=6.11.5

无需配置入口和出口文件

由于在webpack4之前,需要手动指定入口文件和输出文件,这用起来还是比较麻烦。终于,在webpack4 中,当你未配置上述文件时,它会自动查找 ./src/index.js 这个目录的文件,并且把它作为入口文件。同时,将 ./dist/main.js 作为默认的输出文件。

执行 npm run server 命令后,将会在 dist 目录下生成 main.js 文件,该文件是未压缩的,并且它的代码是通过 eval 函数将源文件中 index.js 内容进行动态运行。

而运行 npm run build,则在 dist 目录生成的 main.js 是压缩的,并且它的代码是一个自执行的匿名函数。

配置 mode 选项

当运行 npm run dev 时,会出现如下错误:

The 'mode' option has not been set, webpack will fallback to 'production' for th         is value. Set 'mode' option to 'development' or 'production' to enable defaults          for each environment.

从4.0开始,webpack 新增了 mode 配置,它包含 development | production 这两个值,对于不同的模式,webpack会采用对应的优化打包策略。

如果不指定对应的 mode,则会出现上面的警告信息,并且将 mode 的值默认指定为 production,这也意味着,process.env.NODE_ENV 的值不用再单独定义了。为了根据环境灵活打包,我们得根据文档来进行一下配置,更新 package.json 如下:

// package.json

...
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "webpack --mode development",
"build": "webpack --mode production"
},
...

或者在 webpack.config.js 文件中,单独设置:

// webpack.config.js

module.exports = {

mode: 'development' // or 'production'

...
}

optimization

如果你没在 webpack.config.js 中配置 optimization 属性,则默认情况下,webpack会根据 mode 配置,打包时采用对应的 optimization 策略。

其中,--mode development 模式下有以下特点:

  • 浏览器调试,提供有效错误信息
  • 加速增量编译

--mode production

  • 压缩输出JS文件的体积(默认使用 uglifyjs-webpack-plugin)
  • 优化执行效率

如果你在 webpack.config.js 配置了 optimization 属性,则打包时,可能会导致这些默认策略失效,因为一些 optimization 的一些默认属性被覆盖了。

另外,倘若你需要配置多个压缩插件,可在里面使用 minimizer,但要注意到是,minimizer 会覆盖 minimize 的属性值。

// webpack.config.js

module.exports = {

optimization: {
minimize: true, // 无效
minimizer: [
new OptimizeCSSAssetsPlugin({})
]
}
}

支持 JSON 文件的编译

在之前,webpack只能解析 js 文件,其他文件都需要特定的 loader 才能进行解析。但从 webpack 4.x 开始,它执行 json文件 的解析。因此,你可以在代码中,直接引用 json 文件了。

// index.js

import data from './data';

loader 被移除

webpack4.x 之前,我们可以在 loaders 中配置不同的文件解析包。 但在这之后,官网废弃了 loaders,取而代之的是使用 rules

// webpack.config.js

module.exports = {

module: {
// loaders: [
// {
// test: /\.css$/,
// loader: 'style-loader!css-loader'
// },
// ]

rules: [
{
test: /\.css$/,
use: [
'style-loader',
'css-loader'
]
}
]
}
}

CommonsChunkPlugin 被移除

webpack4.x 之前,如果需要在页面引入第三方库,并且防止模块重复引用第三方库,可以像下面这样使用 CommonsChunkPlugin 进行引入:

// webpack.config.js

module.exports = {
entry: {
zepto: ['n-zepto']
}

plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: ['zepto']
})
]
}

如果你基于 webpack4.x 再这样进行打包,则会报错:

因为从这个版本开始,移除了 CommonsChunkPlugin,现在官网推荐使用 optimization.splitChunksoptimization.runtimeChunk

optimization: {
splitChunks: {
chunks: 'all',
name: 'common',
},
runtimeChunk: {
name: 'runtime',
}
}

将文本(样式)分割成独立一个文件

之前将样式独立成文件时,都是使用 ExtractTextPlugin

npm i -D extract-text-webpack-plugin
// webpack.config.js
const ExtractTextPlugin = require('extract-text-webpack-plugin');

module: {
rules: [
{
test: /\.less$/i,
use: ExtractTextPlugin.extract([ 'css-loader', 'less-loader' ])
}
]
}

plugins: [
new ExtractTextPlugin('css/style.[chunkhash].css'),
],

但到了 webpack 4.x 时,却报错了:

 throw new Error(
^

Error: Chunk.entrypoints: Use Chunks.groupsIterable and filter by instanceof Entrypoint instead
at Chunk.get (G:\wampSever\my-work\test\webpack4.x\node_modules\_webpack@4.6.0@webpack\lib\Chunk.js:528:9)

官方的说法是暂时没有针对 webpack >= v4.0.0 的版本做更新处理,建议使用其他方案或者等待更新。后面有人提出以下解决方案:

npm i -D extract-text-webpack-plugin@next

不过注意,到目前为止,它还是一个 beta 版本,可能存在不稳定。

过了一段时间后,又有人指出,对于 extract-text-webpack-plugin 支持 webpack >= v4.0.0 的工作已经迁移到 mini-css-extract-plugin 这个项目了。并且,对于生成环境,如果你希望将样式进行压缩,你需要再单独使用 optimize-css-assets-webpack-plugin

npm i -D mini-css-extract-plugin optimize-css-assets-webpack-plugin
// webpack.config.js

const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");

module.exports = {

module: {
rules: [
...
{
test: /\.less$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'less-loader',
]
}
]
},

plugins: [
new MiniCssExtractPlugin({
filename: "[name].[hash].css"
}),
new OptimizeCSSAssetsPlugin({})
]

}

其他错误

getaddrinfo ENOTFOUND localhost

events.js:183
throw er; // Unhandled 'error' event
^

Error: getaddrinfo ENOTFOUND localhost
at errnoException (dns.js:50:10)

在 MAC 上会出现此类错误,是因为没有指定 localhost。可通过切换host的工具进行以下指定:

127.0.0.1 localhost

启动服务端口号被占用

当使用 webpack-dev-server --open --port 8100 --mode development 启动服务时,出现如下错误,即我们指定的端口号被占用。后来发现,之前通过 webpack-dev-server 启动的服务,端口一直是自增的,而且在命令行中使用 ctrl + c 无法关闭之前的服务。所以,才会导致端口号被占用的情况。

events.js:183
throw er; // Unhandled 'error' event
^

Error: listen EADDRINUSE 127.0.0.1:8100

有两种解决方案。

方案一: 使用 tskill node 干掉所有的node进程,这种方法比较简单粗暴。
方案二: 根据服务端口号,找到对应的进程端口(PID),然后,将这个进程干掉。

// 通过服务端口号,找到 进程端口(假设服务端口为 8100)
netstat -aon | findstr 8100
// 杀掉进程(假设对应的进程端口为 32120)
taskkill /f -pid 32120

经过查找各方资料,种种尝试。当我使用系统自带的命令窗口去启动和关闭服务时,发现不会复现上述问题。这就表明在系统自带命令窗口中使用 ctrl + c 可以干掉进程。

当我使用 vscode 集成的终端来启动和关闭服务,也不会复现上述问题。

由于我之前都是基于 git-bash 进行命令编译的,这说明问题就出在 git-bash,查看当前使用的 git 版本为 2.13.2

晚上回去试了家里的 git-bash,也不会复现上述的问题,查看 git 版本为 2.8.2。再次证明,这是 git 版本的问题。

相关资料:http server doesn’t shut down with Ctrl-C in Windows Git Bash , Capturing Ctrl-C in MINGW-Bash shell

npm 找不到 node-gyp 模块

npm ERR! code MODULE_NOT_FOUND
npm ERR! Cannot find module 'node-gyp/bin/node-gyp'

node-gyp 模块可能被损坏,重新安装 nodejs 即可。

ES6定义类的错误

定义一个这样的类:

// widgetImg.js

class WidgetImg {
id = null;
type = 'video'; // img video audio flash
width = 180;
height = 180;
}

export default WidgetImg;

在另外一个文件引入用它:

import WidgetImg from '../widgetImg';

结果发现报错了,提示:

经过各方搜寻资料,发现需要单独配置 babel 文件。首先,删除 webpack.config.js 里的一些内容:

module.exports = {
module: {
rules: [
{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
// options: {
// presets: ['env', 'react']
// }
}
}
]
}
}

在项目根目录新增一个 .babelrc 文件:

{
"presets": [
[
"env",
{
"targets": {
"browsers": [
"last 2 versions",
"safari >= 7",
"ie >= 9",
"chrome >= 31"
]
},
"useBuiltIns": true
}
],
"react"
],
"plugins": [
"transform-class-properties"
]
}

重新运行启动服务即可。它的实际问题,就是没有配置 transform-class-properties 这款插件。类似的,如果你项目中需要使用 rest运算 和 spread扩展符,你也可以再配置 transform-object-rest-spread