项目刚发布的时候.npm run build打包出来的chunk-vendor.js体积达到了2.5m,我滴妈,服务器带宽又很低,加载实在是太慢了,然后我做了以下这些事情 (像路由懒加载什么的就不提了)。
1.因为项目中使用了charts,做了echarts的按需加载。
本来直接在Main.js中:
import echarts from 'echarts'
Vue.prototype.$echarts = echarts
现在改为在echarts组件里引入
var echarts = require('echarts/lib/echarts');
require('echarts/lib/chart/line') // 按需导入折线组件
require('echarts/lib/component/tooltip') // 提示组件
require('echarts/lib/component/legend') // 图例组件
2.使用uglifyOptions去除console来减小文件大小
// 安装uglifyjs-webpack-plugin
cnpm install uglifyjs-webpack-plugin --save-dev
// 修改vue.config.js
configureWebpack: config => {
if (isProduction) {
.....
config.plugins.push(
new UglifyJsPlugin({
uglifyOptions: {
compress: {
warnings: false,
drop_debugger: true,
drop_console: true,
},
},
sourceMap: false,
parallel: true,
})
)
}
}
3.服务器开启Gzip
// 安装插件
cnpm i --save-dev compression-webpack-plugin
// 在vue-config.js 中加入
const CompressionWebpackPlugin = require('compression-webpack-plugin');
const productionGzipExtensions = ['js', 'css'];
const isProduction = process.env.NODE_ENV === 'production';
.....
module.exports = {
....
configureWebpack: config => {
if (isProduction) {
config.plugins.push(new CompressionWebpackPlugin({
algorithm: 'gzip',
test: new RegExp('\\.(' + productionGzipExtensions.join('|') + ')$'),
threshold: 10240,
minRatio: 0.8
}))
}
}
}
需要注意的是,这种优化方法非常有效,大改能减少2/3的体积,但是需要服务器端也支持,因为它会在打包过程中生成.gz结尾的压缩包,请求资源的时候就直接加载这个压缩包里的文件了。这里实力nginx的配置方法:
添加以下代码:
gzip on;
gzip_min_length 1k;
gzip_comp_level 9;
gzip_types text/plain application/javascript application/x-javascript text/css application/xml
text/javascript application/x-httpd-php image/jpeg image/gif image/png;
gzip_vary on;
gzip_disable "MSIE [1-6]\.";
nginx.conf最终内容如下:
#user nobody;
worker_processes 1;
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';
#access_log logs/access.log main;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
#gzip on;
upstream njsolar {
server localhost:9000; #Apache
}
server {
listen 8083;
server_name localhost;
gzip on;
gzip_min_length 1k;
gzip_comp_level 9;
gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png;
gzip_vary on;
gzip_disable "MSIE [1-6]\.";
#charset koi8-r;
#access_log logs/host.access.log main;
location / {
root /usr/local/deploy/njsolar-ui/dist;
index index.html index.htm;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}
}
}
4.使用CDN托管
大型Web应用对速度的追求并没有止步于仅仅利用浏览器缓存,因为浏览器缓存始终只是为了提升二次访问的速度,对于首次访问的加速,我们需要从网络层面进行优化,最常见的手段就是CDN(Content Delivery Network,内容分发网络)加速。通过将静态资源缓存到离用户很近的相同网络运营商的CDN节点上,不但能提升用户的访问速度,还能节省服务器的带宽消耗,降低负载。不同地区的用户会访问到离自己最近的相同网络线路上的CDN节点,当请求达到CDN节点后,节点会判断自己的内容缓存是否有效,如果有效,则立即响应缓存内容给用户,从而加快响应速度。如果CDN节点的缓存失效,它会根据服务配置去我们的内容源服务器获取最新的资源响应给用户,并将内容缓存下来以便响应给后续访问的用户。因此,一个地区内只要有一个用户先加载资源,在CDN中建立了缓存,该地区的其他后续用户都能因此而受益。
//vue.config.js头部插入
const CompressionWebpackPlugin = require('compression-webpack-plugin')
// 是否为生产环境
const isProduction = process.env.NODE_ENV !== 'development'
// 本地环境是否需要使用cdn
const devNeedCdn = true
// cdn链接
const cdn = {
// cdn:模块名称和模块作用域命名(对应window里面挂载的变量名称)
externals: {
vue: 'Vue',
vuex: 'Vuex',
'vue-router': 'VueRouter',
'element-ui':'ELEMENT'
},
// cdn的css链接
css: [],
// cdn的js链接
js: [
'https://cdn.staticfile.org/vue/2.6.10/vue.min.js',
'https://cdn.staticfile.org/vuex/3.0.1/vuex.min.js',
'https://cdn.staticfile.org/vue-router/3.0.3/vue-router.min.js',
'https://cdn.bootcss.com/element-ui/2.13.2/index.js'
]
}
//module.exports 内插入
chainWebpack:config =>{
// ============注入cdn start============
config.plugin('html').tap(args => {
// 生产环境或本地需要cdn时,才注入cdn
if (isProduction || devNeedCdn) args[0].cdn = cdn
return args
})
config.plugin('webpack-bundle-analyzer').use(require('webpack-bundle-analyzer').BundleAnalyzerPlugin)
// ============注入cdn start============
},
vue.config.js的最终代码内容如下:
// 代码压缩
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
const CompressionWebpackPlugin = require('compression-webpack-plugin')
// 是否为生产环境
const isProduction = process.env.NODE_ENV !== 'development'
// 本地环境是否需要使用cdn
const devNeedCdn = true
// cdn链接
const cdn = {
// cdn:模块名称和模块作用域命名(对应window里面挂载的变量名称)
externals: {
vue: 'Vue',
vuex: 'Vuex',
'vue-router': 'VueRouter',
'element-ui':'ELEMENT'
},
// cdn的css链接
css: [],
// cdn的js链接
js: [
'https://cdn.staticfile.org/vue/2.6.10/vue.min.js',
'https://cdn.staticfile.org/vuex/3.0.1/vuex.min.js',
'https://cdn.staticfile.org/vue-router/3.0.3/vue-router.min.js',
'https://cdn.bootcss.com/element-ui/2.13.2/index.js'
]
}
let path = require('path')
module.exports = {
productionSourceMap: false, //优化打包体积
publicPath: './',
lintOnSave: false,
pluginOptions: {
'style-resources-loader': {
preProcessor: 'stylus',
patterns: [
path.resolve(__dirname, 'src/assets/stylus/*.styl')
// 打开之后common.styl的加载顺序有问题
]
}
},
pwa: {
iconPaths: { // 修改favicon favicon.ico这个路径不对,所以不显示favicon.ico的,需要改成正确的才会显示
favicon32: 'favicon.ico',
favicon16: 'favicon.ico',
appleTouchIcon: 'favicon.ico',
maskIcon: 'favicon.ico',
msTileImage: 'favicon.ico'
}
},
chainWebpack:config =>{
// ============注入cdn start============
config.plugin('html').tap(args => {
// 生产环境或本地需要cdn时,才注入cdn
if (isProduction || devNeedCdn) args[0].cdn = cdn
return args
})
config.plugin('webpack-bundle-analyzer').use(require('webpack-bundle-analyzer').BundleAnalyzerPlugin)
// ============注入cdn start============
},
configureWebpack: config => {
// 用cdn方式引入,则构建时要忽略相关资源
if (isProduction || devNeedCdn) config.externals = cdn.externals
// 生产环境相关配置
if (isProduction) {
// 代码压缩
config.plugins.push(
new UglifyJsPlugin({
uglifyOptions: {
//生产环境自动删除console
compress: {
// warnings: false, // 若打包错误,则注释这行
drop_debugger: true,
drop_console: true,
pure_funcs: ['console.log']
}
},
sourceMap: false,
parallel: true
})
)
// 代码压缩
// ..................
// gzip压缩
const productionGzipExtensions = ['html', 'js', 'css']
config.plugins.push(
new CompressionWebpackPlugin({
filename: '[path].gz[query]',
algorithm: 'gzip',
test: new RegExp(
'\\.(' + productionGzipExtensions.join('|') + ')$'
),
threshold: 10240, // 只有大小大于该值的资源会被处理 10240
minRatio: 0.8, // 只有压缩率小于这个值的资源才会被处理
deleteOriginalAssets: false // 删除原文件
})
)
}
// 生产环境相关配置
}
}
还有个小技巧:使用webpack-bundle-analyzer能帮助你分析哪个模块占用了你多大的空间
再来打个包看看现在多大:
可以看到现在2.5m到了1126kb,对应的Gzip达到了405kb,让我们看看放到服务器上的效果:
可以看到主要的chunk-vendors只有414kb,虽然加载了6s,那是因为服务器只有1M的带宽,下面的放在cdn上的文件几乎都是秒加载的,很棒,实现了2.5m到400kb的突破。