Webpack是一个现代javascript应用程序的静态模块打包工具: 它做的事情是,分析你的项目结构,找到Javascript模块以及其它的一些浏览器不能直接运行的拓展语言(Scss,Typescript),并将其打包成浏览器识别的格式.
webpack功能
- 代码转换 Typescript编译成Javascript,Scss变异成css等
- 文件优化 压缩Javascript,CSS,HTML代码,压缩图片
- 代码分割 提取多个页面的公共代码,提取首屏不需要执行的代码让其异步加载
- 模块合并 构建功能将项目中的多个模块分类合并
- 自定刷新 监听本地源代码变化,自动重新构建,刷新浏览器
- 代码校验 代码提交之前校验代码是否符合规范,以及单元测试是否通过
- 自动发布 更新完代码,自动构建出线上代码并上传
webpack安装及初始化项目
- 初始化项目 npm init -y 按照默认参数初始化package.json
- 本地安装(局部安装) npm i webpack webpack-cli -D
.\node_modules\.bin\webpack --version
windows需使用\路径npx webpack --version
低版本需要安装 npxnpx
会自动查找node_modules对应的.bin下的webpack.cmd命令 不存在则会自动安装 - 全局安装 npm i webpack webpack-cli -g 常在终端命令窗口使用
webpack --version
配置开发服务器 webpack-dev-server
1 | // package.json中的script 添加 start: 'webpack-dev-server' |
webpack基本配置
配置入口和出口
1
2
3
4
5
6
7
8
9
10
11
12module.exports = {
entry: { // 入口 找到入口之后开始寻找依赖包和各种资源
index: './src/index.js',
},
output: { // 出口 告诉webpack经过各种loader处理后的文件应该生成到哪个目录下
// 输出文件名
filename: 'build.js',
// 这个路径必须是绝对路径
path: path.resolve('./build'),
// path: path.join(__dirname, './build')
}
}配置hash
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19module.exports = {
output: {
// filename: 'build.js',
// 输出目录加入hash 8位
filename: 'build.[hash:8].js',
// 这个路径必须是绝对路径
// path: path.resolve('./build'),
path: path.join(__dirname, './build')
}, // 出口
plugins: [
new HtmlWebpackPlugin({
// 要拷贝的模板文件路径
template: './src/index.html',
// 文件末尾加入hash
hash: true,
})
]
}
// src="build.60df093c.js?60df093c83b96da06c39"多入口 多出口 多页应用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23module.exports = {
entry: {
index: './src/index.js',
b: './src/b.js'
},
output: {
filename: '[name].[hash:8].js',
path: path.resolve('./build')
}, // 出口
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
filename: 'a.html',
template: './src/index.html',
chunks: ['index']
}),
new HtmlWebpackPlugin({
filename: 'b.html',
template: './src/index.html',
chunks: ['b']
})
], // 插件的配置
}开发环境
1
2
3module.exports = {
mode: 'development', // 可以更改模式
}
webpack 插件 plugin
loader与plugin区别
loader用于转换某些类型的模块,是一个转换器
plugin是插件, 是对webpack本身的扩展,是一个扩展器,功能强大可以用来处理各种各样的的任务
html-webpack-plugin 自动生成一个index.html文件(或者指定模板来生成)并自动引入打包后的js文件
npm install html-webpack-plugin --save-dev
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16let HtmlWebpackPlugin = require('html-webpack-plugin')
new HtmlWebpackPlugin({
// 要拷贝的模板文件路径
template: './src/index.html',
// 设置标题
title: 'webpack练习',
// 文件末尾加入hash
hash: true,
// 压缩文件
minify: {
// 去除属性双引号
removeAttributeQuotes: true,
// 压缩成一行
// collapseWhitespace: true
}
})clean-webpack-plugin 删除清空文件
1
2let { CleanWebpackPlugin } = require('clean-webpack-plugin')
new CleanWebpackPlugin(['dist']),webpack 自带热更新
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22let webpack = require('webpack')
module.exports = {
devServer: {
contentBase: './build',
port:3000,
compress: true, // 服务期压缩
open: true, // 自动打开浏览器
hot: true, // 热更新
proxyTable: {//代理 webpack-dev-server提供
'/api':{
target:'http://192.168.5.22:8787',//请求转发到本地服务器
pathRewrite: {//替换路径
'^/api':'/public/json'
}
}
}
}, // 开发服务器
module: {}, // 模块配置
plugins: [
new webpack.HotModuleReplacementPlugin() // 热更新插件
]
}抽离css样式 用于生产环境
npm i extract-text-webpack-plugin -D 或 mini-css-extract-plugin -D
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21let ExtractTextWebpackPlugin = require('extract-text-webpack-plugin')
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: ExtractTextWebpackPlugin.extract({
fallback: 'style-loader',
use: [{ loader: 'css-loader'} ]
})
}
]
}, // 模块配置
plugins: [
new ExtractTextWebpackPlugin({
filename: 'css/index.css',
// 开发环境禁用 否则css无法热更新
disable: true
})
]
}剔除没用的css代码 purifycss-webpack
- postcss-loader 后处理css 兼容性检查 自动加css前缀autoprefixer
- copy-webpack-plugin拷贝文件
压缩js的plugin
npm install uglifyjs-webpack-plugin --save-dev
1
2let uglifyJsPlugin = require('uglifyjs-webpack-plugin')
new uglifyJsPlugin(),打包公共代码 webpack自带的CommonsChunkPlugin
1
2
3
4
5
6
7
8module.exports = {
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: 'common',
minChunks: 2
})
]
}处理第三方js库 直接在html引入线上地址或使用webpakc.ProvidePlugin()
注入plugins: [new webpck.ProvidePlugin({ $: 'jquery' })]
- 将共用代码载入到html中以style标签或script标签中
npm i html-webpack-inline-chunk-plugin -D
loader
loader本身就是一个函数,对接收到的内容进行转换并返回转换后的结果。用于转换各种资源类型的模块
webpack在读取使用的loader的过程中,是按照从右向左的顺序读取的。loader实际上就是一个预处理器
css处理相关的loader
npm install style-loader css-loader less less-loader stylus stylus-loader node-sass sass-loader -D
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19module.exports = {
module: {
rules: [
{
test: /\.css$/, use: [
{ loader: 'style-loader'}, // 将css样式嵌入到文档中
{ loader: 'css-loader'} // 负责加载css文件
]
},
{
test: /\.less$/, use: [
{ loader: 'style-loader'},
{ loader: 'css-loader'},
{ loader: 'less-loader'}
]
}
]
}
}图片文件处理 url-loader file-loader img-loader postcss-sprites
npm install url-loader file-loader -D
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
41module.exports = {
module: {
rules: [
{
test: /\.(png|jpg|jpeg|gif)$/, use: [
{
loader: 'url-loader',
options: {
limit: 8192, // 小于8Kb对图片进行base64编码
// 大于limit限制
// images/ 文件要打包的到的文件夹
// [name] 图片原来的名称
// [hash:8]为了防止图片名称冲突,依然使用hash只保留8位
// [ext] 使用图片原来的的扩展名
name: 'images/[name].[hash:8].[ext]'
}
},
{
loader:'img-loader',
options: [
pngquant: { quality: 80 }
],
}
]
},
test: /\.css$/, use: [
{ loader: 'style-loader'}, // 将css样式嵌入到文档中
{ loader: 'css-loader'} // 负责加载css文件
{
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: [ require('postcss-sprites')({
spritePath: 'dist/assets/imgs/sprites'
})]
}
} // 负责加载css文件
]
]
}
}
es6语法处理 babal-loader
npm install --save-dev babel-loader@7 babel-core babel-preset-es2015或babel-preset-env -D
babel polyfill
对低版本浏览器不支持的 generator set map promise等做兼容性处理 全局使用 开发应用使用npm i babael-polyfill -save
import babel-polyfill
babel runtime transform
局部使用 开发框架使用npm i babel-runtime babel-plugin-transform-runtime -D
在.babelrc中添加"plugins":["transform-runtime"]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23module.exports = {
module: {
rules: [
{
test: /\.js$/,
exclude: /(node_modules|dist)/ // 不需要转化的js文件
use: [
{
loader: 'babel-loader',
options: {
// presets: ['es2015']
presets: ['env'], {
targets: {
browsers: ['> 1%', 'last 2 versions']
}
} // 包含es2015 es2016 es2017
}
}
]
}
]
}
}
编译typescript
代码分割与懒加载
require.include()
引入共用代码require.ensure([], function(){ var _ = require('lodash') })
开发环境与生产环境异同
开发环境与生产环境的相同点
- 同样的入口
- 同样的代码处理
- 同样的解析配置
开发环境与生产环境的不同点
开发环境
- 提取公用代码
- 压缩混淆
- 文件压缩 或是 base64编码
- 去除无用的代码
开发环境
- 模块热更新
- sourceMap
- 接口代理
- 代码规范检查
打包结果分析
npm i webpack-bundle-analyzer -D
1
2
3
4const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
module.export = {
plugins: [ new BundleAnalyzerPlugin() ]
}
优化打包速度
方法一
webpack.dll.conf.js
- 分vendeor和app 分离第三方代码和业务代码(src)
- 使用 DllPlugin 打包第三方库文件(不会经常改变的开发依赖)
- 使用 DllReferencePlugin 关联第三方依赖库文件
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// webpack.dll.conf.js
const path = require('path');
const webpack = require('webpack');
module.exports = {
entry: {
vendor: ['vue', 'vue-router', 'vuex', 'better-scroll', 'axios', 'fastclick'],
},
output: {
path: path.join(__dirname, '../static/dll/'),
filename: '[name].dll.js',
library: '[name]'
},
plugins: [
new webpack.DllPlugin({
path: path.join(__dirname, '../static/dll/', '[name]-manifest.json'),
name: '[name]'
})
]
}
// package.json 添加dll脚本
"scripts": {
"dll": "webpack -p --progress --config build/webpack.dll.conf.js"
}
// webpack.prod.conf.js 关联第三方库文件
plugins: [
// 关联第三方类库
new webpack.DllReferencePlugin({
manifest: require('../static/dll/vendor-manifest.json')
})
]
// 在index.html模板中引入 vendor.dll.js
/*
使用DllPlugin需要在 第一次构建之前 或者 新加第三方组件库时如果要分离 需要单独执行 npm run dll 生成最新文件
之后每次打包就只会构建src下的业务代码 , 不会在打包第三方公用的文件了
*/
<script src="./static/dll/vendor.dll.js"></script>
方法二
- uglifyJsPlugin 并行处理
1
2
3
4
5
6
7
8
9
10
11const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
new UglifyJsPlugin({
uglifyOptions: {
compress: {
warnings: false
}
},
sourceMap: false, // 去除sourcemap
parallel: true, // 除主线程外所有线程
cache: true // 缓存
}),
方法三
- HappyPack 把串行loader变成并行处理
- HappyPack.ThreadPool
方法四
- babel-loader 设置 options.cacheDirectory include exclude
babel-loader 只处理src下js文件
其他
- 减少resolve
- Devtool 去除 sourcemap
- cache-loader
- 升级node及webpack版本
webpack多页面应用配置
- parallel-webpack
webpack配置代码
vendor提取工程中所使用的的库、框架等第三方模块
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 //path是Nodejs中的基本包,用来处理路径
const path = require('path')
//引入html-webpack-plugin
const HTMLPlugin = require('html-webpack-plugin')
//引入webpack
const webpack = require("webpack")
//将css代码抽离出来打包成单独文件
const ExtractPlugin = require("extract-text-webpack-plugin")
//判断是否为测试环境,在启动脚本时设置的环境变量都是存在于process.env这个对象里面的
const isDev = process.env.NODE_ENV === "development"
const config = {
//设置webpack的编译目标是web平台
target: "web",
//声明js文件入口,__dirname就是我们文件的根目录,用join拼接
entry: path.join(__dirname, 'src/index.js'),
//声明出口文件
output: {
//将挂载的App全部打包成一个bundle.js,在浏览器中可以直接运行的代码
filename: 'bundle.js',
//bundle.js保存的位置
path: path.join(__dirname, 'dist')
},
//因为webpack只能处理js文件,且只识别ES5的语法
module: {
//所以针对不同类型的文件,我们定义不同的识别规则,最终目的都是打包成js文件
rules: [
//处理.vue文件
{
test: /\.vue$/,
loader: 'vue-loader'
},
//处理jsx文件
{
test: /\.jsx$/,
loader: 'babel-loader'
},
// {
// test: /\.css$/,
// use: [
// 'style-loader', //将css的样式写入到html里面去
// 'css-loader' //处理css文件
// ]
// },
//处理图片
{
test: /\.(gif|jpg|jpeg|png|svg)$/,
use: [
{
//url-loader实际上依赖于file-loader,file-loader处理完文件可以保存为一个文件供处理
loader: 'url-loader',
//loader是可以配置选项的,如下options
options: {
//url-loader的好处是可以加一个限制的大小,对于小图片,在范围内可直接将图片转换成base64码直接存放在js中,以减少http请求.
limit: 1024,
//输出文件的名字,[name] 文件原名,[ext]文件扩展名.
name: '[name].[ext]'
}
}
]
}
]
},
plugins: [
//主要作用是在此处可以根据isdev配置process.env,一是可以在js代码中可以获取到process.env,
new webpack.DefinePlugin({
//二是webpack或则vue等根据process.env如果是development,会给一些特殊的错误提醒等,而这些特殊项在正式环境是不需要的
'process.env': {
NODE_ENV: isDev ? '"development"' : '"production"'
}
}),
//引入HTMLPlugin
new HTMLPlugin()
]
}
//如果是测试环境下的一些配置
if (isDev) {
config.module.rules.push({
test: /\.styl/,
use: [
'style-loader', //将css写入到html中去
'css-loader', //css-loader处理css
{
loader: 'postcss-loader',
options: {
//stylus-loader和postcss-loader自己都会生成sourceMap,如果前面stylus-loader已生成了sourceMap
//那么postcss-loader可以直接引用前面的sourceMap
sourceMap: true,
}
},
//处理stylus的css预处理器的问题件,转换成css后,抛给上一层的css-loader
'stylus-loader'
]
})
//官方推荐使用这个配置,作用是在浏览器中调试时,显示的代码和我们的项目中的代码会基本相似,
//而不会显示编译后的代码,以致于我们调试连自己都看不懂
config.devtool = '#cheap-module-eval-source-map'
//这个devServer的配置是在webpack2.x以后引入的,1.x是没有的
config.devServer = {
port: 8000, //访问的端口号
host: '127.0.0.1', //可以设置0.0.0.0 ,这样设置你可以通过127.0.0.1或则localhost去访问
overlay: {
errors: true, //编译中遇到的错误都会显示到网页中去
},
open: true, //项目启动时,会默认帮你打开浏览器
hot: true //在单页面应用开发中,我们修改了代码后是整个页面都刷新,开启hot后,将只刷新对应的组件
},
//添加两个插件用于hot:true的配置
config.plugins.push(
new webpack.HotModuleReplacementPlugin(),
new webpack.NoEmitOnErrorsPlugin()
)
} else {
config.entry = {
app: path.join(__dirname, 'src/index.js'),
vendor: ['vue']
}
//此处一定是chunkhash,因为用hash时app和vendor的hash码是一样的了,这样每次业务代码更新,vendor也会更新,也就没有了意义.
config.output.filename = '[name].[chunkhash:8].js'
config.module.rules.push({
test: /\.styl/,
use: ExtractPlugin.extract({
fallback: 'style-loader',
use: [
'css-loader', //css-loader处理css
{
loader: 'postcss-loader',
options: {
//stylus-loader和postcss-loader自己都会生成sourceMap,如果前面stylus-loader已生成了sourceMap
//那么postcss-loader可以直接引用前面的sourceMap
sourceMap: true,
}
},
//处理stylus的css预处理器的问题件,转换成css后,抛给上一层的css-loader
'stylus-loader'
]
})
}),
config.plugins.push(
//定义打包分离出的css文件名
new ExtractPlugin('styles.[contentHash:8].css'),
//定义静态文件, 库文件, 插件打包
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor'
}),
//将app.js文件中一些关于webpack文件的配置单独打包出为一个文件,用于解决部分浏览器长缓存问题
new webpack.optimize.CommonsChunkPlugin({
name: 'runtime'
})
)
}
//声明一个config的配置,用于对外暴露
module.exports = config