前端性能优化
从输入url到页面呈现经历的步骤:
DNS解析--->TCP建立连接--->http请求--->http响应--->渲染
DNS解析
可以设置DNS预解析;通过keep-alive建立长连接
涉及网络的有3个部分,dns解析,tcp建立连接,http请求/响应 前2个能做的优化非常有限,所以重点放在http请求和响应,主要包括2个方面:减少请求的次数,减少单次请求花费的时间
减少http请求的次数
(1)通过合并文件,将多个css,js文件合并成一个
(2) html、css、js文件的压缩,以及js代码的混乱,起到保护作用
(3)将多个小图片合并成雪碧图;使用图标字体文件
(4)浏览器缓存: 通过设置http header的expires和cache-control完成强缓存,当命中缓存,请求不会发送到服务器端,直接从缓存中读取,当没有命中缓存,会判断有没有协商缓存 通过设置http header的Etag和last-modified完成协商缓存,当命中缓存,服务器端返回304,资源从缓存中换取,当没有命中,服务器端直接返回资源内容
减少单次请求的花费的时间
主要是使用gzip
HTTP协议上的gzip编码是一种用来改进web应用程序性能的技术,web服务器和客户端(浏览器)必须共同支持gzip。目前主流的浏览器,Chrome,firefox,IE等都支持该协议。常见的服务器如Apache,Nginx,IIS同样支持gzip。
gzip压缩比率在3到10倍左右,可以大大节省服务器的网络带宽。而在实际应用中,并不是对所有文件进行压缩,通常只是压缩静态文件。
图片优化
(1)jpg、jpeg 有损压缩,体积小,加载快,不支持透明,适合背景图,轮播图,banner图
(2)png 无损压缩,体积大,加载慢,支持透明,适合logo,图标
(3)svg 文本文件,体积小,不失真, 可以写入html中
(4)Base64 文本文件,体积小,依赖编码
(5)webp google提供的,集中jpeg、png的优点,但是兼容性差
本地存储
(1)cookie 每次请求头都会携带cookie,所以cookie一定不会很大
(2)localstorage、sessionstorage, localstorage:持久化存储,一定存在,除非手动删除sessionstorage:会话存储,当前窗口关闭,就消失
CDN 内容分发网络
重点就是缓存和回流 ,缓存就是把根服务器的资源copy一份到当前服务器 ,回流就是没有资源就去根服务器请求
渲染
(1)将css放在顶部,js放下body的最后,html没有加载完所有css是不会渲染的,所以先加载css,js有阻塞性质,碰到script时,会立即执行,如果程序逻辑比较复杂且庞大就会耗费大量时间,阻塞下一步的执行
(2)减少重绘和回流
(3)能使用css实现的操作,不使用js
(4)减少操作DOM
1、如何保持页面当前的状态
- 将状态存储在LocalStorage / SessionStorage
- 通过路由传值
- 使用keep-alive
2、v-if、v-show、v-html的原理
- v-if会调用addIfCondition方法,生成VNode的时候会忽略对应节点,render的时候就不会渲染了
- v-show会生成真实的VNode,也会进行render,但是在render的过程中,会给节点添加show属性,也就是display
- v-html会先移除节点下的所有节点,调用html方法,通过addProps添加innerHTML属性,本质上还是设置innerHTML是v-html的值
3、v-if和v-show区别
手段
v-for是对DOM节点的操作,删除或者新增 v-if是控制display:none
编译条件
v-if是惰性的,如果初始条件为假,则不会进行编译,只有在第一次初始值为true的时候,才会进行局部编译,v-show在任何条件下,无论真假,首次都会进行编译,然后缓存,DOM元素也会保留
性能
v-if有更高的切换消耗,v-show有更高的渲染消耗
使用场景
v-if适合不经常改变的,v-show适合频繁的切换
4、初始化页面闪动问题
当网页还在加载vue.js,导致vue页面还来不及渲染,就会出现显示vue源代码的情况,我们可以利用v-cloak解决
html:
<div id="app" v-cloak>
{{context}}
</div>
css:
[v-cloak]{
display: none;
}
5、v-for和v-if的问题
1.优先级
v-for的优先级高于v-if的优先级
2.缺点
如果v-for和v-if同时使用,每次渲染都会先进行循环在进行条件判断,无论如何循环都不可避免,会带来性能问题
3.解决方案
(1)在外层增加template,在这一层进行v-if判断,然后在内部进行v-for
(2)如果条件出现在循环内部,可以使用计算属性提前过滤掉那些条件
6、权限控制
1.对权限管理的理解
具体来说就是控制用户能够使用系统的哪些功能,具体到页面,模块和按钮。
2.页面访问权限的实现
(1) 直接将路由表保存在服务器端,通过登录返回对应的路由表
(2) 设置路由守卫,通过用户的权限将对应的路由添加到路由表中
3.登录时
当用户填写完账号和密码后向服务器端验证是否正确,验证通过后,服务器端会返回一个token,拿到token后,会保存在cookie中,防止刷新页面,用户信息丢失。 根据token,去拉取user_info接口的数据,获取用户的信息,角色等。
权限验证:通过token获取用户对应的role,动态根据用户的role算出其对应有权限的路由,通过router.addRoutes动态挂载这些路由
4.页面级别权限控制
创建vue实例的时候,将vue-router挂载,但是这个时候,vue-router挂载是一些登录、404这些不需要权限的公共页面。
当用户登录的时候,获取用户的role,根据角色判断用户对哪些页面有权限,生成对应的路由表
调用router.addRoutes(store.getters.addRoutes)添加用户可以访问的路由
使用vuex管理路由表,根据vuex渲染侧边栏组件
5.属性级别的权限控制
如果是控制某个按钮管理员能够看到,普通用户看不到,那我们就拿到用户的roles,然后使用v-if控制
// html
<button v-if="role=='superAdmin'">权限按钮</button>
// js
export default{
computed:{
//当然实际工作中这里一般都使用mapState
role:this.$store.state.role
}
}
第二种思路:定义自定义指令
全局定义一个自定义指令
Vue.directive('per', {
bind: (el, binding, vnode) => {
//roles是我们的权限数组,binding.value是我们传入自定义指令的值
//如果找不到,那证明没有权限,把当前元素节点移除掉
if (roles.indexOf(binding.value)==-1) {
el.parentNode.removeChild(el);
}
}
//这个传入的admin是对应上面binding.value
<div v-per="[admin]">
admin 可见
</div>