作为一名前端开发,按时按质实现业务需求只是基础条件,了解一些webpack相关的配置,除了能够帮助我们提升一下自己的技术能力,也能够帮助我们更好的维护项目、搭建适合自己团队的web站点。

希望通过本文能让大家对webpack4的相关配置项有一个直观的了解,以例子的形式帮助大家更好&更快的掌握相关知识点。

webpack4介绍

  • webpack是一个JS应用打包器,它将应用中的各个模块打成一个或者多个bundle文件。借助loaders和plugins,它可以改变、压缩和优化各种各样的文件。它的输入是不同的资源,比如:js、css、图片、字体和html文件等等,然后将它们输出成浏览器可以正常解析的文件。
  • 2018.8.25,正式发布webpack4,相比于webpack3,带来很多的新特性更新和改善,让webpack配置更加简单
  • webppack4是目前比较主流打包工具之一,跟其他的打包工具相比,比如gulp,rollup等,webpack4优势:社区活跃度高,使用的人多,官方更新迭代快速。

webpack4配置项

准备工作

  • 安装node,webpack,然后新建文件夹–npm初始化
1
2
3
4
//webpack安装一般是不-g全局安装的,因为同时会进行好几个项目,有时候这几个项目的webpack版本都是不一样的,因此都是根据项目来局部安装,只在独立的项目中有效。
npm install webpack-cli webpack --save-dev
//可以一路回车,初始化(项目名字,版本号,描述等默认就好,需要更改的可以在package.json文件中更改)
npm init/npm init -y

mode属性

  • mode属性可以设置为:development(代码不压缩)/production(代码压缩),在开发模式的时候可以提供更加详细的错误日志和提示,在生产环境打包的时候可以webpack专注项目的打包,去除掉开发阶段的运行的代码,是打包的生产环境的包文件更小。

entry/output

  • 入口和打包成功的出口–基本配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//webpack打包的时候会默认找webpack.config.js文件
//使用path,首先需要引入
const path = require('path')
module.exports = {
mode: 'development',//开发模式
entry: './index.js',//指定入口文件
output: {
filename: 'bundle.js',//给成功打包的文件定义名字
path: path.resolve(__dirname,'dist'),//__dirname--相对webpack.config.js这个文件同级根目录,生成的打包文件dist目录下面
}
}
entry: {
main: './src/index',
sub: './src/index'
}
output: {
publicPath: ''---如果需要加域名的话可以这样配置
filename: '[name].js',---name占位符对应着entry中的key值
path: resolve.path(__dirname,'dist')
}
  • 代码演示—1
1
2
3
4
5
6
7
npx webpack -v
npx webpack index.js
//在package.js的script可以配置运行打包命令
"script": {
"build": "webpack"
}
npm run build
  • 代码运行成功后–package.json中的默认配置,npm webpack index.js成功后生成后,Hash–唯一的hash值,version–webpack版本,Time–打包耗费的时间,built中,asset–打包后的文件,size–打包文件的大小,chunks–每个打包文件对应的id值,chunk Names–打包文件名,main–对应着打包入口文件

img

img

img

loader

  • 什么是loader,webpack对不是js后缀的文件是不能进行处理的,这时候需要借助loader,loader其实就是一个打包方案,webpack对不是js后缀的文件不知道怎么去打包,但是loader是知道的,所以这时候webpack就要依赖loader来执行打包文件。
  • file-loader 图片/字体文件打包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
//webpack.config.js
module.exports = {
...
module: {
//规则数组类型--有很多不同类型的打包规则
rules: [
{
test: /\.(jpg|png|jpeg)$/,
use: {
loader: 'file-loader',
options: {
//placehold--占位符 name--文件名,hash--hash值,ext-文件后缀名
name: '[name]_[hash].[ext]',
//回把图片打包到dist目录下image文件夹里
outputPath: "./image"
}
}
},{
test:/\.(eot|ttf|svg)$/,
use: {
loader: 'file-loader',
...
}
}
]
}
}
  • url-loader 其包含了file-loader的图片打包的功能,区别:就是不加任何配置项的时候,默认把图片打包成base64字符放在打包的js文件中,优势,加载js文件完成之后图片也加载好了,减少一次http请求,不好的地方就是当图片文件过大的时候,那么被放入图片的打包js文件也会很大,那么在加载技js文件的时候,加载耗时较长,也面回停留空白页的时间过长,因此增加配置项limit,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
rules: [
{
test:/\.(jpg|jepg|png)$/,
use: {
loader: 'url-loader',
options: {
filename: '[name]_[hash].[ext]',
outputPath: './image',
limit: 20480, //==20480个字节==20kb
//大于20kb就会打包成图片放在dist目录下面,小于则打包成base64字符串放在打包的js文件里面
}
}
}
]
  • 代码演示–2 –使用url前后加了limit配置之后的对比
1
2
3
//使用file-loader ,url-loader 需要安装
npm install file-loader --save-dev
npm install url-loader --save-dev

img
img
img
img

style-loader css-loader sass-loader postcss-loader

  • 同样的对css,scss打包的时候,webpack都是不知道怎么去打包,所以需要借助相关的loader来执行打包,要同时安装css-loader,style-loader,css-loader只负责处理css文件,在得到css-loader处理并且合并之后的css内容文件时,需要使用style-loader挂在到页面header部分,这样样式的打包才会在页面生效,同样的如果sass文件时,还需要安装sass-loader,node-sass ,因为sass-loader是依赖于node-sass来执行解析sass文件的,所以执行顺序是,先是sass-loader将scss文件解析成css文件,其次css-loader处理这些被解析成的css文件,然后再有style-loader将这些内容挂在到页面的header部分,postcss-loader就是增加厂商前缀的,是最先执行的。
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
//安装loader
npm install style-loader css-loader sass-loader node-sass postcss-loader --save-dev
//在使用postcss-loader的时候需要借助插件
//autoprefixer
npm install autoprefixer -D
//使用postcss-loader还需要增加配置文件
//postcss.config.js
module.exports = {
plugins: [
require('autoprefixer')
]
}
//webpack.config.js
module: {
rules: [
{
test: /\.css$/,
use: [
"style-loader",
<!--"css-loader",-->
{
loader: "css-loader",
options: {
//表示css文件中又引入css文件,这样配置项可以使每一层都可以走到(下-上,右-左)
importLoader: 2,
//配置支持css模块化,避免样式渲染全局,name.类名的方式调用
<!--modules: true-->
}
}
"postcss-loader"
]
},{
test: /\.scss$/,
use:[
"style-loader",
<!--"css-loader",-->
{
loader: "css-loader",
options: {
importLoader: 2,
<!--modules: true-->
}
},
"sass-loader",
"postcss-loader"
]
}
]
}
  • 代码演示–3

img

img

img

img

img

img

img

img

plugins

  • html-webpack-plugin: 作用就是帮我们自动生成一个html,并且把打包好生成的js文件引入到html中,同时可以在src目录下创建一个html模板,然后可以设置配置项,根据这个html模板自动生成html放在dist目录下,,在打包完成之后开始执行
  • clean-webpack-plugin:作用就是把之前打包的dist目录删除掉,这个是在打包之前开始执行的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//首先安装html-webpack-plugin hlean-webpack-plugin
npm install html-webpack-plugin clean-webpack-plugin -D

//在webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
module.exports = {
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
}),
new CleanWebpackPlugin('./dist')
]
}
  • 代码演示 – 4 TypeError: CleanWebpackPlugin is not a constructor

img

img
img

1
2
3
4
//所以引用这个插件必须要使用对象结构
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
//这个插件使用默认配置就好了,如果要传入参数的话,必须以对象的形式
new CleanWebpackPlugin()/ new CleanWebpackPlugin({})

img

sourceMap的配置

  • 这个是什么呢?,这个配置就是帮我们做一个打包之后和源代码之间的一个映射关系,当打包的时候出现问题的时候,我们并不是要知道打包文件哪一行出现错误,而是要知道源代码的哪一行出现错误。
1
2
3
4
5
6
7
8
9
10
11
//webpack.config.js
module.exports = {
//基本配置
devtool: "source-map"
//可以提示比较全面的错误信息,具体到哪一行哪一列第几个字符,并且映射文件会打包到打包文件里面
devtool: "inline-source-map"
//开发环境配置推荐
devtool: "cheap-module-eval-source-map"
//生产环境
devtool: "cheap-module-source-map"
}

webpackDevServer

  • 可以帮我们监听文件的改动,还可以模拟服务器的一些特性,一旦src目录下的内容发生改变就会自动帮我们打包文件并且自动刷新页面,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//先安装webpack-dev-server
npm install webpack-dev-server -D

//package.json
"scripts": {
"watch": "webpack --watch",
"start": "webpack-dev-server",
}

//webpack.config.js
devServer: {
contentBase: './dist',//本地服务器加载的目录
open: true,//打包完成之后自动帮我们启动一个本地服务器,端口默认是8080,
port: "",//端口可以自定义
...
}

Hot Module Replace–HMR:热更新模块

  • 使用webpack-dev-server启动项目,帮我们启动一个本地服务器,并且还会自动帮我们打包,打包文件是放在内存当中,这样打包的速度更快,性能更好。
  • webpack自带的一个热更新模块插件 –HotModuleReplacementPlugin,但是作用于css和js的过程中,相交于css热更新,js需要多一下代码,原因是css-loader已经帮我们写好了,像vue框架热更新也是一样,已经内置已经写好的代码,react框架也是借助babel的preset的配置来处理的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// weebpack.config.js
const webpack = require('webpack');

module.exports = {
devServer: {
contentBase: './dist',
port: 8080,
open: true,
hot: true,//--开启热更新功能
hotOnly: true,//阻止页面自动刷新,即使hot更新功能不起作用,浏览器也不自动刷新页面
}
plugins: [
new webpack.HotModuleReplacementPlugin()
]
}
//js
if(module.hot) {
module.hot.accept('./number',() => {
document.body.removeChild(document.getElementById('id'))
number()//重新执行一下这个模块的函数
})
}
  • 代码演示–5
  • sourceMap
    img
    img
    img
    img
    img

babel处理es6语法

  • 对于普通浏览器目前是不支持es6语法的,但是支持es5语法代码,因此需要借助babel插件来做转换,这里可以查Babel的官方文档—这里省略一万步—这里面有使用场景的选择–选择webpack使用场景
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
//安装插件 --@babel/core是babel核心库
npm install babel-loader @babel/core -D
//同时要按装,babel-loader只是打通webpack的一个桥梁,并不会转义代码,需要借助@babel/preset-env 来做语法的转换
npm install @babel/preset-env -D
//还有继续安装@babel/polyfill -- 作用是帮助低版本的浏览器弥补缺失的变量以及函数,同时可以在options配置,根据业务代码来做低版本的缺失弥补,这样打包代码可以做到精简,注意的是,这个插件只适合做业务代码带包,因为会污染全局
npm install @babel/polyfill -D
//新建.babelrc文件,options配置可以放在这个文件里面
{
presets: [
[
"@babel/preset-env",
{
"targets": {
"chrome": "67"
},
"useBuiltIns": "usage"// 按需加载
}
]
]
}
//webpack.config.js
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader",
<!--options: {-->
<!-- presets: [['@babel/preset-env',{targets: {chrome: "67",},useBuiltIns: 'usage'}]]-->
<!--}-->
//test code --es6
import '@babel/polyfill'
const arr = [
new Promise(() => {}),
new Promise(() => {})
]
arr.map(item => {
console.log(item)
})

webpack实现对react框架代码的打包

  • 需要借助@babel/preset-react插件,在转换react框架代码之后还是要插件@babel/preset-env转换es6语法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//使用安装插件
npm install react react-dom --save-dev
npm install @babel/preset-react --save-dev
//.babelrc中去引用插件
presets:[
"@babel/preset-react"
]
//js--test-code
import React ,{ Component } from 'react'
import ReactDom from 'react-dom'
class App extends Compnent {
render() {
return(<div>herllo world</div>)
}
}
ReactDom.render(<App/>,document.getElementById('root'));

Tree shaking

  • 作用:这是webpack自带的一个功能,当引入一个模块的时候,不需要引入全部的代码,只需要引入被需要的代码,–就是摇树的意思,把不必要的叶子全部摇掉。
  • 但是webpack中的tree shaking仅支持esmodule这种引入方式。
1
2
3
4
5
6
7
8
9
10
//webpack.config.js
module.exports = {
plugins: [],
optimization: {
usedExports:true//只是在开发环境需要配置,在生产环境把不需要配置
}

}
//package.json中 开发环境是会保留这段代码的
"sideEffects": ['*.css']---忽略相关模块不做tree shaking

development和production模式的区分打包,webpack.dev.js,webpack.pord.js,webpack.common.js –可以把文件放到build文件中

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
//需要下载 webpack-merge
npm install webpack-merge -D
//dev
const webpack = require('webpack');
const devConfig = {
mode: 'development',
devtool: 'cheap-module-source-map',
devServer: {
contentBase: '../dist',//本地服务器加载的目录
open: true, //打包完成之后自动打开浏览器
// port: 8080,
hot: true,//开启模块热更新更能
hotOnly: true,//阻止页面刷新,即使热更新功能失效,也不会刷新页面
},
plugins: [
new webpack.HotModuleReplacementPlugin()
],
optimization: {
usedExports:true,
},
}
module.exports = devConfig;
//pord
const pordConfig = {
mode: 'production',//production
}
module.exports = pordConfig;
//common
const path = require('path');
const merge = require('webpack-merge')
const devConfig = require('./webpack.dev.js');
const prodConfig = require('./webpack.prod.js');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const commonConfig = {
entry: {
main: './src/index.js'
},
module: {
//规则数组类型--有很多不同类型的打包规则
rules: [
// {
// test: /\.(jpg|png|jpeg)$/,
// use: {
// loader: 'file-loader',
// options: {
// //placehold--占位符 name--文件名,hash--hash值,ext-文件后缀名
// name: '[name]_[hash].[ext]',
// //回把图片打包到dist目录下image文件夹里
// outputPath: "./image"
// }
// }
// },
{
test: /\.js$/,
exclude:/node_modules/,
loader: 'babel-loader'
},
{
test: /\.(jpg|png|jpeg)$/,
use: {
loader: 'url-loader',
options: {
//placehold--占位符 name--文件名,hash--hash值,ext-文件后缀名
name: '[name]_[hash].[ext]',
//回把图片打包到dist目录下image文件夹里
outputPath: "./image",
limit: 204800,
},
}
},{
test:/\.(eot|ttf|svg)$/,
use: {
loader: 'file-loader',
}
},{
test: /\.css$/,
use: [
"style-loader",
"css-loader",
"postcss-loader"
]
},{
test: /\.scss$/,
use: [
"style-loader",
"css-loader",
"sass-loader",
"postcss-loader"
]
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
}),
// * All files inside webpack's output.path directory will be removed once, but the
// * directory itself will not be. If using webpack 4+'s default configuration,
// * everything under <PROJECT_DIR>/dist/ will be removed.
new CleanWebpackPlugin({ verbose: true,cleanOnceBeforeBuildPatterns:['**/*']}),
],
output: {
filename: 'bundle.js',//打包成功后的文件名
path: path.resolve(__dirname,'../dist'),//__dirname---表示跟web.config.js的同级根目录,打包之后的文件夹
}
}

module.exports = (env) => {
if(env&&env.production) {
return merge(commonConfig,prodConfig)
}else{
return merge(commonConfig, devConfig)
}
}
//package.js
"script": {
"start": "webpack-dev-server --config ./build/webpack.common.js",
"build": "webpack --env.production --config ./build/webpack.common.js",
"dev": "webpack --env.development --config ./build/webpack.common.js"
}
  • 代码演示 -6
    img
    img
    img

webpack和code Splitting:

  • 代码分割是webpack相当重要的一个特性,可以让代码分割到不同的文件中,一边按需加载或者并行加载这些文件,这样可以优化加载性能,以及用户体验会更好。
  • 代码分割其实只是在webpack中去实现代码分割,两种方式,同步代码分割只需optimization中配置即可,异步代码分割,webpack代码分割默认配置就是对异步代码进行代码分割,但是需要babel插件来做翻译,浏览器对异步的这种语法规则不支持。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//异步代码分割,安装babel插件,babel-plugin-dynamic-import-webpack
npm install babel-plugin-dynamic-import-webpack -D
//同步代码分割的话需要在optimization中配置splitChunks
//---在.babelrc中去配置一个plugins,
presets: [],
plugins: ["dynamic-import-webpack"]
//webpack.common.js
optimization: {
splitChunks: {
chunks: 'all'//对同步代码和异步代码同时做代码分割
chunks:'aysnc'//对异步代码做分割
}
}
//test-code--index.js
function getComponent() {
return import('lodash').then(({default:_}) => {
var element = document.createElement('div');
element.innerHTML = _.join(['dell','lee'],'_');
return element
})
}
getComponent().then(element => {
document.body.appendChild(element);
})
  • splitChunksPlugin的底层配置中,异步代码会自动打包成一个文件0.js,这里需要指定引入异步代码文件的名字可以使用魔法注释,这里就需要这个插件@babel/plugin-syntax-dynamic-import来做异步代码的转化,不能使用上面那个插件babel-plugin-dynamic-import-webpack,/webpackChuunkName:‘lodash’/,
  • chunks:aysnc这种异步代码可以解决打包文件过大,加载时间过长,分出第三方库和插件,需要的时候在进行按需加载和并行加载,提供加载性能。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
npm install @babel/plugin-syntax-dynamic-import --save-dev
splitChunks: {
chunks: async //--如果是async的话,只是对异步代码做代码分割,all是对异步和同步分都做代码分割
minSize: 30000===30kb //大于30kb就会做代码分割,小于的话就做代码分割
maxSize:0//可配可不配 --如果是50000===50kb, 会做二次分割 lodash 打包成1mb ,会拆成20个50kb代码分割
minChunks: 1,//当这个模块使用几次的话在做代码分割,小于的设置的次数就不做代码分割
maxAsyncRequests: 5,//同时加载的模块是5个,在打包前五个会帮你打包做代码分割,超过五个的话就不做代码分割
maxInitialRequests: 3,//指整个网站首页进行加载的时候或者是入口文件进行加载的时候,入口文件会引入其他库,也只能最多三个,超过三个就不会做代码分割了
automaticNameDelimiter: '~',//文件生成的时候,文件的中间会有一些连接符,
name: true, ---//起什么名字,让cacheGroups中的名字有效
cacheGroups: {//如果是同步的话会走完chunks之后会走这个配置,缓存组
vendors: {
test: /[\\/]node_modules[\\/]/,//--是同步代码发现是从node_modules引入的话,那么符合这个组会被打包成单独文件
priority: -10
filename: 'vendors.js'//是打包文件的名字
}
default: {
priority: -20,//假设同时符合两个组,通过这个配置来设置优先级,值越大,优先级越高
reuseExistingChunk: true,如果一个模块a,b,如果a使用了b,符合代码分割的要求,然后又符合default这个组,就不打包之前打包过的内容
filename: 'common.js'
}
}
}
  • lazy loading—就是支持esmodule这种语法,按需加载。
  • css代码分割: filename与chunkFilename区别:官网的代码分割插件:做的事情就是把css文件单独打包,不直接打包到js文件里面,这个插件不支持HMR,所以css代码分割插件一般应用到生产环境.
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
//安装插件
npm install mini-css-extract-plugin --save-dev
//线上环境单独生成的css文件需要做代码压缩合并
npm install optimize-css-assets-webpack-plugin -D
//optimization
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCssAssetsWebpackPlugin = require(' optimize-css-assets-webpack-plugin')
//配置做更改
//开发环境使用规则默认
//线上环境
module: {
rules: [
use: ['MiniCssExtractPlugin.loader'] //所有使用style-loader全部替换,不使用style-loader
]
},
optimization: {minimizer: [new OptimizeCssAssetsWebpackPlugin({})]},
plugins: [new MiniCssExtractPlugin({
filename: '[name].css',//被文件直接引用走这个配置
chunkFilename: '[name].chunk.css'//被间接引用的话是走这个配置项
})]
output: {
filename: '[name].js',
chunkFilename: '[name].chunk.js'//就是被js间接引用的打包文件就会走这个配置内容,
path: path.resolve(__dirname,'../dist')
}