对于一个网站来讲打开速度是一个很重要的指标,只是大部分时间内咱们的精力可能都用来对付需求了,特别是当咱们作的是一些内部的项目时,咱们经常的会忽略了这一方面的优化。其实要对一个页面的打开速度作出一些比较常见的优化并无想象中的困难,本文将带你作一些既不费力也不费时间的优化操做,这些操做中涉及到压缩,缓存,preload加载关键资源,prefetch缓存懒加载资源与一些引用组件的建议及常见的工具库处理。javascript
当咱们使用webpack打包并压缩js代码后,每每某些js(好比vendor)依然会很大,可能会达到1mb左右的大小,虽然以如今的带宽来讲若是咱们是pc端项目这也不是什么大问题,可是这里面明显是存在着至关大的优化空间的,gzip就是一种形式。 在浏览器的请求头里包含着这样一句话Accept-Encoding:gzip, deflate
,这告诉咱们浏览器是能够识别gzip压缩的,使用gzip压缩后的文件将大大减少,不少状况下甚至能压缩70%。如今的服务基本上都是使用nginx作转发的,对于咱们来讲开启gzip其实至关容易,只要配置以下的代码就能够了。css
server {
gzip on;
gzip_types text/xml text/css text/plain text/javascript application/javascript application/x-javascript;
}
复制代码
上图能看到没开启gzip时vendor近800k,而开启gzip后大概只有250k。html
能够说若是咱们连gzip都没有开启的话,其余任何优化都显得有点多余,由于大概没有另一种优化方式能压缩如此高的比例。vue
除了常见的gzip压缩外,另外一个能够利用的优化点就是浏览器的缓存。我会顺带着介绍一下浏览器的缓存方式,可是不会过于详细,有兴趣的同窗能够额外去找一些资料学习。java
浏览器分为两种缓存,强缓存与协商缓存(也被称为弱缓存),其中协商缓存不用咱们本身配置,下面咱们经过连续两次刷新页面来观察一下协商缓存。 webpack
Last-Modified: sometime
location ~* \.(css|js)$ {
proxy_set_header Host $host;
proxy_pass http://tomcat_xxx;
expires 7d;
}
location ~* \.(jpg|jpeg|png|gif|webp)$ {
proxy_set_header Host $host;
proxy_pass http://tomcat_xxx;
expires 30d;
}
复制代码
注意咱们这里使用的是tomcat,你可能须要的配置与我这个并不同,可是这并不关键,咱们主要须要的是expires这项配置,他表示了咱们但愿缓存的时间,咱们配置的js与css缓存时间为10天,而图片则缓存30天。一块儿打开浏览器看一下效果。nginx
from memory cache
代表此时是从内存中直接取出缓存,并无发送http请求,这对一些图片与咱们的依赖包vendor至关有用,咱们彻底能够给这两个资源设定一个较大的缓存时间,这样当用户访问第一次后,这些资源始终会保持在用户的缓存中,就算咱们以后更改了不少咱们的业务代码,只要依赖没有更改,用户只用加载一些小的业务代码文件就能够了,对于较大的vendor则依然能够从缓存中获取。
咱们能够简要的总结一下浏览器的缓存方式并增长一些注意的点。浏览器会首先检测强缓存,若是命中则直接返回缓存文件,不会发送http请求,若是没有命中则去检查弱缓存,当弱缓存命中时返回304状态码,浏览器依然从缓存中获取资源,若是弱缓存也没有命中则返回200状态码从新加载服务器上的资源。git
注意点:github
max-age=0或是no-cache
,注意我这里说的当前请求资源指的通常是你页面的html文档,可是对于文档中外链的js与img等,不会由于刷新致使强缓存失效。不过若是你直接请求的是一个js文件,那么刷新后这个js文件强缓存也会失效。因为咱们的技术栈是vue,因此如下示例咱们用vue来进行演示,可是本质上不管是什么技术栈都是同样的。 假设咱们的项目是单页面应用那么首先应该优化的点就是路由的懒加载,也就是说不要一次性的将全部代码一块儿返回,只有切换到当前路由时咱们才去请求当前路由对应的代码。对于vue-cli初始化的项目来讲配置十分的简单,在router中更改一下import的方式就能够。web
const router = new Router({
routes: [
{
path: '/',
redirect: '/a',
},
{
path: '/a',
component: () => import('../components/a/index.vue'),
},
{
path: '/b',
component: () => import('../components/b/index.vue'),
},
]
复制代码
如今咱们就能够根据咱们访问的router动态的加载js文件了。可是这样其实还有优化的空间,假设咱们如今请求路由a,加载了vendor等公共js与a自己的js,那么在访问a页面的空余时间里为何咱们不将b路由的js也对应的加载到浏览器的缓存中那,这样当用户切换到b路由时就能够不用在发送http请求而是直接使用缓存中的文件就能够了。
在这里咱们要用到一个webpack插件,PreloadWebpackPlugin
,这个插件的做用是帮助咱们对应的生成<link rel="preload" href="xxxx">
与<link rel="prefetch" href="xxxx">
标签,其中preload中href的资源浏览器会优先的进行加载,关于preload的做用mdn文档是如此说的。
在浏览器的主渲染机制介入前就进行预加载。这一机制使得资源能够更早的获得加载并可用,且更不易阻塞页面的初步渲染,进而提高性能。
具体相关其实就是浏览器的关键路径的知识,这里不详述,能够另找资料。
而对于prefetch的href浏览器会进行预加载,一样这里引用mdn文档中的话对其描述
其利用浏览器空闲时间来下载或预取用户在不久的未来可能访问的文档。网页向浏览器提供一组预取提示,并在浏览器完成当前页面的加载后开始静默地拉取指定的文档并将其存储在缓存中。当用户访问其中一个预取文档时,即可以快速的从浏览器缓存中获得。
因此对于vue-cli生成的项目要作的是用preload加载vendor、manifest与app三个js而用prefetch去加载全部路由对应的文件。这样当咱们访问路由a时会首先下载须要的js与css,而后浏览器会自动的加载其余的路由文件。此时当用户去访问其余路由时就不会点击时才去发送请求。在webpack.prod.conf.js中加入以下代码,注意放在new HtmlWebpackPlugin()
的下面,因为咱们的项目中只是用js与css组成的,你能够本身配置img与font这类资源。
new PreloadWebpackPlugin({
rel: 'prefetch',
}),
new PreloadWebpackPlugin({
rel: 'preload',
as(entry) {
if (/\.css$/.test(entry)) return 'style'
return 'script';
},
include: ['app', 'vendor', 'manifest']
})
复制代码
不少第三方组件中让咱们使用的方式是在main.js中引入组件,而后经过Vue.component()
来注册全局组件,其实不少状况下咱们不该该采用这种方案,由于这会增大vendor的体积,特别是当此组件只是在其中的一个路由中用到,放入vendor中就更是不合理的,由于缓存这个组件的代码对咱们并无不少好处,假设页面有5个路由,至关于进其余4个路由时这些代码都是没有意义的,因此不少状况下局部注册组件将其打包进到路由代码中是更好的选择。额外提一句,若是是echarts这种巨型库,仍是建议打包进vendor中的,由于因为业务代码老是在变动的,因此路由代码的hash值老是在变化,echarts这种重量的代码就不要每次上线都让用户从新加载一遍了...
关于这一点我再思考以后以为我多是错的,由于像引入的第三方组件咱们是做为外部依赖来使用的,就算是体积较小,每次打包进路由对应的js里也会由于业务代码的变更致使用户从新加载这部分代码,而体积大的时候就像我上文说的更不能打包进业务代码中。我能想到的场景只有一个状况可能有些不同,就是当代码上线后咱们开发新的需求,须要引入新的依赖,但咱们不但愿用户缓存的vendor失效,因此咱们能够打包一个新的依赖包出来,但这样就提升了http的请求数量,咱们知道在http1中咱们优化的方向是域名分散与下降请求数,这样到底好很差见仁见智吧,反正我应该是不会这么作的。
咱们常见的工具库好比lodash与moment其实都至关至关的大,而每每咱们只是用几个小功能而已,为了用这些小功能引入这么巨大的库真的有些浪费,这里提一些基础的解决方案。
lodash:安装lodash-webpack-plugin
babel-plugin-lodash
在.babelrc中配置"plugins": ["lodash"]
,去按需引入lodash,可是说实话,按需引入有时候也不小,若是真的用到几个很简单的功能本身写未必不是一个更好的选择。
moment: 用day.js去替换。仅2kb大小的库,与moment同样的api,简直不能再赞,我没看源码,不知道2kb是怎么作到与moment同样的功能的,难道是moment实现的太笨了吗...有点费解
还有相似echarts这种巨型的库也有按需引入的方式,你们能够本身试着优化本身vendor的大小