前端首屏优化的一些总结¶
统计信息:字数 7233 阅读15分钟
开篇¶
很多人在构建前端项目时会发现打包出来的chunk.vendors.xxx.js
文件实在是太大,导致首屏加载速度很慢.这里我把阿童木聊天室的首屏优化步骤分享给大家.
阿童木聊天室在线地址 www.genal.fun
优化流程¶
1. 使用webpack-bundle-analyzer
分析前端项目包大小, 找出问题源头.¶
// vue.config.js
chainWebpack: (config) => {
config.plugin('webpack-bundle-analyzer').use(require('webpack-bundle-analyzer').BundleAnalyzerPlugin);
}
2. 去掉vuecli
打包生成的map文件¶
这些文件用于错误时给出准确的提示,线上环境是不需要的.
// vue.config.js
productionSourceMap: false
3.CDN
引入需要的资源¶
在vue.config.js
中配置不打包相关资源. 这样可以大幅降低项目打包文件大小
// vue.config.js
const cdn = {
css: [
// antd css 由于引入失败只好放弃了antd的按需引入
],
js: [
// vue
'https://cdn.bootcdn.net/ajax/libs/vue/2.6.10/vue.min.js',
// vue-router
'https://cdn.bootcdn.net/ajax/libs/vue-router/3.1.3/vue-router.min.js',
// vuex
'https://cdn.bootcdn.net/ajax/libs/vuex/3.1.2/vuex.min.js',
// axios
'https://cdn.bootcdn.net/ajax/libs/axios/0.18.0/axios.min.js',
// moment
'https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.18.1/moment.js',
],
};
chainWebpack: (config) => {
// 正式环境配置cdn引入
if (process.env.NODE_ENV === 'production') {
let externals = {
vue: 'Vue',
axios: 'axios',
'vue-router': 'VueRouter',
vuex: 'Vuex',
moment: 'moment',
};
config.externals(externals);
// 通过 html-webpack-plugin 将 cdn 注入到 index.html 之中
config.plugin('html').tap((args) => {
args[0].cdn = cdn;
return args;
});
}
},
然后在public/index.html
中加入以下代码, 正式环境才走cdn
<% if (process.env.NODE_ENV === 'production') { %>
<% for(var css of htmlWebpackPlugin.options.cdn.css) { %>
<link rel="stylesheet" href="<%=css%>" as="style">
<% } %>
<% for(var js of htmlWebpackPlugin.options.cdn.js) { %>
<script src="<%=js%>"></script>
<% } %>
<% } %>
开启了cdn
加载资源后, 线上环境没有被webpack
打包的资源都走了cdn
加载, 如图
4. 开启gzip
打包¶
// vue.config.js
const path = require('path');
const PrerenderSPAPlugin = require('prerender-spa-plugin');
const Renderer = PrerenderSPAPlugin.PuppeteerRenderer;
configureWebpack: (config) => {
// 代码 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, // 删除原文件
})
);
},
同时需要配置nginx
才可支持gzip
// nginx.conf
http {
#nginx开启gzip
#前端文件在build的时候已经配置好压缩,需要再配置一下nginx;
gzip on;
gzip_static on;
gzip_buffers 4 16k;
gzip_comp_level 5;
gzip_types text/plain application/javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg
image/gif image/png;
}
配置完之后可以看到线上环境的资源会以gzip
的格式传输
5. 配置路由懒加载¶
这样能让打包出来的代码分割, 而不会统统堆一个文件里. 这样的好处是首屏渲染在用户眼里更快了.
// router.js
const routes: Array<RouteConfig> = [
{
path: '/',
name: 'Chat',
component: () => import('@/views/GenalChat.vue'),
},
];
6. 服务端渲染(SSR)/预渲染(Prerendering)¶
由于服务端渲染对代码侵入性比较高,因此我使用了预渲染方案.
// vue.config.js
configureWebpack: (config) => {
// 预渲染
return {
plugins: [
// 预渲染配置
new PrerenderSPAPlugin({
// 输出预渲染文件的路径。
staticDir: path.join(__dirname, 'dist'),
// 预渲染的路由
routes: ['/'],
// 要使用的实际渲染器
renderer: new Renderer({
// 等待渲染,直到检测指定事件
renderAfterDocumentEvent: 'render-event',
args: ['--no-sandbox', '--disable-setuid-sandbox'], // chentos8 构建失败需要加
}),
}),
],
};
},
特别提醒
1. 第5条和第6条是冲突的! 配置了预渲染的路由不可以使用路由懒加载!
2. linux环境下构建预渲染代码很可能会报一些奇怪的错误, 需要百度解决!
3. 坑是真的多...
更多思路¶
- 静态资源使用
cdn
加速- 浏览器缓存
- 各种库的按需引入
- 首屏加载太久时使用
loading
效果- why not use
promise.all
?promise.all
可以并发多个没有关联的数据请求(如获取多个用户的头像),提高资源加载速度,充分发挥js
异步的特性.
优化前后文件对比¶
优化前 优化后
- 需要注意的是优化前并没有开启
gzip
, 所以优化前资源大小参考图中Size
栏. - 优化效果用
chunk-vendors.js
文件来说, 就是优化前 1544kb, 优化后 346kb !
总结¶
通过使用上述方法优化前端代码,我用 1m 带宽的腾讯云服务器居然做到了 3s 内渲染完成, 原本不做任何优化的加载速度是 12s+!!! 通过首屏优化, 网站响应速度足足快了 四倍以上 !
一些疑问¶
如果用webpack
的splitchunks
功能将chunk-vendors.xxx.js
文件拆分成多个小文件,通过http
多路并行加载会不会比以前快?
答案: 很遗憾,基本不会变快.我亲自试验过, splitchunks不会带来任何的性能提升.直接给图
- 不拆分
chunk-vendors.xxx.js
- 拆分
chunk-vendors.xxx.js
成多个小文件
从图中看其实还变慢了,原因是在单条tcp连接传输多个文件,即便复用连接,浏览器在文件与文件之间仍然要停一下发送一次HTTP的请求头,这里会造成多一点的时间损耗.
有个文章写得不错,推荐一下 合并HTTP请求 vs 并行HTTP请求,到底谁更快?