最近在项目开发中,需要将VUE前端代码打包上传至静态资源服务器上,结果静态资源服务器限制上传的资源每个文件大小不能大于1MB;然而在日常的项目开发中,我们会遇到各种第三方插件来提高效率,但随之带来的问题就是打包后的vendor.js文件体积过大,导致加载时空白页事件过长,而且首屏加载慢,给用户的体验太差。为此我们需要减小vendor.js文件的体积;
定位webpack打包慢并且文件大的原因
externals优化项目
vendor.js文件过大,这时候用webpack提供的externals属性可以解决以上的问题,像vue.js、vuex.js、vue-router.js这些第三方,基本不会变的;如果将它们独立出来单独加载就能利用浏览器的缓存机制,不用每次都重新加载这些第三方库文件,并且大大减小了vendor.js文件的大小;
- 添加配置如下
webpack的外部扩展(externals)可以有效的解决;externals配置选项提供了从输出的bundle中排除依赖的方法;相反,所创建的bundle依赖于那些存在用户环境中的依赖。防止将某些import的包打包到bundle中,而是在运行的时候再去从外部获取这些扩展依赖;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# build/webpack.base.conf.js
var path = require('path');
var utils = require('./utils');
var config = require('../config');
var vueLoaderConfig = require('./vue-loader.conf');
function resolve(dir) {
return path.join(__dirname, '..', dir);
}
module.exports = {
entry: {
app: './src/main.js'
},
output: {
path: config.build.assetsRoot,
filename: '[name].js',
publicPath: process.env.NODE_ENV === 'production' ? config.build.assetsPublicPath : config.dev.assetsPublicPath
},
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'vue$': 'vue/dist/vue.esm.js',
'@': resolve('src')
}
},
module: {
rules: [
{
test: /\.vue$/,
loader: /vue-loader/,
options: vueLoaderConfig
},
{
test: /\.js$/,
loader: 'babel-loader',
options: {
cacheDirectory: true
},
include: [resolve('src'), resolve('test')]
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('img|[name].[hash:7].[ext]')
}
},
{
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('media/[name].[hash:7].[ext]')
}
},
{
test: /\.(woff2?\eot|ttf|otf)(\.?*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
}
},
{
test: /\.s[a|c]ss$/,
loader: 'sass-loader',
/*options: {
incluedePaths: [resolve('src'), resolve('test')]
}*/
}
]
},
# externals中的key是后面需要require的名字,value是第三方库暴露出来的方法名
externals: { // 放到common.min.js
'vue': 'Vue',
'element-ui': 'ELEMENT',
'vuex': 'Vuex',
'vue-loader': 'VueRouter',
'axios': 'axios'
}
}
全局引入第三方的库
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24# main.js
import Vue from 'vue'
import App from './App'
import router from './router'
import axios from 'axios'
import '@/theme/index.css'
import ElementUI from 'element-ui'
import Utils from './utils/utils'
import Constant from './utils/constant'
import request from './util/request'
import Menus from './utils/menus'
import Loading from './utils/loading'
Vue.use(ElementUI);
Vue.use(Loading);
Vue.prototype.$axios = axios;
Vue.prototype.$request = request;
Vue.prototype.$menus = menus;
Vue.prototype.$utils = Utils;
Vue.prototype.$constant = Constant;全局引入静态的资源
1
2
3
4
5
6
7
8
9
10
11
12# index.html
<!DOCTYPE html>
<html>
<head></head>
<body>
<div id="app"></div>
<script type="test/javascript" src="./src/assets/js/lib/common.min.js"></script>
<script type="test/javascript" src="./src/assets/js/lib/element-ui.js"></script>
</body>
</html>
注释: 这里全局引入的第三方库,当然可以从CND官网进行引入,但是为了安全性和可维护性,所以将第三方库下载至本地进行路径的引用;
优化第三方库
项目中对库的使用有点儿混乱,有些安装了但是很少使用到或者根本没有用到;但是又在webpack中的vender入口指定打包,造成不必要的麻烦,所以需要评估每个第三方库是否有必要安装;
按需引入第三方库的组件
echart是一个比较大的第三方库,或许项目中我们就是用了其中的饼状图和条形图,但是引入整个第三方库会加重浏览器加载的负荷,所以这里我们按需引入响应的组件;包括ElementUI也会可以按需引入相应的组件;
路由懒加载
当打包构建应用的时候,Javascript包会变得非常的大,影响页面的加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应的组件,这样就更加高效了;
结合Vue的异步组件和webpack代码分割功能轻松实现路由的懒加载;
首先,可以讲异步组件定义为返回一个Promise的工厂函数1
const Foo = () => Promise.resolve({/* 组件定义对象
第二,在Webpack 2中,我们可以使用动态import语法来定义代码分割点1
import('./Foo.vue') // 返回Promise
结合两者,这就是如何定义一个能够被Webpack自动代码分割的异步组件;1
2
3
4
5
6
7
8const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo
}
]
})
把组件按组分开
有时候我们想把某个路由下的所有组件都打包在同一个异步块中。只需要使用名字命名chunk一个特殊的注释语法来提供chunk name(需要 Webpack > 2.4)
1 | const Foo = () => import(/* webpackChunkName: 'group-foo' */ './Foo.vue') |
Webpack会将任何一个异步模块于相同的块名称组合到相应的异步块中
webpack配置文件区分开发环境和生产环境
生产环境不要包含HotModuleReplacementPlugin
和NoEmitOnErrorsPlugin
等没必要的插件;
拆开vendor
webpack默认是将依赖打包成一个文件,这样做的好处是减少资源的请求数量,但是当依赖增多的时候,体积增大,一个资源的加载速度就会变慢;所以尝试拆包;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17new webpack.optimize.CommonsChunkPlugin({
name: 'charts',
chunks: ['vendor'],
minChunks: module => module.resource.indexOf('highcharts') > -1
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'utils',
chunks: ['vendor'],
minChunks: module => module.resource.indexOf('lodash') > -1
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'ui',
chunks: ['vendor'],
minChunks: module => module.resource.indexOf('element-ui') > -1
})
拆包后的结果如下:1
2
3static/js/vender.snaskdads123.js 251KB 1 [emitted] vender
static/js/ui.sdfjafjk.js 211KB 1 [emitted] ui
static/js/charts.asfdjasdad.js 151KB 1 [emitted] charts
分离出webpack runtime代码
webpack在客户端运行时首先会加载webpack相关的代码, 例如require函数等,这部分代码会随着每次修改业务代码后发生变化;原因是这里面会包含chunk id
等容易变化的信息;
如果不抽取出来将会被打包在vendor中,导致vendor每次都要被用户重新加载,vendor也就失去了它原有的意义;所以配置要分离出来,具体代码如下:
1 | # build/webpack.intranet.conf.js |