加入QQ群:864680898,一起学习进步!点击群名可查看本人网站,有最新文章!
前端性能优化————(一)提升加载速度
由于现在大部分是做的单页面应用了,那么会导致页面的首次加载时间非常的长。常见的三大框架Vue,react,angular都会存在这种问题,那么怎么来解决呢?
路由拆分
可以将路由打包拆分,将会生成多个js文件,在路由加载到的时候才加载该js文件,具体实现方案的话,如下:
1、基于require.js来实现引入模块的方式(比较老)
let routes = [
{path: '/home', component: resolve => require(['./components/Home.vue'], resolve), children:[
{path: '/detail', component: resolve => require(['./components/Detail.vue'], resolve)}
]}
]
2、es6模块化的拆分形式
let routes = [
{path: '/home', component: () => import('./components/Home.vue'), children:[
{path: '/detail', component: () => import('./components/Detail.vue'), resolve)}
]}
]
组件按需引入
像这种全局引入会导致最终打包文件过大,首次加载时间非常长
import Vue from 'vue';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);
可以按需引入element的组件,要借助babel的模块插件,那么只会引入你导入的组件的那部分js,没有用到的就不会导入
1、安装babel的插件
npm install babel-plugin-component -D
2、修改.babellrc
{
"presets": [["es2015", { "modules": false }]],
"plugins": [
[
"component",
{
"libraryName": "element-ui",
"styleLibraryName": "theme-chalk"
}
]
]
}
3、在main.js或者vue的组件中只引入需要的组件
import { Button, Select } from 'element-ui';
图片懒加载
没有进入到可视区域的图片如果使用懒加载的话,就可以不加载,等进入可视区域再加载,可以缩短加载时间
在vue中使用一个懒加载的库vue-lazyload:
import VueLazyload from 'vue-lazyload'
Vue.use(VueLazyload, {
preLoad: 1, //预加载高度的比例
error: 'http://cdn.uehtml.com/201402/1392662591495_1140x0.gif', //图像的src加载失败
loading: '', //src的图像加载
attempt: 2, //尝试计数
listenEvents: [ 'scroll', 'mousewheel' ] //你想要监听的事件,我个人喜欢全部监听,方便
});
然后在vue的组件中使用v-lazy
<img v-lazy="myUrl" alt="">
可视化加载
原理和图片懒加载一样,但是我们有的不是图片,但是可能比图片还庞大的资源,肯定得使用可视化加载,如地图,庞大的组件;当然这是在它不必一开始就显示的前提下
webpack打包
1、sourcemap 一个可以从中查看源码的文件,但是在线上环境是没必要的,这个就可以关闭
2、别名的使用,使用别名比使用相对路径在服务器上查找文件更快
module.exports = {
resolve: {
extensions: ['.js', '.vue', '.json'], // 使用这些后缀的文件时就可不写扩展名
alias: {
'vue$': 'vue/dist/vue.esm.js',
'@': resolve('src'),
}
}
}
3、assetsPublicPath设置为/时能被nginx代理识别,但是无法被文件系统识别,但是访问速度比设置成./时快
module.exports = {
build:{
assetsPublicPath: '/',
}
}
有时我们想项目打包出来直接就能访问,完全是基于文件系统,相对路径的识别,只需要做如下更改
module.exports = {
build:{
assetsPublicPath: './',
}
}
4、js文件压缩,webpack使用uglifyjs-webpack-plugin插件压缩js代码
CDN加速
CDN的全称Content Delivery Network,(缩写:CDN)即内容分发网络。
CDN是一个经策略性部署的整体系统,从技术上全面解决由于网络带宽小、用户访问量大、网点分布不均而产生的用户访问网站响应速度慢的根本原因。
CDN目的是通过在现有的Internet中增加一层新的网络架构,将网站的内容发布到最接近用户的网络“边缘”,使用户可以就近取得所需的内容,解决 Internet 网络拥塞状况,提高用户访问网站的响应速度。
一般的公司不可能在每个城市地区都有服务器,那么用户一次完整的请求要经过的历程将路漫漫其修远兮
- 方法1、将比较大的库,如vue,vue-router引用稳定的三方cdn
在index.html引入
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="https://cdn.bootcss.com/vue/2.5.2/vue.min.js"></script>
<script src="https://cdn.bootcss.com/vue-router/3.0.1/vue-router.min.js"></script>
<title>mySkey</title>
</head>
<body>
<div id="app"></div>
</body>
</html>
然后在webpack中配置,修改webpack.base.config.js
module.exports = {
externals: {
'vue': 'Vue',
'vue-router': 'VueRouter'
}
}
- 方法二、将前端的代码托管到有cdn的平台
像腾讯云,阿里云,七牛云这些平台都有cdn功能,可以将打包文件托管在上面来使用
服务端渲染 SSR
简单理解是将组件或页面通过服务器生成html字符串,再发送到浏览器,最后将静态标记"混合"为客户端上完全交互的应用程序
- 优势
1、首屏加载快
相比于加载单页应用,我只需要加载当前页面的内容,而不需要像 React 或者 Vue 一样加载全部的 js 文件(当然,单页应用文件加载过大的情况可以使用 code spliting 来解决)。
2、SEO 优化
对于单页应用,搜索引擎并不能收录到 ajax 爬取数据之后然后再动态 js 渲染出来的页面(个人感觉是搜索引擎自己挖了个坑,然后全互联网跟着填坑)。
- 缺陷
1、线上排错不方便
如果数据请求是 ajax 来进行,出现的问题都能够很方便的通过浏览器的控制台来观察。但是如果是服务端渲染,如果一个后台拼接数据的过程中或者组件书写的过程中出了问题,必须到对应的服务器端(node,java 等)去查看线上日志,再分析 debug。
2、职责混杂
浏览器和服务器端职责混杂,服务器端在一定程度上还得做前端的页面绘制工作(以前写 html 页面,写完之后再转成 jsp,再在里面混写 java 代码调用数据)。
加载顺序上限制,保证首页先显示的内容
首页可能不止请求一个接口,那么这些接口返回的顺序也不一致,我们应该保证能直接进入视野的区域先显示
比如列表从上到下的顺序是热门,最新,分类
<template>
<div id="app">
<div>{{hots}}</div>
<div>{{news}}</div>
<div>{{types}}</div>
</div>
</template>
<script>
export default {
data () {
return {
hots:[],
news:[],
types:[]
}
},
created () {
// 保证最先加载热门的列表
this.getHots()
.then(()=>this.getNews())
.then(()=>this.getTypes())
},
method: {
async getHots(){
return new Promise((resolve,reject)=>{
this.hots = await ajax.get('/hots')
})
},
async getNews(){
return new Promise((resolve,reject)=>{
this.news = await ajax.get('/news')
})
},
async getTypes(){
return new Promise((resolve,reject)=>{
this.types = await ajax.get('/types')
})
}
},
}
</script>
预加载
在当前页面完全加载之后就可以预先加载一些数据,可以加快用户操作时的速度,下面一个例子不太好,但是把原理说明了
<template>
<div id="app">
<img src="https://img.alicdn.com/simba/img/TB14sYVQXXXXXc1XXXXSutbFXXX.jpg" alt="">
<img src="//img.alicdn.com/tfs/TB1iZ6EQXXXXXcsXFXXXXXXXXXX-520-280.jpg_q90_.webp" alt="">
<img src="https://img.alicdn.com/simba/img/TB1C0dOPXXXXXarapXXSutbFXXX.jpg" alt="">
<img src="//img.alicdn.com/tfs/TB1iZ6EQXXXXXcsXFXXXXXXXXXX-520-280.jpg_q90_.webp" alt="">
</div>
</template>
<script>
export default {
data () {
return {
count : 0,
show : false
}
},
mounted () {
var _this = this
let imgs = document.querySelectorAll('img')
Array.from(imgs).forEach((item)=>{
let img = new Image()
img.onload = ()=>{
this.count++
}
img.src=item.getAttribute('src')
})
},
watch : {
count (val,oldval) {
if(val == 4){
this.show = true
alert("加载完毕")
//然后可以对后台发送一些ajax操作
}
}
}
}
</script>
减少DNS查找
有一篇文章写的不错,讲述了从浏览器输入url回车后到页面显示到底发生了什么————从输入url到页面加载完成发生了什么?
每一个不同的域名都会DNS解析,这是很耗时间的,所以要减少域名的使用,当然,正常的项目也只有一个后台一个域名
当我们在浏览器的地址栏输入网址(譬如: www.22family.com) ,然后回车,回车这一瞬间到看到页面到底发生了什么呢?
域名解析 --> 发起TCP的3次握手 --> 建立TCP连接后发起http请求 --> 服务器响应http请求,浏览器得到html代码 --> 浏览器解析html代码,并请求html代码中的资源(如js、css、图片等) --> 浏览器对页面进行渲染呈现给用户
域名解析是页面加载的第一步,那么域名是如何解析的呢?见文章:(后面补上,or自行百度)
DNS也是开销,通常浏览器查找一个给定域名的IP地址要花费20~120毫秒,在完成域名解析之前,浏览器不能从服务器加载到任何东西。那么如何减少域名解析时间,加快页面加载速度呢?
当客户端DNS缓存(浏览器和操作系统)缓存为空时,DNS查找的数量与要加载的Web页面中唯一主机名的数量相同,包括页面URL、脚本、样式表、图片、Flash对象等的主机名。减少主机名的 数量就可以减少DNS查找的数量。
而减少唯一主机名的数量会潜在减少页面中并行下载的数量,这样减少主机名和并行下载的方案会产生矛盾,需要大家自己权衡。建议将组件放到至少两个但不多于4个主机名下,减少DNS查找的同时也允许高度并行下载。
gzip压缩
使用vue-cli的同胞都知道如何查看打包报告
npm run build --report
那么鼠标放上去会看到有个gzip的大小,明显比静态包小很多,这个放到服务器压缩承诺gzip文件后,依然能被谷歌这样的浏览器解析,能大幅提升加载速度
最基础的css,js引入位置
- 样式表放头部
放在头部对于实际页面加载的时间并不能造成太大影响,但可以减少页面首屏出现的时间,使页面内容逐步呈现,改善用户体验,防止“白屏”。
我们总是希望页面能够尽快显示内容,为用户提供可视化的回馈,这对网速慢的用户来说是很重要的。
将样式表放在文档底部会阻止浏览器中的内容逐步出现。为了避免当样式变化时重绘页面元素,浏览器会阻塞内容逐步呈现,造成“白屏”。这源自浏览器的行为:如果样式表仍在加载,构建呈现树就是一种浪费,因为所有样式表加载解析完毕之前不需绘制任何东西
- 脚本放底部
与样式表相同,脚本放在底部对于实际页面加载的时间并不能造成太大影响,但是这会减少页面首屏出现的时间,使页面内容逐步呈现。
js的下载和执行会阻塞Dom树的构建(严谨地说是中断了Dom树的更新),所以script标签放在首屏范围内的HTML代码段里会截断首屏的内容。
因为脚本可能修改页面内容,因此浏览器会等待;另外,也是为了保证脚本能够按照正确的顺序执行,因为后面的脚本可能与前面的脚本存在依赖关系,不按照顺序执行可能会产生错误。
使用本地存储
不是必要的动态数据可以使用本地存储localStorage,能保证用户下次进入的速度
- 持续总结,没有结束~~~