Fetch
Fetch API 是近年来被提及将要取代XHR的技术新标准,是一个 HTML5 的 API。
基于promise的设计,返回的是Promise对象
- fetch()采用模块化设计,API 分散在多个对象上(Response 对象、Request 对象、Headers 对象),更合理一些;
- fetch()通过数据流(Stream 对象) 处理数据,可以分块读取,有利于提高网站性能表现,减少内存占用,对于请求大文件或者网速慢的场景相当有用。XMLHTTPRequest 对象不支持数据流,所有的数据必须放在缓存里,不支持分块读取,必须等待全部拿到后,再一次性吐出来。
- 过程:
- fetch 请求成功后,响应 response 对象
- response 对象根据服务器返回的不同类型数据,提供了不同的读取方法。如response.json() 得到 json 对象,text(),formData() ——这些返回的都是 promise 对象,必须等到异步操作结束,才能得到服务器返回的完整数据。
- 创建副本:stream 对象只能读取一次,读取完就没了,提供了 clone() 方法,创建 respons 对象副本,实现多次读取。
- fetch()发出请求以后,只有网络错误或者无法连接时,fetch()才会报错,其他情况都不会报错,而是认为请求成功。只有通过Response.status属性,得到 HTTP 回应的真实状态码,才能判断请求是否成功;另一种方法是判断response.ok是否为true
- 浏览器兼容:fetch 是相对较新的技术,一些浏览器不支持。需要引入 ES5 的
polyfill
(polyfill 的原理就是探测fetch是否支持,如果不支持则用 xhr 实现) - fetch默认不带cookie
传递cookie时,必须在header参数内加上credentials:'include'
,才会像 xhr 将当前cookie 带有请求中。
fetch其他参数
- method: 请求的方法,例如:GET,POST。
- headers: 请求头部信息
- body: 需要发送的信息内容,注意,GET,HEAD方法不能包含body。
- mode: 请求模式
- cors: 允许跨域,要求响应中Acess-Control-Allow-Origin表示允许跨域。
- no-cors: 只允许使用HEAD,GET,POST方法。
- same-origin: 只允许同源请求,否则直接报错。
- navigate: 支持页面导航。
- credentials: 表示是否发送cookie。omit: 不发送。 same-origin: 仅在同源时发送。 include: 发送
- cache: 表示处理缓存的策略。
- redirect: 表示发生重定向时, follow: 跟随。 error: 发生错误。 manual: 需要用户手动跟随。
- integrity: 验证资资源完整性
前端问题
1. 前端需要注意哪些SEO
- 合理的title、description、keywords:搜索对着三项的权重逐个减小,title值强调重点即可,重要关键词出现不要超过2次,而且要靠前,不同页面title要有所不同;
- description把页面内容高度概括,长度合适,不可过分堆砌关键词,不同页面description有所不同;keywords列举出重要关键词即可
- 语义化的HTML代码,符合W3C规范:语义化代码让搜索引擎容易理解网页
- 重要内容HTML代码放在最前:搜索引擎抓取HTML顺序是从上到下,有的搜索引擎对抓取长度有限制,保证重要内容一定会被抓取
- 重要内容不要用js输出:爬虫不会执行js获取内容
- 少用iframe:搜索引擎不会抓取iframe中的内容
- 非装饰性图片必须加alt
- 提高网站速度:网站速度是搜索引擎排序的一个重要指标
1. 浏览器输入URL发生了什么?
DNS域名解析→发送TCP连接→处理请求→接受响应→渲染页面
- 浏览器根据请求的URL交给DNS域名解析,找到真实IP,向服务器发送TCP连接请求
- 服务器交给后台处理完成后返回数据,浏览器接收文件(html,css,js等)
- 浏览器对加载到的资源进行语法解析,建立相应的内部数据结构(比如HTML的DOM)
- 载入解析的资源文件,渲染页面
2. 浏览器如何渲染页面的?
HTML,CSS分别解析出对应树 → 两树合并生成渲染树→计算布局,渲染页面 → 加载剩余img,vedio等文件
(1)解析HTML文件,构建 DOM 树
(2)解析CSS,构建 CSSOM 树
(3)将两个树合并,生成 Render 树
(4)计算布局,绘制页面的所有节点
(5)根据计算好的信息绘制整个页面
(6)加载剩余的img,video等媒体文件
3. 重绘,重排,以及如何避免
重绘就是重新绘制(repaint):是在一个元素的外观被改变,浏览器会根据元素的新属性重新绘制
重排就是重新排列(reflow):当渲染树的一部分必须更新并且节点的尺寸发生了变化,重新构造渲染树。
如何避免:
避免重排:样式集中改变,使用absolute或fixed脱离文档流
5. 前端性能优化手段
前端性能优化分为两类:文件加载更快,文件渲染更快
加载优化:
- 让传输的数据包更小(压缩文件/图片)
- 减少网络请求的次数:精灵图/雪碧图,节流防抖
- 减少渲染的次数:缓存(HTTP缓存,本地缓存,keep-alive缓存)
- 按需加载:懒加载,滚屏加载
- 减少cookie,cookie会影响加载速度
- 避免重定向
- 异步加载第三方资源
执行优化:
- CSS写头部,JS放在尾部,避免在HTML中书写style
- 避免img,iframe等src为空:空src会重新加载当前页面,影响速度和效率
渲染优化:
- 提前渲染:ssr服务器端渲染
- 减少dom节点:对dom查询进行缓存,将dom操作合并,减少使用重排的标签
- 设置viewport:HTML的viewport可加速页面的渲染
脚本优化
- 减少重绘和回流
- 尽量使用事件代理:避免批量绑定事件
雪碧图的应用场景一般是项目中不常更换的一些固定图标组合在一起,如logo,搜索图标等
电商项目中的懒加载,一般在查看商品展示的时候通常下拉加载更多,因为商品数据太多,一次性请求过来数据太大且渲染事件太长
前端性能优化手段从以下几个方面入手:加载优化、执行优化、渲染优化、样式优化、脚本优化
- 加载优化:减少HTTP请求、缓存资源、压缩代码、无阻塞、首屏加载、按需加载、预加载、压缩图像、减少Cookie、避免重定向、异步加载第三方资源
- 执行优化:CSS写在头部,JS写在尾部并异步、避免img、iframe等的src为空、尽量避免重置图像大小、图像尽量避免使用DataURL
- 渲染优化:设置viewport、减少DOM节点、优化动画、优化高频事件、GPU加速
- 样式优化:避免在HTML中书写style、避免CSS表达式、移除CSS空规则、正确使用display:display、不滥用float等
- 脚本优化:减少重绘和回流、缓存DOM选择与计算、缓存.length的值、尽量使用事件代理、尽量使用id选择器、touch事件优化
6. 性能优化有哪些性能指标,如何量化?
- FCP(First Contentful Paint):
白屏时间
,即页面中第一次有内容渲染的时间,值越低越好 - SI(Speed Index):
页面渲染时间
,即页面从白屏到渲染完毕的时间,值越低越好 页面加载时间
:从页面开始加载到页面onload事件触发的时间。一般来说onload触发代表着直接通过HTML引用的CSS,JS,图片资源已经完全加载完毕,衡量网页从开始加载到完全展示所需的时间。通常以毫秒为单位进行计量,理想情况下,网页加载时间应该小于3秒- 全部页面加载时间:全部页面载入时间指从最初启动浏览开始,直到所有元素都被加载完成后,在2秒后仍然没有网络活动的时间,理想情况下,渲染时间应该小于1秒
- 首字节时间:从客户端发起HTTP请求到服务端返回第一个字节的时间,理想情况下,TTFB应该小于200ms。
- DNS时间:从浏览器发起DNS查询到DNS查询结束的时间
TCP时间
:从浏览器发起TCP连接到TCP连接建立完成的时间下载速度
:从服务器下载资源的速度,通常以KB/s或MB/s表示页面流畅度
:指页面的动画和滚动是否流畅- 页面大小:指网页的大小,通常以字节为单位进行计量。网页大小越小,加载时间越快,用户体验就会越好
网络丢包率
:指在网络传输过程中丢失数据包的比例网络时延
:指数据从客户端发送到服务端并返回的时间请求响应时间
:指客户端发送请求到服务端返回响应的时间,请求次数越少,网页加载时间越短,用户体验就会越好- 事务响应时间:指完成一次完整的操作所需要的时间,如用户登录、购物车结算等
并发用户数
:指同时访问网站的用户数量
7. 一个官网怎么优化,有哪些性能指标,如何量化?
对首屏加载速度影响最大:资源大小,请求数量,请求速度。
代码方面:前端代码一般不会复杂,不太影响速度
- 减少资源大小:各种压缩,懒加载,预加载,异步加载
- 减少请求数量:精灵图,将多个请求合并为一个
- 对于官网这种,最好使用服务器端渲染,除了首屏快,也有利于seo
检测方案:使用 lighthouse
进行性能检测,并对 lighthouse 提出的建议进行优化
具体优化方法:
① 静态化(服务端渲染,减少了请求数量,不用等大量接口返回)
② 图片懒加载,压缩
③ 异步加载 JS,CSS
- JS: 通过设置defer,async属性异步加载(考察defer async区别)
- CSS:使用media属性是媒体查询用的,用于在不同情况下加载不同的css。这里是将其设置为一个不适配当前浏览器环境的值,甚至是不能识别的值,浏览器会认为这个样式文件优先级低,会在不阻塞的情况加载。加载完成后再将media设置为正常值,让浏览器解析css
④ 代码优化:lighthouse上显示主线程耗时最多的是样式和布局,所以对这部分进行优化。
- 去掉table,table本身性能低且过时
- 减少一些无意义的dom,减少dom元素的数量和嵌套
- 减少css选择器的嵌套,例如一些less,sass这种css预处理器容易造成多层嵌套
- js优化和重构:
- 拆分函数,将功能复杂的函数拆分成小函数,让每个函数只做一件事
- 优化分支结构:用对象object代替if else和switch等
⑤ preconnent: lighthouse建议对于接下来会访问的地址可以提前建立连接。有以下几种方式。
1. dns-prefetch 域名预解析
<link rel="dns-prefetch" href="//example.com">2. preconnet 预连接
<link rel="preconnect" href="//example.com">
<link rel="preconnect" href="//cdn.example.com" crossorigin>3. prefetch 预加载
<link rel="prefetch" href="//example.com/next-page.html" as="html" crossorigin="use-credentials">
<link rel="prefetch" href="library.js" as="script">4. prerender 预渲染
<link rel="prerender" href="//example.com/next-page.html">
8. 服务端渲染
服务端渲染 Server Side Render ——SSR
- 客户端渲染:使用 JavaScript 框架进行页面渲染
- 服务端渲染:服务端将HTML文本组装好,并返回给浏览器,这个HTML文本被浏览器解析之后,不需要经过 JavaScript 脚本的执行,即可直接构建出希望的 DOM 树并展示到页面中,最后将这些静态标记"激活"为客户端上完全可交互的应用程序。
优点:
- 更好的SEO,因为搜索引擎爬虫抓取可以直接查看完整渲染的页面(但是如果客户端渲染来说,则不能,因为返回的HTML是一个空壳,它需要执行JS脚本后才可以渲染)
- 用户将会更快的看到完整页面
- 无需占用客户端资源。即解析模板的工作完全交由后端来做,客户端只要解析标准的html页面即可
- 后端生成静态化文件。即后端生成缓存片段,这样就可以减少数据库查询浪费的时间了,且对于数据变化不大的页面非常高效 。
缺点:
- 为了实现服务端渲染,代码中需要兼容服务端和客户端两种运行情况
- 不利于前后端分离,开发效率低。
- 占用服务器端资源。即服务器端完成html模板的解析,如果请求较多,会对服务器造成一定的访问压力。而如果使用前端渲染,就是把这些解析的压力分摊了前端,而这里确实完全交给了一个服务器。
react框架的项目,想使用SSR服务端渲染,可以使用react-dom/server
的renderToString()
方法将React组件转化为HTML代码,从而实现服务端渲染。
12. 顺序存储结构 VS 链式存储结构
优缺点:
- 顺序存储:逻辑上相邻元素,物理地址上也相邻(要求内存中可用的存储单元的地址必须是连续的
优:存储密度大,存储空间利用率高
缺:插入/删除元素不方便 - 链式存储:相邻元素随意存放,但是所占内存空间分为两部分:节点值+指针
优:插入/删除方便
缺:存储密度小,空间利用率低
13. 移动端适配方案
思路
- 写页面时,按照设计稿写固定宽度,最后再统一缩放处理,在不同手机上都能用
- 按照设计稿的标准开发页面,在手机上部分内容根据屏幕宽度等比缩放,部分内容按需要变化,需要缩放的元素使用 rem, vw 相对单位,不需要缩放的使用 px
- 固定尺寸+弹性布局,不需要缩放
1)viewport 适配
根据设计稿标准(750px 宽度)开发页面,写完后页面及元素自动缩小,适配 375 宽度的屏
<meta name="viewport" content="width=750,initial-scale=0.5">
其中 initial-scale = 屏幕的宽度 / 设计稿的宽度
2)vw 适配(部分等比缩放)
- 开发者拿到设计稿(假设设计稿尺寸为750px,设计稿的元素标注是基于此宽度标注)
- 开始开发,对设计稿的标注进行转换,把px换成vw。比如页面元素字体标注的大小是32px,换成vw为
(100/750)*32 vw
(计算:1vw=1%=750px / 100份 = 7.5px ===》1vw=7.5px,所以32px转为vw:32px➗7.5px=4.27vw - 对于需要等比缩放的元素,CSS使用转换后的单位
- 对于不需要缩放的元素,比如边框阴影,使用固定单位px
假如设计图是按 640px 来设计的,那么我把设计图分成 10份(随你自己分),
也就是 640px = 10rem,那么就是 1rem = 64px, 在根元素 html 上设置的 font-size 实际就是给网页的一个标准,它的px是多少,那么子级的 1rem 就等于多少
那么在 640px的屏幕下,根元素 html 的font-size 就可以计算为 640/10,
但是屏幕是不指定大小的,如果屏幕缩小的,那么根元素的值也要按百分比来缩小,如:
屏幕如果缩到了一半 320 , (320/640)*(640/10)=(屏幕宽度/设计图) *(设计图/设计图的总分成)
我们公司是按苹果宽度 320px 的设计图来做的,然后我把设计图分成16份,也就是16rem,那么我的 1rem = 320/16 = 20px,那么公式就是 (屏幕宽度/320) *(320/16)然后改变 html 标签的fontSize
在使用弹性布局时,一般会限制最大宽度,比如在pc端查看我们的页面,此时vw就无法力不从心了,因为除了width有max-width,其他单位都没有,而rem可以通过控制html根元素的font-size最大值,而轻松解决这个问题
有些场景下,并不需要页面整体缩放(viewport 自动处理的也很好了),所以有时只需要合理的布局即可。
弹性盒适配(合理布局)flex
特性: 给父亲添加了 display: flex; 所有的子盒子(弹性盒子)都会在一行显示,不会自动换行。
伸缩比:把父盒子分为若干份数,每个子盒子各占几份。flex:1;
一定给子盒子加
比如有一个父盒子里面有三个子盒子,每个子盒子写 flex:1; 此时每个子盒子各占三分之一。
.father {display: flex;height: 300px;background-color: pink;
}.father div {flex: 1; /* 每个孩子各占1份 *//* 默认子盒子和父亲一样高 */background: purple;
}
在flex眼中,标签不再分类。
- 简单说就是没有块级元素,行内元素和行内块元素
- 任何一个元素都可以直接给宽度和高度一行显示
Flex不存在脱标的情况:也就是基本淘汰了浮动,更不用清除浮动
当然存在兼容性问题,如果不考虑兼容性可以大量使用,如果是移动端则不用考虑直接flex
媒体查询(了解): 可以自动检测视口的宽度。根据屏幕宽度修改html文字大小。
使用媒体查询, 根据不同的视口宽度, 设置不同的根字号 ,就完成了适配
@media (width:375px) {html {font-size: 40px;}}@media (width:320px) {html {font-size: 30px;}}
14. 什么是响应式开发?
同一个网站兼容不同的大小的设备。如PC端、移动端(平板、横屏、竖排)的显示风格。
需要用到的技术
- Media Query(媒体查询)
用于查询设备是否符合某一特定条件,这些特定条件包括屏幕尺寸,是否可触摸,屏幕精度,横屏竖屏等信息。 - 使用em或rem做尺寸单位:用于文字大小的响应和弹性布局。
- 禁止页面缩放
- 屏幕尺寸响应
a) 固定布局:页面居中,两边留白,他能适应大于某个值一定范围的宽度,但是如果太宽就会有很多留白,太窄会出现滚动条,在PC页面上很常见。
b) 流动布局:屏幕尺寸在一定范围内变化时,不改变模块布局,只改变模块尺寸比例。比固定布局更具响应能力,两边不留白,但是也只能适应有限的宽度变化范围,否则模块会被挤(拉)得不成样子。
c) 自定义布局:上面几种布局方式都无法跨域大尺寸变化,所以适当时候我们需要改变模块的位置排列或者隐藏一些次要元素。
d) 栅格布局:这种布局方式使得模块之间非常容易对齐,易于模块位置的改变用于辅助自定义布局。
15. 如何提高网站安全?
网站安全性问题:iframe,opener,CSRF(跨站请求伪造),XSS(跨站脚本攻击),ClickJacking(点击劫持),HSTS(HTTP严格传输安全),CDN劫持
CSRF / XSRF(跨站请求伪造):攻击者盗用了你的身份,以你的名义进行恶意请求。它能做的事情有很多包括:以你的名义发送邮件、发信息、盗取账号、购买商品、虚拟货币转账等。总结起来就是:个人隐私暴露及财产安全问题。
1.阐述 CSRF 攻击思想:(核心2和3)
- 1、浏览并登录信任网站(举例:淘宝)
- 2、登录成功后在浏览器产生信息存储(举例:cookie)
- 3、用户在没有登出淘宝的情况下,访问危险网站
- 4、危险网站中存在恶意代码,代码为发送一个恶意请求(举例:购买商品/余额转账)
- 5、携带刚刚在浏览器产生的信息cookie进行恶意请求
- 6、淘宝验证请求为合法请求(区分不出是否是该用户发送)
- 7、达到了恶意目标
2.预防措施(推荐添加token / HTTP头自定义属性):
- 涉及到数据修改操作严格使用post请求而不是get
- http 协议中使用Referer属性来确定请求来源进行过滤(禁止外域)
- 请求地址添加token,使黑客无法伪造用户请求
- http 请求自定义属性验证
- 添加验证码,密码等
XSS/CSS 跨站脚本攻击
攻击者在目标网站插入恶意脚本js/html,用户在浏览器上运行时可以获取用户敏感信息(cookie/session),修改web页面欺骗用户
预防措施:
- 现在大部分浏览器自带XSS过滤器,react等框架也对它进行防护
- 对重要内容加密传输
- 对于url携带参数谨慎使用
ClickJacking 点击劫持:一般会利用透明 iframe 覆盖原网页诱导用户进行某些操作达成目的。
HSTS(HTTP严格传输安全,HTTP Strict Transport Security)
网站接受从 HTTP 请求跳转到 HTTPS 请求的做法,例如我们输入“www.baidu.com”最终都会被302重定向到“https://www.baidu.com”。这就存在安全风险,当我们第一次通过 HTTP 或域名进行访问时,302重定向有可能会被劫持,篡改成一个恶意或钓鱼网站。
业务场景
1. 注册功能
几种常用的登录方式:
- Cookie + Session 登录
- Token 登录
- SSO 单点登录
- OAuth 第三方登录
Cookie + Session 登录
http 是一个无状态协议,客户端每次发送请求,先和服务器建立连接,请求完之后又断开连接。这种方式节省连接资源,但是每次请求都是独立的,服务器无法判断本次请求和上一次是不是同一个用户,进而也无法判断出用户的登陆状态。
所以推出cookie:cookie是服务器发送给客户端的信息,它会存储在客户端,之后客户端每次请求服务器都会带上cookie。
有了cookie后,服务器就能获取到客户端传来的信息,之后对这些信息进行验证则需要session(客户端请求服务端,服务端就会为这次请求开辟一块内存空间,这就是session对象
- 第一次登录1. 用户访问a.com/pageA,并输入密码登录。
- 服务器验证密码无误后,会创建 SessionId,并将它保存起来。
- 服务器端响应这个 HTTP 请求,并通过 Set-Cookie 头信息,将 SessionId 写入 Cookie 中。
2.第一次登录完成之后,后续的访问就可以直接使用 Cookie 进行身份验证了:
- 用户访问a.com/pageB页面时,会自动带上第一次登录时写入的 Cookie。
- 服务器端比对 Cookie 中的 SessionId 和保存在服务器端的 SessionId 是否一致。
如果一致,则身份验证成功。
Cookie + Session 存在的问题
- 大量的客户端,需要存放大量的 SessionId,这会导致服务器压力过大。
- 如果服务器端是一个集群,为了同步登录态,需要将 SessionId 同步到每一台机器上,无形中增加了服务器端维护成本。
- 由于 SessionId 存放在 Cookie 中,所以无法避免 CSRF 攻击。
Token 登录
token 是服务器生成的一串字符串,作为客户端请求的一个令牌
当第一次登陆之后,服务器会生成一个token并返回给客户端,客户端后续访问时,只需要带上这个token即可身份认证
token 优缺点:
- 优:服务器端不需要存放token,减轻服务器压力。
可以存放在前端任何地方,不用保存在cookie中,提升安全性 - 缺:token 下发后,只要在有效时间内,就一直有效,这时服务器不方便回收token权限
SSO 单点登录
指在公司内部搭建一个公共的认证中心,公司下的所有产品登录都可以在认证中心完成,一个产品在认证中心登录后,再去访问另一个产品,可以不用再次登录,即可获取登陆状态。
1.用户首次访问,需要在认证中心登录
OAuth 第三方登录
通过第三方应用程序的账号密码, 快速的获取用户相关的信息实现登录。
例如: QQ登录
点击QQ登录按钮之后,就会要求用户输入QQ的账号和密码 只要用户输入了QQ的账号和密码, 我们就可以拿到用户的QQ信息
我们就可以通过用户的QQ信息来实现注册登录
存在的问题:
如果让用户在我们的网站上输入QQ的账号和密码, 那么我们就可以窃取用户的QQ号, 做一些见不得人的事
如何解决:通过OAuth协议进行授权
与以往的授权方式不同之处是OAuth的授权不会使第三方触及到用户的帐号信息(如用户名与密码),
即第三方无需使用用户的用户名与密码就可以申请获得该用户资源的授权,因此OAuth是安全可靠的
总结一下这 4 种方案的使用场景:
- Cookie + Session 历史悠久,适合于简单的后端架构,需开发人员自己处理好安全问题。
- Token 方案对后端压力小,适合大型分布式的后端架构,但已分发出去的 token ,如果想收回权限,就不是很方便了。
- SSO 单点登录,适用于中大型企业,想要统一内部所有产品的登录方式。
- OAuth 第三方登录,简单易用,对用户和开发者都友好,但第三方平台很多,需要选择合适自己的第三方登录平台。
2. 懒加载
懒加载就是一种在页面加载时,延迟加载一些非关键资源,也就是按需加载。
懒加载特点:
- 减少无用资源的加载:减少了服务器和浏览器的压力
- 提升用户体验: 如果同时加载较多图片,可能需要等待的时间较长
- 防止加载过多图片而影响其他资源文件的加载 :会影响网站应用的正常使用。
一. 图片懒加载
图片的加载是由src引起的,当对src赋值时,浏览器就会请求图片资源。
根据这个原理,我们使用data-xxx属性来储存图片的路径,在需要加载图片的时候,将data-xxx中图片的路径赋值给src,这样就实现了图片的懒加载。
注意:data-xxx 中的xxx可以自定义,这里我们使用data-src来定义。
懒加载的实现重点在于确定用户需要加载哪张图片,在浏览器中,可视区域内的资源就是用户需要的资源。所以当图片出现在可视区域时,获取图片的真实地址并赋值给图片即可。
- 利用JS检查标签是否在视窗中,如果在的src则设置为目标图片的url
① 通过监听一些事件比如scroll或者resize来检测元素出现在视窗
② 利用intersection observer
:开发者只需要注册一个observer去监控元素,注册之后只需要做的是当元素可见时改变它的行为
<img class="lazy" src="placeholder-image.jpg"
data-src="image-to-lazy-load-1x.jpg"
data-srcset="image-to-lazy-load-2x.jpg 2x, image-to-lazy-load-1x.jpg 1x">class:用于在JavaScript中关联元素
src属性:指向了一张占位图片,图片在页面的第一次加载会显现
data-src和data-srcset属性:这是占位属性,里面放的是目标图片的url// 处理兼容性,在不支持intersection observer的浏览器,你需要引入polyfill,或者回退到更安全的方法。
- CSS 图像
展示图像不是标签的特权,CSS利用background-image也可以做到
开始请求外部资源之前,浏览器会检测CSS。如果浏览器检测到CSS引用的外部资源并没有应用到已存在的DOM节点上,浏览器就不会请求这些资源。
思路:通过JavaScript检测到元素处于视窗中时,加一个class类名,这个class就引用了外部图片资源。
<div class="lazy-background"><h1>Here's a hero heading to get your attention!</h1>
</div>
这个div.lazy-background元素会正常地显示CSS规则加载的占位图片。当元素处于可见状态时,我们可以添加一个类名完成
如下:.lazy-background {background-image: url("hero-placeholder.jpg"); /* 占位图片 */
}.lazy-background.visible {background-image: url("hero.jpg"); /* 真正的图片 */
}
下面是利用JavaScript去检测元素是否处于视窗(intersection observer),如果可见就为它加上一个visible的类名。
document.addEventListener("DOMContentLoaded", function() {var lazyBackgrounds = [].slice.call(document.querySelectorAll(".lazy-background"));if ("IntersectionObserver" in window) {let lazyBackgroundObserver = new IntersectionObserver(function(entries, observer) {entries.forEach(function(entry) {if (entry.isIntersecting) {entry.target.classList.add("visible");lazyBackgroundObserver.unobserve(entry.target);}});});lazyBackgrounds.forEach(function(lazyBackground) {lazyBackgroundObserver.observe(lazyBackground);});}
});
二 . 视频懒加载
指定标签的preload属性为none,这样浏览器就不会预加载任何视频数据。
为了占用空间,我们用poster属性为其占位:
<video controls preload="none" poster="占位图"><source src="视频资源名.mp4" type="video/mp4">
</video>
三. 懒加载库
- lazysizes:主要用于加载图片和iframes。你只需要指定data-src/data-srcset属性,lazysizes会帮你自动懒加载内容。
懒加载与预加载的区别
这两种方式都是提高网页性能的方式,两者主要区别是一个是
提前加载,一个是迟缓甚至不加载。懒加载对服务器前端有一定的缓解压力作用,预加载则会增加服务器前端压力。
- 懒加载:也叫延迟加载,指在长网页中延迟加载图片的时机,当用户需要访问时再去加载
这样可以提高网站的首屏加载速度,提升用户的体验,并且可以减少服务器的压力。
它适用于图片很多,页面很长的电商网站的场景。
懒加载的实现原理是,将页面上的图片的 src 属性设置为空字符串,将图片的真实路径保存在一个自定义属性中,当页面滚动的时候,进行判断,如果图片进入页面可视区域内,则从自定义属性中取出真实路径赋值给图片的 src 属性,以此来实现图片的延迟加载。- 预加载:指将所需的资源提前请求加载到本地,这样后面在需要用到时就直接从缓存取资源。
通过预加载能够减少用户的等待时间,提高用户的体验。
我了解的预加载的最常用的方式是使用 js 中的 image 对象,通过为 image 对象来设置 scr 属性,来实现图片的预加载。
3. 单页应用
优点:
- 良好的交互性
单页应用的内容改变不需要重新加载整个页面,只需要用Ajax异步获取数据,没有页面之间的切换,不会出现“白屏现象”,页面显示流畅 - 良好的前后端分离模式
后端不再负责模板渲染等工作,所以同一套后端代码不用修改可用于 Web页面,手机平板等多种客户端 - 减轻服务器压力
服务器只需要输出数据即可,不用管展示和页面合成等
缺点:
- 首屏加载慢
因为如果不对路由进行处理,加载首屏会把所有组件全部加载,并向服务器请求数据,这必然会导致加载速度(解决方法:路由懒加载) - 不利于SEO
一个单页应用,html在服务器端还没有渲染部分数据,即搜索引擎请求到的html是模型页面而不是最终数据的渲染页面。 这样就很不利于内容被搜索引擎搜索到。
解决方法:① 服务端渲染:服务器合成完整的 html 文件再输出到浏览器
② 页面预渲染
3. 页面资源预加载 preload
preload 提供了一种声明式的命令,让浏览器提前加载指定资源(加载后并不执行),在需要执行的时候再执行。
优点:将加载和执行分离开,不阻塞渲染
使用 preload:使用 link 标签创建 <link rel="preload" href="/path/style.css" as="style">
3. 图片压缩算法
PNG图片的压缩,分两个阶段:
- 预解析
- 压缩(Compression):执行Deflate压缩,该算法结合了 LZ77 算法和 Huffman 算法对图片进行编码。
4. 加载很多图片时的优化方法,页面加载时的交互优化
- 图片压缩:页面是由“小图”平铺来的,却需要加载大量原图。于是只有实际点击大图时再去请求原图,这样会大大减少页面加载时间。
- 图片异源加载:img 中将真实图片地址写在data-original 属性中,src中换成占位符图片
<img class="lazy" src="grey.gif" data-original="example.jpg" >
- java 后台图片压缩
- 前端JS实现图片压缩
- 图片异源加载:img 中将真实图片地址写在data-original 属性中,src中换成占位符图片
- 将图片转为Base64格式来节约请求
采用Base64的编码方式将图片直接嵌入到网页中,而不是从外部载入,这样就减少了HTTP请求。 - 图片预加载
把稍后需要用到的图片悄悄的提前加载到本地- memory cache:将资源缓存到内存中,等待下次访问时不需要重新下载资源,而直接从内存中获取。
- diskCache:将资源缓存到磁盘中,等待下次访问时直接从磁盘中获取
- 其他
- 图片懒加载
- 将图片服务和应用服务分离:当网站存在大量的图片读写操作时,建议使用图片服务器
- CSS Sprites 技术:将这些小icon合并成一张图片,只需要加载一次,也就是精灵图
5. 如何触发下拉刷新、上拉加载
下拉刷新
- 监听原生 touchstart 事件,记录其初始位置值 e.touches[0].pageY
- 监听原生 touchmove 事件,记录当前滑动位置值与初始位置值的差值,大于0表示向下拉动,并借助css的translateY使元素跟随手势向下滑动对应的差值,同时设置一个允许滑动的最大值
- 监听 touchend,若此时元素滑动到最大值,则触发callback,同时将translateY设为0,元素回到初始位置
在下拉到松手的过程中,经历了三个状态:
- 当前手势滑动位置与初始位置差值大于零时,提示正在进行下拉刷新操作;
- 下拉到一定值时,显示松手释放后的操作提示;
- 下拉到达设定最大值松手时,执行回调,提示正在进行更新操作。
上拉加载
页面滚动到底部时触发,也可以选择在滚动到一定位置的时候触发。
实现原理:分别获得当前滚动条的scrollTop值、当前可视范围的高度值clientHeight以及文档的总高度scrollHeight。
当scrollTop+clientHeight ≥ scrollHeight,触发callback。
6. 扫描二维码登录的原理
token:
扫描二维码登录:
扫码登录可以分为三个阶段:待扫描、已扫描待确认、已确认。
- 待扫描
流程图1-5,生成二维码阶段。这个阶段和移动端无关,只是pc端和服务端交互- pc端携带设备信息向服务端发起生成二维码请求,服务端会生成唯一的二维码ID,将此二维码ID和PC设备信息联系起来
- PC端收到二维码ID后,将它以二维码的形式展现,等待移动端扫码。这时pc端会启动一个定时器,轮询查询二维码状态(如果移动端未扫,则一段时间后二维码失效)
- 已扫描,待确认
流程图 6-10 (在 PC 端登录微信时,手机扫码后,PC 端二维码会变成已扫码,请在手机端确认。这个阶段是移动端跟服务端交互的过程。)- 首先移动端扫描二维码,获取二维码ID,将手机端登录的信息凭证token和二维码ID作为参数发送给服务端
- 服务端接受请求后,会将token值和二维码ID关联。然后生成一个一次性token,这个会返回给移动端,一次性token用作确认时的凭证
- pc端的定时器,会轮询到二维码状态已经发生改变,会将pc端二维码更新为已扫描,请确认
- 已确认
11-15流程图,- 移动端携带上一步获取到的临时token,确认登录
- 服务端校对完成后,会更新二维码状态,并且给pc端一个正式的token,后续pc端就用这个token访问服务端
7. 前端切换中英文
- 方法1:(中英文各做一份,然后用不同的文件夹区分开来,点击切换语言时,链接跳转到不同文件夹就行了)
优点:各自的版本是分离开来的,比较稳定,不会出现互相干扰
缺点:修改一个样式或功能,要把变更的操作在所有的语言版 本上重复一次,加重了工作量 - 方法2:借助 jquery 插件——jquery.i18n.properties
- 方法3:使用微软字典整站翻译