Fork me on GitHub

webpack15--一些高级的配置

Eslint在webpack中的配置

每次在开发的时候,可能团队需要使用相同的代码规范,我们可以使用eslint来约束我们的代码.

npm install eslint -D

然后使用相对应的eslint配置来进行.

npx eslint –init

然后我们可以在下面选择几种比较通用的eslint模板,一般我们就选择比较直接的模板(选择第一项).

然后在第一项里面选择对应的比较流行的几种代码规范.(在进行选择的时候选择比较流行的代码规范即可)

后面按照自己的配置去进行就可以了.

配置完成之后,整个项目的根目录下面会生成一个叫做.eslintrc.js的配置文件,我们可以在这个里面配置关于一些代码规范的设置.

然后配置完成之后的eslint来校验我们的代码的时候,直接使用npx eslint src来对src目录下面的代码去进行校验即可,这样会在控制台里面输出相关的不合法的信息,同样的我们也可以使用vscode插件来简化这个过程,我们安装一个vscode插件eslint,这样我们代码中有不符合eslint规范的代码都会提示出相关的报错信息(因为它会根据.eslintrc.js的配置)

相关配置可以参考官网的行为.

使用webpack的相关loader来对eslint的代码去进行检测:

npm install eslint-loader -D

然后去webpack.congif.js里面去进行相关的配置.

1
2
3
module:{
use:['babel-loader','eslint-loader']
}

对webpack进行相关的loader的配置.

然后之后我们使用webpack的打包命令的时候,这个eslint都会出现相关的配置错误.

注意eslint-loader是不能放在babel-loader的前面的.

在进行开发的时候,我们一般不使用eslint-loader,一般通过git的钩子函数来运行webpack,从而让通过不了的代码不提交到git仓库

Webpack性能优化

  1. 跟上技术的迭代(Node,Npm,Yarn)

尽量使用新的Node,Npm和Yarn的版本

  1. 尽可能

比如说我们之前使用了babel-loader去编译js文件,一般都会在module下面这样去写:

1
2
3
4
5
6
7
{
test: /\.js$/,
exclude: /node_modules/,
use: [{
loader: 'babel-loader'
}]
}

这里面除了node_modules的js模块全部会被打包进去,这里我们也可以使用

1
include :path.resolve(__dirname,'../src')

只对src里面的代码进行一个babel-loader的编译,这样我们可以降低loader被频繁执行的频率.

  1. Plugin尽可能精确并且保持可靠

在DEV环境下我们一般是不使用压缩代码的插件,比如这个css的压缩plugin,我们一般都是在线上的代码中才去进行压缩.

1
2
3
4
5
6
plugins: [
new MiniCssExtractPlugin({
filename: '[name].css',
chunkFilename: '[name].chunk.css'
})
],

webpack的插件是会对我们的代码打包速度的性能造成影响的.

  1. resolve参数的合理配置

一般给react代码去进行打包的时候,我们要让js和jsx代码都要去走一下babel-loader,于是我们可以在test一栏里面写上:

1
test: /\.jsx?$/,

这样无论是js还是jsx的代码都会使用babel-loader去进行打包编译.

resolve:

1
2
3
resolve:{
extensions: ['.js','.jsx']
},

这个配置项写在webpack的common环境里.然后以后我们在项目中去引入某个文件夹中的文件时,它会默认以js或者jsx为后缀名.

我们也可以在这里插入很多模块,例如.css.jpg文件的时候,但是这里引入太多的话会造成一定的性能的损耗,所以我们一般就使用一个js和jsx的配置就可以了.

我们去引入一个文件夹下面的代码的时候,它会默认帮我们去引入目录下面的index.js文件,这里我们希望能够引入一些其他名字的默认文件,这样就可以了.

1
2
3
4
5
6
7
resolve:{
extensions: ['.js','.jsx'],
mainFiles: ['index','child'],
alias: {
others: path.resolve(__dirname,'../src/child')
}
},

这样我们就引入了一个叫做others的文件.去引入的时候直接引入others就相当于引入了src/child目录下的js文件,这个alias的作用就是起个别名.

  1. 使用DllPlugin提高打包速度

目标是让第三方模块只打包一次.
引入第三方模块使用dll文件去引入

使用一些插件来提高打包的速度,假设我们在index.js中写上这样的一些代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import React , {Component} from 'react'
import ReactDom from 'react-dom'
import Child from './child';
import _ from 'lodash'
class App extends Component{
render(){
return(
<div>
<div>{_.join(['wd','qaq'],'-')}</div>
<Child />
</div>
)
}
}
ReactDom.render(<App />,document.getElementById('root'))

在里面我们使用了react,react-dom,lodash这些库,我们想将这些库分开进行打包于是可以使用dll来对其打包,我们新建一个webpack.dll.js的js文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const path = require('path')
const webpack = require('webpack')
module.exports = {
mode:'production',
entry:{
vendors: ['react','react-dom','lodash']
},
output:{
filename: '[name].dll.js',
path: path.resolve(__dirname,'../dll'),
library: '[name]'
},
plugins: [
new webpack.DllPlugin({
name: '[name]',
path: path.resolve(__dirname,'../dll/[name].manifest.json')
})
]
}

在package.json中添加一个打包的命令:

1
"build:dll": "webpack --config ./build/webpack.dll.js"

这样去进行打包命令的时候npm run build:dll,然后这些库就会打包到dll这个文件夹中去.

会生成一个叫做vendors.dll.js的js文件.

npm install add-asset-html-webpack-plugin –save-dev

这个插件能够往html-webpack-plugin里面去引入一些静态的资源,我们需要让我们打包生成的index.html中能够引入dll那边打包生成的js文件.

在webpack.common.js中的plugins中添加两个新的插件

1
2
3
4
5
6
7
8
const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin');
new AddAssetHtmlWebpackPlugin({
filepath: path.resolve(__dirname,'../dll/vendors.dll.js')
})
new webpack.DllReferencePlugin({
manifest: path.resolve(__dirname,'../dll/vendors.manifest.json')
})

这样在打包生成index.html中会引入这样的这个js文件

这样我们去进行打包的时候时间大约会减低很多了(因为第三方模块我们只进行了一次打包).
打包的流程大概为:
npm run build:dll

npm run build

其实上面第三方库的打包是可以去做一些拆分的:

1
2
3
4
entry:{
vendors: ['lodash'],
react: ['react','react-dom']
},

这样dll文件中就会产生react.dll.jsvendors.dll.js

这样我们就需要在webpack.common.js中再加上两个plugins的配置:

1
2
3
4
5
6
new AddAssetHtmlWebpackPlugin({
filepath: path.resolve(__dirname,'../dll/react.dll.js')
})
new webpack.DllReferencePlugin({
manifest: path.resolve(__dirname,'../dll/react.manifest.json')
})

这样我们先运行npm run build:dll之后运行npm run dev在控制台中输出react会有一个相对应的输出.

但是当我们需要打包的包增多了之后,这个打包就会很复杂,我们可以采用一种动态的方法,使用node.js的手法来实现.

在webpack.common.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
const fs =require('fs');
const plugins = [
new HtmlWebpackPlugin({
template: 'src/index.html'
}),
new CleanWebpackPlugin(['dist'], {
root: path.resolve(__dirname, '../')
})
]
// 上面是一些固有的插件
const files = fs.readdirSync(path.resolve(__dirname,'../dll'))
// 把dll目录下面的文件读取出来
// 通过正则去对其进行匹配
files.forEach(file=>{
if(/.*\.dll.js/.test(file)){
plugins.push(
new AddAssetHtmlWebpackPlugin({
filepath: path.resolve(__dirname,'../dll',file)
})
)
}
if(/.*\.manifest.json/.test(file)){
plugins.push(
new webpack.DllReferencePlugin({
manifest: path.resolve(__dirname,'../dll',file)
}),
)
}
})
// 将其引入plugins中
module.exports = {
// some codes ...
plugins
}

这样每次我们在dll那边对外部的js模块进行了dll操作,这边都会动态去引入对应的模块.

  1. 控制包文件的大小

有时候我们会在代码里面引入一些没有用的模块,如果没有配置tree-shaking,会拖累webpack的打包速度,在源代码中没有引入的包可以使用tree-shaking来去掉他.

  1. thread-loader,parallel-webpack,happypack多进程打包

webpack默认是使用nodeJS来运行的,所以它是使用单进程来进行打包,我们可以使用node里面的多进程来利用多个CPU进行打包.

  1. 合理使用sourceMap

  2. 结合status分析打包结果

  3. 开发环境内存编译,无用插件的剔除(分清楚prod和dev)

多页面打包配置

之前我们打包的项目都是单页面文件(我们每次打包生成的dist目录下面只有一个index.html文件,这就叫做单页面文件).

现在主流的框架vue和react都是单页面的应用

如果我们有一些奇怪的场景(比如兼容jquery和zepto等老应用)

我们创建一个list.js文件

1
2
3
4
5
6
7
8
9
10
11
12
import React , {Component} from 'react'
import ReactDom from 'react-dom'
class List extends Component{
render(){
return(
<div>
<div>This is Home Page</div>
</div>
)
}
}
ReactDom.render(<List />,document.getElementById('root'))

如果我们想打包生成的dist目录下面有两个html文件,我们需要在webpack的htmlWebpackPlugin中进行一系列的配置

一个是以index.js为入口文件的index.html文件,一个是以list.js为入口文件的list.html文件

1
2
3
4
entry: {
main: './src/index.js',
list:'./src/list.js',
}

首先添加两个打包入口文件,然后去分别配置两个js打包出来的html文件的格式以及大小,使用htmlWebpackPlugin中的相关设置去进行配置即可.

1
2
3
4
5
6
7
8
9
10
new HtmlWebpackPlugin({
template: 'src/index.html',
filename: 'index.html',
chunks:['runtime','vendors','main']
}),
new HtmlWebpackPlugin({
template: 'src/index.html',
filename: 'list.html',
chunks:['runtime','vendors','list']
}),

然后去进行打包的时候时候就会生成两个html页面,同样和上面打包dll的方式相同,我们可以使用动态的方式对html去进行引入

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
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const webpack = require('webpack');
const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin')
const fs = require('fs')
const makePlugins = (configs) => {
const plugins = [
new CleanWebpackPlugin(['dist'], {
root: path.resolve(__dirname, '../')
})
];
Object.keys(configs.entry).forEach(item => {
plugins.push(
new HtmlWebpackPlugin({
template: 'src/index.html',
filename: `${item}.html`,
chunks: ['runtime', 'vendors', item]
})
)
});
const files = fs.readdirSync(path.resolve(__dirname, '../dll'))
// console.log(files); // 拿到dll目录下面所有的文件的文件名称
files.forEach(file => {
if (/.*\.dll.js/.test(file)) {
plugins.push(
new AddAssetHtmlWebpackPlugin({
filepath: path.resolve(__dirname, '../dll', file)
})
)
}
if (/.*\.manifest.json/.test(file)) {
plugins.push(
new webpack.DllReferencePlugin({
manifest: path.resolve(__dirname, '../dll', file)
}),
)
}
});
return plugins
}
const configs = {
entry: {
main: './src/index.js',
list: './src/list.js',
},
resolve: {
extensions: ['.js', '.jsx'],
mainFiles: ['index', 'child'],
alias: {
others: path.resolve(__dirname, '../src/child')
}
},
module: {
rules: [{
test: /\.jsx?$/,
exclude: /node_modules/,
use: [{
loader: 'babel-loader'
}]
}, {
test: /\.(jpg|png|gif)$/,
use: {
loader: 'url-loader',
options: {
name: '[name]_[hash].[ext]',
outputPath: 'images/',
limit: 10240
}
}
}, {
test: /\.(eot|ttf|svg)$/,
use: {
loader: 'file-loader'
}
}]
},
optimization: {
runtimeChunk: {
name: 'runtime'
},
usedExports: true,
splitChunks: {
chunks: 'all',
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10,
name: 'vendors',
}
}
}
},
performance: false,
output: {
path: path.resolve(__dirname, '../dist')
}
}
configs.plugins = makePlugins(configs);
module.exports = configs

这样就可以能够对多页面应用去进行一个具体的引入了,比如说我们在src目录下新建一个wd.js然后去入口文件里面添加一个新的入口即可.

-------------本文结束感谢您的阅读-------------