Webpack 2.x 在Vue2.x项目中的应用
最近开发的项目里使用了
Webpack
+Vue
全家桶技术栈,鉴于Webpack的版本迭代还在高速发展期,我使用了Webpack2.x的稳定版本2.2.0
来作为脚手架的打包构建工具。
# 管理资源
# 加载CSS
style-loader
css-loader
less-loader
extract-text-webpack-plugin
项目中使用了 less
作为css的预处理,并且使用 extract-text-webpack-plugin
插件抽取css到单独的文件中。
_.cssLoader = config.cssModules
? 'css-loader?-autoprefixer&modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]'
: 'css-loader?-autoprefixer'
_.cssProcessors = [
{
loader: '',
test: /\.css$/
}, {
loader: 'less-loader?sourceMap',
test: /\.less$/
}
]
// extract css in standalone css files
_.cssProcessors.forEach(processor => {
base.module.loaders.push({
test: processor.test,
loader: ExtractTextPlugin.extract({
use: [_.cssLoader],
fallback: 'style-loader'
})
})
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 加载Javascript & ES6
babel-loader
{
test: /\.js$/,
loaders: ['babel-loader?cacheDirectory=true'],
exclude: [/node_modules/]
},
{
test: /\.es6$/,
loaders: ['babel-loader?cacheDirectory=true']
}
2
3
4
5
6
7
8
9
10
# 加载图片 & 加载字体
file-loader
{
test: /\.(svg|ico|jpg|png|gif|eot|otf|webp|ttf|woff|woff2)(\?.*)?$/,
loader: 'file-loader',
query: {
limit: 10000,
name: 'static/media/[name].[hash:8].[ext]'
}
}
2
3
4
5
6
7
8
9
# 加载Vue单页文件
vue-loader
{
test: /\.vue$/,
loaders: ['vue-loader']
}
2
3
4
5
# 管理输出
# html自动绑定bundle
HtmlWebpackPlugin
用来在打包后根据模版自动重新生成html文件并注入bundle。
new HtmlWebpackPlugin({
title: config.title,
template: path.resolve(__dirname, 'index.html'),
filename: 'page.html'
})
2
3
4
5
6
# 清理 /dist 文件夹
clean-webpack-plugin
new CleanWebpackPlugin(['dist'])
2
# 开发工具
# 使用 source map
为了更容易地追踪错误和警告,JavaScript 提供了 source map
功能,将编译后的代码映射回原始源代码
# 使用 webpack-dev-server
webpack-dev-server
提供了一个简单的 web 服务器,并且能够实时重新加载(live reloading)
# 启用 HMR
Vue中使用vue-loader
实现 vue
组件的 HMR
# Tree Shaking
tree shaking 是一个术语,通常用于描述移除 JavaScript 上下文中的未引用代码(dead-code)
uglifyjs-webpack-plugin
new webpack.optimize.UglifyJsPlugin({
// sourceMap: true,
compress: {
warnings: false,
drop_debugger: true,
drop_console: true
},
output: {
comments: false
}
}),
2
3
4
5
6
7
8
9
10
11
12
# 优化方案与思路
# 分析Webpack打包大小
webpack-bundle-analyzer
一款分析 bundle 内容的插件及 CLI 工具,以便捷的、交互式、可缩放的树状图形式展现给用户。
非常好用的工具,它可以将打包后的文件树按打包大小展示为可视化的直观图。轻松定位有哪些构建后引入的包占比比较大,我们可以分析这些引入的包是否多余。进而进行优化。
# 代码压缩
uglifyjs-webpack-plugin
和 webpack-parallel-uglify-plugin
上面讲到过 uglifyjs-webpack-plugin
的配置
下面用到 webpack-parallel-uglify-plugin
插件,多进程进行代码压缩。从而减少构建时间。
new ParallelUglifyPlugin({
cacheDir: '.cache/',
uglifyJS: {
output: {
comments: false,
beautify: false
},
compress: {
warnings: false,
drop_console: true
}
}
})
2
3
4
5
6
7
8
9
10
11
12
13
14
# 多进程构建
happypack
参考官网 happypack
# 缓存与增量构建
针对项目中主要使用的ES6与Vue.js,通过babel-loader
进行编译。babel-loader
可以缓存处理过的模块,对于没有修改过的文件不会再重新编译,cacheDirectory
有着2倍以上的速度提升,这对于 rebuild
有着非常大的性能提升。
{
test: /\.js$/,
loaders: ['babel-loader?cacheDirectory=true'],
exclude: [/node_modules/]
}
2
3
4
5
6
# 引用外部CDN
如果项目环境允许,我们可以把常用的类库,比如 lodash
/moment
这些工具库使用外部CDN在运行时(runtime)引入到项目中。构建的时候减少对这些库的打包。
{
externals: {
lodash : {
commonjs: "lodash",
amd: "lodash",
root: "_" // indicates global variable
}
}
}
2
3
4
5
6
7
8
9
10
这里 lodash
这个外部 library 可以在 AMD
和 CommonJS
模块系统中通过 lodash
访问,但在全局变量形式下用 _
访问
需要把CDN配置在 index.html
中
<script src="https://cdn.bootcss.com/lodash.js/4.17.4/lodash.min.js"></script>
2
# 提取公共模版
CommonsChunkPlugin
插件可以将公共的依赖模块提取到已有的入口 chunk 中,或者提取到一个新生成的 chunk。
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks: ({ resource }) => {
return resource &&
/\.(js|css|es6)$/.test(resource) &&
resource.indexOf('node_modules') !== -1
}
})
2
3
4
5
6
7
8
9
# 分离第三方依赖
DllPlugin
这个插件是在一个额外的独立的 webpack 设置中创建一个只有 dll 的 bundle(dll-only-bundle)。 这个插件会生成一个名为 manifest.json
的文件,这个文件是用来让 DLLReferencePlugin
映射到相关的依赖上去的。我们这里生成的依赖文件为 vendor-manifest.json
。
var path = require('path')
var webpack = require('webpack')
module.exports = {
entry: {
vendor: [
'axios',
'echarts',
'iview',
'lodash',
'moment',
'q',
'qs',
'vue/dist/vue.common.js',
'vuex',
'vue-router',
'vuex-router-sync'
]
},
output: {
path: path.join(__dirname, '../static'),
filename: '[name].dll.js',
library: '[name]_library'
},
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader'
},
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/
}
]
},
plugins: [
new webpack.ContextReplacementPlugin(/moment[\/\\]locale$/, /zh-cn|en-gb/),
new webpack.DllPlugin({
path: path.join(__dirname, '.', '[name]-manifest.json'),
libraryTarget: 'commonjs2',
name: '[name]_library'
}),
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
}
})
],
resolve: {
modules: ['node_modules']
}
}
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
DllReferencePlugin
这个插件是在 webpack
主配置文件中设置的, 这个插件把只有 dll
的 bundle(们)(dll-only-bundle(s)) 引用到需要的预编译的依赖。它通过分析上一步使用 DllPlugin
生成的 vendor-mainfest.json
来把名称映射到模块的id上,生产环境打包过程中就会略过通过 dll 打包的模块。从而减少打包体积,大幅度减少构建时间。
// 分离vendor
new webpack.DllReferencePlugin({
context: path.resolve(__dirname, '..'),
manifest: require('./vendor-manifest.json')
})
2
3
4
5
6
7
copy-webpack-plugin
将预先打包好的dll静态文件拷贝到 dist
目录
new CopyWebpackPlugin([
{
from: _.cwd('./static'),
// to the roor of dist path
to: './'
}
])
2
3
4
5
6
7
8