1、说一下http和https

  1. 基本概念

http: 超文本传输协议,是应用最广泛的一种网络协议,是客户端与服务端请求与应答的标准,可以使浏览器更加高效,使得网络传输减少
https:是以安全为目标的http通道,就是http的安全版,在http下增加SSL层,其主要作用就是建立一个信息安全通道,来确保数据的传输,确保网站的真实性

  1. 区别

http:明文传输;端口80
https:ssl加密传输;端口443

  1. https协议的工作原理

① 客户端使用https url访问web服务器,要求web服务器建立ssl连接
② 服务器端接收到请求之后,会把网站的证书返回或者传输给客户端
③ 客户端和服务器端协商ssl链接的安全等级,也就是加密等级
④ 客户端浏览器通过协商一致的安全等级,建立会话密钥,然后通过网站的公钥来加密会话密钥,并传送网站
⑤ web服务器通过自己的私钥解密出会话密钥
⑥ web服务器通过会话密钥加密与客户端之间的通信

  1. https协议的优点

① 可以认证用户和服务器,确保数据发送到正确的客户机和服务器
② 是由ssl+http构建的可进行加密传输,身份认证的网络协议,要比http更加安全,可以防止数据在传输过程中不被窃取和改变,确保数据的完整性

  1. https协议的缺点

① 握手阶段比较费时,会使得页面加载时间延长50%,增加10%~20%的耗电
② 缓存不如http高效,会增加数据开销
③ SSL证书功能越强大费用越高
④ SSL证书需要绑定IP,不能在同一个IP上绑定多个域名,ipv4资源支持不了这种消耗

2、TCP和UDP的区别

① TCP是面向连接的,UDP是无连接的(即发送数据之前不需要先建立连接)
② TCP提供可靠的服务(也就是说通过此传送的数据无差错,不丢失,不重复,并且按序到达,适合大数据量的交换);UDP尽最大努力交付(不保证可靠交付)
③ TCP是面向字节流,UDP面向报文
④ TCP只能是1对1的;UDP支持1对1,1对多

3、什么是BOM?有哪些常用的BOM属性呢?

location对象

⑴ location.href 返回或设置当前文档的url
⑵ location.search 返回url中的查询字符串部分
⑶ location.hash 返回url#后面的内容,如果没有则返回空
⑷ location.host 返回url中域名的部分,例如:www.baidu.com
⑸ location.hostname 返回url中的主域名部分,例如:baidu.com
⑹ location.pathname 返回url的域名后的部分,例如:www.baidu.com/xhtml/,返回的是/xhtml/
⑺ location.port 返回url中的端口部分
⑻ location.protocol 返回url中的协议部分,例如:http://www.baidu.com,返回的是http:
⑼ location.assign 设置当前文档的url
⑽ location.replace 设置文档的url,并且在history的地址列表中删除
⑾ location.reload 重载当前页面

history对象

⑴ history.go(num) 前进或后退指定的页面数
⑵ history.back() 后退一页
⑶ history.forward() 前进一页

Navigator对象

⑴ navigator.userAgent 返回包括浏览器信息的字符串信息
⑵ navigator.cookieEnabled 返回浏览器是否支持cookie

4、Cookie,sessionStorage,localStorage的区别,并说明一下Cookie的作用

Cookie:Cookie数据始终在同源的http请求中携带,即cookie在浏览器和服务器间来回传递,而sessionStoragelocalStorage不会自动把数据发送给服务器,仅仅在本地保存;cookie数据还有路径的概念,可以限制cookie只属于某个路径下;存储的大小只有很小的4k左右

sessionStorage:仅在当前浏览器窗口关闭前有效,自然也就不可能持久保存;localStorage:始终有效,窗口或者浏览器关闭也一直有效,因此可用作持久数据;cookie:只在设置的cookie过期时间之前一直有效

Cookie的作用
⑴ 保存用户登录状态
例如将用户id存储于一个cookie中,这样当用户下次访问该页面时就不需要再登录了
⑵ 跟踪用户行为
例如选择所在地,系统可以依据cookie记住上一次访问的地区,可以根据此来获取该地区的天气

5、iframe是什么?有什么缺点?

定义

iframe元素会创建包含另一个文档的内联框架

提示

可以将提示文字放在iframe闭合标签之间,来提示某些不支持的浏览器

缺点

① 会阻塞主页面的onload事件
② 搜索引擎无法解读这种页面,不利于SEO优化
③ iframe和主页面共享连接池,而浏览器对相同区域有限制会影响性能

6、Doctype的作用?严格模式和混杂模式怎么区分?

作用

Doctype声明于文档最前面,告诉浏览器以何种方式来渲染页面,有两种模式:严格和混杂

区分

严格模式的排版和Js运作模式是以该浏览器支持的最高标准运行
混杂模式向后兼容,模拟老式浏览器,防止浏览器无法兼容页面

7、Cookie和Session的区别

① Cookie存放于客户端,Session存放于服务器端
② Cookie不是很安全,很容易遭受到攻击,考虑到安全,可以使用session
③ Session会在一定时间内保存在服务器,当访问增多,会影响服务器的性能,考虑到减轻服务器压力,应当使用cookie
④ 单个cookie保存的内容不能超4K,很多浏览器都会限制一个站点最多保存20个Cookie

8、http常用请求头

协议头 说明
Accept 可接受的响应内容类型
Accept-Charset 可接受的字符集
Accept-Encoding 可接受的响应内容的编码方式
Accept-Language 可接受的响应内容的语言列表
Accept-Datetime 可接受的按照时间来表示的响应内容版本
Authorization 用于表示HTTP协议中需要认证资源的认证信息
Cache-Control 用来指定当前的请求/回复中的,是否使用缓存机制
Connection 客户端(浏览器)想要优先使用的连接类型
Cookie 由之前服务器通过Set-cookie设置的一个Http协议cookie
Content-Length 以8进制表示的请求体的长度
Content-MD5 请求体的内容的二进制MD5散列值,以Base64编码的结果
Content-Type 请求体的MIME类型,用于请求中
Date 发送消息的日期和时间
Expect 表示客户端要求服务器做出特定的行为
From 发起此请求的用户的邮件地址
Host 服务器所监听的端口号
If-Modified-Since 允许在对应的资源未被修改的情况下返回304未修改
Max-Forwards 限制该消息可被代理及网关转发的次数
Origin 发起一个针对跨域资源共享的请求(该请求要求服务器在响应中加 入一个 Access-Control-Allow-Origin 的消息头,表示访问控制所允许 的来源)
Proxy-Authorization 用于向代理进行认证的认证信息
Range 表示请求某个实体的一部分,字节偏移以 0 开始
Referer 表示浏览器所访问的前一个页面,可以认为是之前访问页面的链接 将浏览器带到了当前页面。Referer 其实是 Referrer 这个单词,但 RFC 制作标准时给拼错了,后来也就将错就错使用 Referer 了
User-Agent 浏览器的身份标识字符串
Upgrade 要求服务器升级到一个高版本协议
Via 告诉服务器,这个请求是由哪些代理发出的
Warning 一个一般性的警告,表示在实体内容体中可能存在错误

9、GET请求和POST请求的区别

① get参数通过url传递,post参数则放到了request body中
② get请求在url中传递的参数是有长度限制的,而post则没有
③ get相比于post更不安全,不能用来传递敏感信息
④ get请求只能进行url编码,而post请求支持多种编码方式
⑤ get请求参数会被完整保留在浏览历史记录里,而post则不会
⑥ get和post本质上就是TCP链接,并无差别,但是由于http的规定和浏览器/服务器的限制,导致在应用过程中体现出一定的不同(get产生一个数据包,post产生两个数据包)

10、CSRF和XSS的网络攻击及防范

CSRF:跨站请求伪造,可以理解为攻击者盗用了用户的身份,以用户的名义发送了恶意请求;防范:使用验证码,检查https头部的refer,使用token

XSS:跨站脚本攻击,可以理解为攻击者通过注入恶意的脚本,在用户浏览网页的时候进行攻击;防范:为Cookie设置httpOnly属性,对用户的输入进行检查,进行特殊字符过滤

11、输入Url到页面加载显示完成发生了什么

DNS解析 —> TCP连接 —> 发送HTTP请求 —> 服务器处理请求并返回HTTP报文 —> 浏览器解析渲染页面 —> 连接结束

12、link标签和@import标签的区别

① link属于html标签,@import则是由css提供的
② 页面被加载时,link会同时被加载,而@import引用的css会等到页面加载结束后加载
③ link没有兼容性问题,而@import只有在IE5以上浏览器才支持
④ 权重方面,link > @import方式

13、flex布局是什么?一些常用的属性作用?

定义

弹性布局,用来为盒模型提供最大的灵活性

常用属性

容器的属性

flex-direction:决定主轴的方向
flex-direction: row | row-reverse | column | column-reverse;

flex-wrap:决定换行规则
flex-wrap: nowrap | wrap | wrap-reverse;

flex-flow: || ;
justify-content:对其方式,水平主轴对齐方式
align-items:对齐方式,竖直轴线方向

元素的属性

order 属性:定义项目的排列顺序,顺序越小,排列越靠前,默认为 0
flex-grow 属性:定义项目的放大比例,即使存在空间,也不会放大
flex-shrink 属性:定义了项目的缩小比例,当空间不足的情况下会等比例的缩小,如果 定义个 item 的 flow-shrink 为 0,则为不缩小
flex-basis 属性:定义了在分配多余的空间,项目占据的空间

flex:是 flex-grow 和 flex-shrink、flex-basis 的简写,默认值为 0 1 auto。

14、CSS中关于垂直居中的方法

① margin: auto法

  1. div {
  2. width: 400px;
  3. height: 400px;
  4. position: relative;
  5. border: 1px solid #465468;
  6. }
  7. img {
  8. position: absolute;
  9. margin: auto;
  10. top: 0;
  11. left: 0;
  12. right: 0;
  13. bottom: 0;
  14. }

② margin 负值法

container{
  width: 500px;
  height: 400px;
  border: 2px solid #379;
  position: relative;
}
.inner{
  width: 480px;
  height: 380px;
  background-color: #746;
  position: absolute;
  top: 50%;
  left: 50%;
  margin-top: -190px; /*height 的一半*/
  margin-left: -240px; /*width 的一半*/
}

# 补充:其实这里也可以将 marin-top 和 margin-left 负值替换成,
transform:translateX(-50%)和 transform:translateY(-50%)

③ table-cell法

div{
  width: 300px;
  height: 300px;
  border: 3px solid #555;
  display: table-cell;
  vertical-align: middle;
  text-align: center;
}
img{
    vertical-align: middle;
}

④ flex

div {
    display:flex;
  align-items:center;
  justify-content:center;
}

15、了解重排和重绘吗?引起的原因是什么?如何减少?

定义

重排:DOM的变化影响到了几何属性比如宽高,浏览器重新计算元素的几何属性,其它元素的几何属性也会收到影响,浏览器需要重新构造渲染书
重绘:浏览器将收到影响的部分重新绘制到页面上的过程

原因

① 添加或者删除可见的DOM元素
② 元素尺寸位置的改变
③ 浏览器页面初始化
④ 浏览器窗口大小发生改变,重排一定导致重绘,重绘并不一定导致重排

减少

① 不在布局信息改变时做DOM查询
② 使用csstext,className一次性改变属性
③ 使用 fragment
④ 对于多次重排的元素,比如说动画。使用绝对定位脱离文档流,使其不影响其他元素

16、使元素消失的方法有哪些

① opacity:0,隐藏,不会改变页面布局,如果绑定click事件,是可以被触发的
② visibility:hidden,隐藏,不会改变页面布局,不会触发已绑定的事件
③ display: none,隐藏,会改变页面布局,可以理解成在页面中把该元素删除掉

17、Ajax解决浏览器缓存问题

① 在 ajax 发送请求前加上 anyAjaxObj.setRequestHeader(“If-Modified-Since”,”0”)
② 在 ajax 发送请求前加上 anyAjaxObj.setRequestHeader(“Cache-Control”,”no-cache”)
③ 在 URL 后面加上一个随机数: “fresh=” + Math.random()
④ 在 URL 后面加上时间搓:”nowtime=” + new Date().getTime()
⑤ 如果是使用 jQuery,直接这样就可以了 $.ajaxSetup({cache:false})。这样页面的所有 ajax 都会执行这条语句就是不需要保存缓存记录。

18、前端性能优化

① 减少http请求
② 使用cdn
③ 添加本地缓存
④ 压缩资源文件
⑤ css放顶部,JavaScript文件放底部
⑥ 避免使用css表达式
⑦ 减少dns查询
⑧ 避免重定向
⑨ 图片懒加载

19、说说em、px、rem、vh、vw的区别

介绍

在传统的项目中,我们只会用到px,%,em这几个单位,它可以适用于大部分的项目开发,且拥有比较良好的兼容性;但是从css3开始,浏览器对计量单位的支持又提升到另外一个境界,新增了rem、vh、vw等,可以利用这些新的单位开发出比较良好的响应式页面,适应多种不同分辨率的终端,包括移动设备等

单位

CSS单位
相对长度单位 em、ex、ch、rem、vw、vh、vmin、vmax、%
绝对长度单位 cm、mm、in、px、pt、pc

px ,表示像素,所谓像素就是呈现在我们显示器上的一个个小点,每个像素点都是大小等同的;有些人会把px认为是相对长度,原因在于在移动端中存在设备像素比,px实际显示的大小是不确定的,这里之所以认为px是绝对单位,在于px的大小和元素的其它属性无关


em,相对长度单位,相对于当前对象内文本的字体尺寸。如当前对行内文本的字体尺寸未被人为设置,则相对于浏览器的默认字体尺寸(1em = 16px);为了简化 font-size 的换算,我们需要在css中的 body 选择器中声明font-size= 62.5%,这就使 em 值变为 16px*62.5% = 10px

这样 12px = 1.2em, 10px = 1em, 也就是说只需要将你的原来的px 数值除以 10,然后换上 em作为单位就行了

特点:

  • em 的值并不是固定的
  • em 会继承父级元素的字体大小
  • em 是相对长度单位。相对于当前对象内文本的字体尺寸。如当前对行内文本的字体尺寸未被人为设置,则相对于浏览器的默认字体尺寸
  • 任意浏览器的默认字体高都是 16px

rem,相对单位,相对的只是HTML根元素font-size的值,同理,如果想要简化font-size的转化,我们可以在根元素html中加入font-size: 62.5%

html {font-size: 62.5%;  } /*  公式16px*62.5%=10px  */

这样页面中1rem=10px、1.2rem=12px、1.4rem=14px、1.6rem=16px;使得视觉、使用、书写都得到了极大的帮助

特点:

  • rem单位可谓集相对大小和绝对大小的优点于一身
  • 和em不同的是rem总是相对于根元素,而不像em一样使用级联的方式来计算尺寸

vh,vw

vw ,就是根据窗口的宽度,分成100等份,100vw就表示满宽,50vw就表示一半宽。(vw 始终是针对窗口的宽),同理,vh则为窗口的高度

这里的窗口分成几种情况:

  • 在桌面端,指的是浏览器的可视区域
  • 移动端指的就是布局视口

vwvh,比较容易混淆的一个单位是%,不过百分比宽泛的讲是相对于父元素:

  • 对于普通定位元素就是我们理解的父元素
  • 对于position: absolute;的元素是相对于已定位的父元素
  • 对于position: fixed;的元素是相对于 ViewPort(可视窗口)

总结

「px」:绝对单位,页面按精确像素展示
「em」:相对单位,基准点为父节点字体的大小,如果自身定义了font-size按自身来计算,整个页面内1em不是一个固定的值
「rem」:相对单位,可理解为root em, 相对根节点html的字体大小来计算
「vh、vw」:主要用于页面视口大小布局,在页面布局上更加方便简单

20、谈一谈单点登录

是什么

单点登录,简称SSO,是目前比较流行的企业业务整合的解决方案之一;SSO的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。

SSO一般都需要一个独立的认证中心(passport),子系统的登录均通过passport,子系统本身将不参与登录操作;当一个系统成功登录以后,passport将会颁发一个令牌给各个子系统,子系统可以拿着令牌去获取各自的受保护资源,为了减少频繁认证,各个子系统被passport授权以后,会建立一个局部会话,在一定时间内可以无需再次向passport发起认证

如何实现

1、同域名下的SSO

cookie的domain属性设置为当前域的父域,并且父域的cookie会被子域所共享;利用cookie的这一特点,我们只需要将Cookiedomain属性设置为父域的域名(主域名),同时将 Cookiepath属性设置为根路径,将 Session ID(或 Token)保存到父域中。这样所有的子域应用就都可以访问到这个Cookie

2、不同域名下的SSO(一)

如果是不同域的情况下,Cookie是不共享的,这里我们可以部署一个认证中心,用于专门处理登录请求的独立的 Web服务
用户统一在认证中心进行登录,登录成功后,认证中心记录用户的登录状态,并将token 写入 Cookie(注意这个 Cookie是认证中心的,应用系统是访问不到的)
应用系统检查当前请求有没有 Token,如果没有,说明用户在当前系统中尚未登录,那么就将页面跳转至认证中心
由于这个操作会将认证中心的 Cookie 自动带过去,因此,认证中心能够根据 Cookie 知道用户是否已经登录过了
如果认证中心发现用户尚未登录,则返回登录页面,等待用户登录
如果发现用户已经登录过了,就不会让用户再次登录了,而是会跳转回目标 URL,并在跳转前生成一个 Token,拼接在目标URL 的后面,回传给目标应用系统
应用系统拿到 Token之后,还需要向认证中心确认下 Token 的合法性,防止用户伪造。确认无误后,应用系统记录用户的登录状态,并将 Token写入Cookie,然后给本次访问放行。(注意这个 Cookie 是当前应用系统的)当用户再次访问当前应用系统时,就会自动带上这个 Token,应用系统验证 Token 发现用户已登录,于是就不会有认证中心什么事了
此种实现方式相对复杂,支持跨域,扩展性好,是单点登录的标准做法

3、不同域名下的SSO(二)

可以选择将 Session ID (或 Token )保存到浏览器的 LocalStorage中,让前端在每次向后端发送请求时,主动将LocalStorage的数据传递给服务端
这些都是由前端来控制的,后端需要做的仅仅是在用户登录成功后,将 Session ID(或 Token)放在响应体中传递给前端
单点登录完全可以在前端实现。前端拿到 Session ID(或 Token )后,除了将它写入自己的 LocalStorage 中之外,还可以通过特殊手段将它写入多个其他域下的 LocalStorage 中,前端通过 iframe+postMessage() 方式,将同一份 Token 写入到了多个域下的 LocalStorage 中,前端每次在向后端发送请求之前,都会主动从 LocalStorage 中读取Token并在请求中携带,这样就实现了同一份Token 被多个域所共享
此种实现方式完全由前端控制,几乎不需要后端参与,同样支持跨域

// 获取 token
var token = result.data.token;

// 动态创建一个不可见的iframe,在iframe中加载一个跨域HTML
var iframe = document.createElement("iframe");
iframe.src = "http://app1.com/localstorage.html";
document.body.append(iframe);
// 使用postMessage()方法将token传递给iframe
setTimeout(function () {
    iframe.contentWindow.postMessage(token, "http://app1.com");
}, 4000);
setTimeout(function () {
    iframe.remove();
}, 6000);

// 在这个iframe所加载的HTML中绑定一个事件监听器,当事件被触发时,把接收到的token数据写入localStorage
window.addEventListener('message', function (event) {
    localStorage.setItem('token', event.data)
}, false);

流程

微信图片_20210409143116.jpg

  • 用户访问系统1的受保护资源,系统1发现用户未登录,跳转至sso认证中心,并将自己的地址作为参数
  • sso认证中心发现用户未登录,将用户引导至登录页面
  • 用户输入用户名密码提交登录申请
  • sso认证中心校验用户信息,创建用户与sso认证中心之间的会话,称为全局会话,同时创建授权令牌
  • sso认证中心带着令牌跳转会最初的请求地址(系统1)
  • 系统1拿到令牌,去sso认证中心校验令牌是否有效
  • sso认证中心校验令牌,返回有效,注册系统1
  • 系统1使用该令牌创建与用户的会话,称为局部会话,返回受保护资源
  • 用户访问系统2的受保护资源
  • 系统2发现用户未登录,跳转至sso认证中心,并将自己的地址作为参数
  • sso认证中心发现用户已登录,跳转回系统2的地址,并附上令牌
  • 系统2拿到令牌,去sso认证中心校验令牌是否有效
  • sso认证中心校验令牌,返回有效,注册系统2
  • 系统2使用该令牌创建与用户的局部会话,返回受保护资源

用户登录成功之后,会与sso认证中心及各个子系统建立会话,用户与sso认证中心建立的会话称为全局会话
用户与各个子系统建立的会话称为局部会话,局部会话建立之后,用户访问子系统受保护资源将不再通过sso认证中心
全局会话与局部会话有如下约束关系:

  • 局部会话存在,全局会话一定存在
  • 全局会话存在,局部会话不一定存在
  • 全局会话销毁,局部会话必须销毁

21、Js中如何实现上拉加载和下拉刷新

介绍

上拉加载和下拉刷新通常是出现在移动端更多的两种交互方式,本质上等同于PC端的分页,只是交互形式不同;在开源社区中也有很多优秀的解决方案,例如:iscroll,better-scroll等

实现原理

微信图片_20210409143711.jpg

上拉加载

上拉加载的本质是页面触底或者快要触底时的动作,首先判断页面触底需要先了解几个属性

scrollTop:滚动视窗的高度距离window顶部的距离,它会随着往上滚动而不断增加,初始值是0,是一个变化的值
clientHeight:是一个定值,表示屏幕可视区域的高度
scrollHeight:页面不能滚动时是不存在的,body长度超过window时才出现,表示body所有元素的长度

综上,可以得到一个公式

scrollTop + clientHeight >= scrollHeight

简单实现:

let clientHeight  = document.documentElement.clientHeight; //浏览器高度
let scrollHeight = document.body.scrollHeight;
let scrollTop = document.documentElement.scrollTop;

let distance = 50;  //距离视窗还用50的时候,开始触发;

if ((scrollTop + clientHeight) >= (scrollHeight - distance)) {
    console.log("开始加载数据");
}

下拉刷新

下拉刷新的本质是页面本身置于顶部时,用户下拉时需要触发的动作

下拉刷新主要分三步:
1、监听原生touchstart事件,记录其初始位置的值,e.touches[0].pageY
2、监听原生touchmove事件,记录并计算当前滑动的位置与初始位置值的差值,大于0表示向下拉动,并借助css3的translateY属性使元素跟随手势向下滑动对应的差值,同时也应设置一个允许滑动的最大值
3、监听原生touchend事件,若此时元素滑动到最大值,则触发callback,同时将translateY重设为0,元素回到初始位置

<main>
    <p class="refreshText"></p>
    <ul id="refreshContainer">
        <li>111</li>
        <li>222</li>
        <li>333</li>
        <li>444</li>
        <li>555</li>
        ...
    </ul>
</main>
var _element = document.getElementById('refreshContainer'),
    _refreshText = document.querySelector('.refreshText'),
    _startPos = 0,  // 初始的值
    _transitionHeight = 0; // 移动的距离

_element.addEventListener('touchstart', function(e) {
    _startPos = e.touches[0].pageY; // 记录初始位置
    _element.style.position = 'relative';
    _element.style.transition = 'transform 0s';
}, false);

_element.addEventListener('touchmove', function(e) {
    // e.touches[0].pageY 当前位置
    _transitionHeight = e.touches[0].pageY - _startPos; // 记录差值

    if (_transitionHeight > 0 && _transitionHeight < 60) { 
        _refreshText.innerText = '下拉刷新'; 
        _element.style.transform = 'translateY('+_transitionHeight+'px)';

        if (_transitionHeight > 55) {
            _refreshText.innerText = '释放更新';
        }
    }                
}, false);

_element.addEventListener('touchend', function(e) {
    _element.style.transition = 'transform 0.5s ease 1s';
    _element.style.transform = 'translateY(0px)';
    _refreshText.innerText = '更新中...';
    // todo...

}, false);

从上面可以看到,在下拉到松手的过程中,经历了三个阶段:

  • 当前手势滑动位置与初始位置差值大于零时,提示正在进行下拉刷新操作
  • 下拉到一定值时,显示松手释放后的操作提示
  • 下拉到达设定最大值松手时,执行回调,提示正在进行更新操作

案例

这里使用第三方插件better-scroll

<div id="position-wrapper">
    <div>
        <p class="refresh">下拉刷新</p>
        <div class="position-list">
   <!--列表内容-->
        </div>
        <p class="more">查看更多</p>
    </div>
</div>

实例化上拉下拉插件,通过use来注册插件

import BScroll from "@better-scroll/core";
import PullDown from "@better-scroll/pull-down";
import PullUp from '@better-scroll/pull-up';
BScroll.use(PullDown);
BScroll.use(PullUp);

实例化BetterScroll,并传入相关的参数

let pageNo = 1,pageSize = 10,dataList = [],isMore = true;  
var scroll= new BScroll("#position-wrapper",{
    scrollY:true,//垂直方向滚动
    click:true,//默认会阻止浏览器的原生click事件,如果需要点击,这里要设为true
    pullUpLoad:true,//上拉加载更多
    pullDownRefresh:{
        threshold:50,//触发pullingDown事件的位置
        stop:0//下拉回弹后停留的位置
    }
});
//监听下拉刷新
scroll.on("pullingDown",pullingDownHandler);
//监测实时滚动
scroll.on("scroll",scrollHandler);
//上拉加载更多
scroll.on("pullingUp",pullingUpHandler);

async function pullingDownHandler(){
    dataList=[];
    pageNo=1;
    isMore=true;
    $(".more").text("查看更多");
    await getlist();//请求数据
    scroll.finishPullDown();//每次下拉结束后,需要执行这个操作
    scroll.refresh();//当滚动区域的dom结构有变化时,需要执行这个操作
}
async function pullingUpHandler(){
    if(!isMore){
        $(".more").text("没有更多数据了");
        scroll.finishPullUp();//每次上拉结束后,需要执行这个操作
        return;
    }
    pageNo++;
    await this.getlist();//请求数据
    scroll.finishPullUp();//每次上拉结束后,需要执行这个操作
    scroll.refresh();//当滚动区域的dom结构有变化时,需要执行这个操作    
}
function scrollHandler(){
    if(this.y>50) $('.refresh').text("松手开始加载");
    else $('.refresh').text("下拉刷新");
}
function getlist(){
    //返回的数据
    let result=....;
    dataList=dataList.concat(result);
    //判断是否已加载完
    if(result.length<pageSize) isMore=false;
    //将dataList渲染到html内容中
}

注意点:
使用better-scroll实现下拉刷新、上拉加载时要注意以下几点:

  • wrapper里必须只有一个子元素
  • 子元素的高度要比wrapper要高
  • 使用的时候,要确定DOM元素是否已经生成,必须要等到DOM渲染完成后,再new BScroll()
  • 滚动区域的DOM元素结构有变化后,需要执行刷新 refresh()
  • 上拉或者下拉,结束后,需要执行finishPullUp()或者finishPullDown(),否则将不会执行下次操作
  • better-scroll,默认会阻止浏览器的原生click事件,如果滚动内容区要添加点击事件,需要在实例化属性里设置click:true

22、如何使用css完成视差滚动效果

是什么

视差滚动(Parallax Scrolling)是指多层背景以不同的速度移动,形成立体的运动效果,带来非常出色的视觉体验

分析

我们可以把网页解刨成:背景层、内容层、悬浮层

微信图片_20210422162710.jpg

当滚动鼠标滑轮的时候,各个图层以不同的速度移动,形成视觉差的效果

微信图片_20210422162850.gif

实现方式

使用css形式实现的方式有:

background-attachment

作用:设置背景图像是否固定或者随着页面的其余部分滚动
值:
① scroll , 默认值,背景图像会随着页面其余部分的滚动而滚动
② fixed,当页面的其余部分滚动时,背景图像不会移动
③ inherit,继承父元素background-attachment属性的值

完成滚动视觉差就需要把值设置为fixed,让背景相对于视口固定;即使一个元素有滚动机制,背景也不会随着元素的内容而滚动,也就是说背景一开始就已经被固定在初始的位置

<style>
div {
            height: 100vh;
            background: rgba(0, 0, 0, .7);
            color: #fff;
            line-height: 100vh;
            text-align: center;
            font-size: 20vh;
        }

        .a-img1 {
            background-image: url(https://images.pexels.com/photos/1097491/pexels-photo-1097491.jpeg);
            background-attachment: fixed;
            background-size: cover;
            background-position: center center;
        }

        .a-img2 {
            background-image: url(https://images.pexels.com/photos/2437299/pexels-photo-2437299.jpeg);
            background-attachment: fixed;
            background-size: cover;
            background-position: center center;
        }

        .a-img3 {
            background-image: url(https://images.pexels.com/photos/1005417/pexels-photo-1005417.jpeg);
            background-attachment: fixed;
            background-size: cover;
            background-position: center center;
        }
</style>
<div class="a-text">1</div>
<div class="a-img1">2</div>
<div class="a-text">3</div>
<div class="a-img2">4</div>
<div class="a-text">5</div>
<div class="a-img3">6</div>
<div class="a-text">7</div>

transform: translate3D

了解这个,需要先了解两个概念

transform

css3属性,可以对元素进行变换(2d/3d),包括平移translate,rotate,scale等

perspective

css3属性,当元素涉及到3d变换时,此属性可以定义我们所看到的3d立体效果

微信图片_20210422164040.jpg

<style>
    html {
        overflow: hidden;
        height: 100%
    }

    body {
        /* 视差元素的父级需要3D视角 */
        perspective: 1px;
        transform-style: preserve-3d; 
        height: 100%;
        overflow-y: scroll;
        overflow-x: hidden;
    }
    #app{
        width: 100vw;
        height:200vh;
        background:skyblue;
        padding-top:100px;
    }
    .one{
        width:500px;
        height:200px;
        background:#409eff;
        transform: translateZ(0px);
        margin-bottom: 50px;
    }
    .two{
        width:500px;
        height:200px;
        background:#67c23a;
        transform: translateZ(-1px);
        margin-bottom: 150px;
    }
    .three{
        width:500px;
        height:200px;
        background:#e6a23c;
        transform: translateZ(-2px);
        margin-bottom: 150px;
    }
</style>
<div id="app">
    <div class="one">one</div>
    <div class="two">two</div>
    <div class="three">three</div>
</div>

image.png
这种方式实现视觉滚动的原理如下:
① 在容器上设置transform-style: preserve-3d 和 prespective: …px,那么处于这个容器的子元素就将位于3D空间
② 子元素设置不同的transform: translateZ(),这个时候,不同元素在3D z轴方向距离屏幕的距离也就不一样
③ 滚动滚动条,由于子元素设置了不同的Z值,那么滚动的上下距离translateY相对屏幕也是不一样的,从而达到滚动视差的效果

23、如何使用css画一个三角形

实现过程

默认情况是一个矩形

<style>
    .border {
        width: 50px;
        height: 50px;
        border: 2px solid;
        border-color: #96ceb4 #ffeead #d9534f #ffad60;
    }
</style>
<div class="border"></div>

image.png
将border设置50px,

image.png

白色区域则为width,height,这时候只需要将白色区域部分宽高逐渐变小,最终变为0,变为0后就可以看到四个不同颜色的三角形,如果需要下方三角形,则只需要将上,左,右边框设置为透明就可以了

image.png

这种方式虽然实现了三角形,但实际上,隐藏的部分依然占据着部分高度,需要将上方的宽度去掉

.border {
    width: 0;
    height: 0;
    border-style:solid;
    border-width: 0 50px 50px;
    border-color: transparent transparent #d9534f;
}

如果要实现一个只有边框是空心的三角形,由于这里不能再使用border属性,所以最直接的方法是利用伪类新建一个小一点的三角形定位上去

.border {
    width: 0;
    height: 0;
    border-style:solid;
    border-width: 0 50px 50px;
    border-color: transparent transparent #d9534f;
    position: relative;
}
.border:after{
    content: '';
    border-style:solid;
    border-width: 0 40px 40px;
    border-color: transparent transparent #96ceb4;
    position: absolute;
    top: 0;
    left: 0;
}

image.png

伪类元素定位参照对象的内容区域宽高都为0,则内容区域即可以理解成中心一点,所以伪元素相对中心这点定位,将元素定位并进行微调颜色即可

.border:after {
    content: '';
    border-style: solid;
    border-width: 0 40px 40px;
    border-color: transparent transparent #96ceb4;
    position: absolute;
    top: 6px;
    left: -40px;
}

实现原理

可以看到,边框是实现三角形的部分,边框实际上并不是一个直线,如果我们将四条边设置不同的颜色,将边框逐渐放大,可以得到每条边框都是一个梯形

image.png

当分别取消边框的时候,会发现下面几种情况:
① 取消一条边的时候,与这条边相邻的两条边的接触部分会变成直的
② 当仅有邻边时, 两个边会变成对分的三角
③ 当保留边没有其他接触时,极限情况所有东西都会消失

image.png

通过上图的变化规则,利用旋转、隐藏,以及设置内容宽高等属性,就能够实现其他类型的三角形

如设置直角三角形,如上图倒数第三行实现过程,我们就能知道整个实现原理

.box {
    /* 内部大小 */
    width: 0px;
    height: 0px;
    /* 边框大小 只设置两条边*/
    border-top: #4285f4 solid;
    border-right: transparent solid;
    border-width: 85px; 
    /* 其他设置 */
    margin: 50px;
}

24、如何使用css实现单行/多行文本溢出的省略样式

单行文本溢出省略

文本在一行内显示,超出的部分以省略号的形式展现

实现原理:
① text-overflow:规定当文本溢出时,显示省略符号来代表被修剪的文本
② white-space:设置文字在一行显示,不能换行
③ overflow:文字长度超出限定宽度,则隐藏超出的内容

overflow设为hidden,普通情况用在块级元素的外层隐藏内部溢出元素,或者配合下面两个属性实现文本溢出省略

white-space:nowrap,作用是设置文本不换行,是overflow:hidden和text-overflow:ellipsis生效的基础

text-overflow属性值有如下:

  • clip:当对象内文本溢出部分裁切掉
  • ellipsis:当对象内文本溢出时显示省略标记(…)

text-overflow只有在设置了overflow:hidden和white-space:nowrap才能够生效的

<style>
    p{
        overflow: hidden;
        line-height: 40px;
        width:400px;
        height:40px;
        border:1px solid red;
        text-overflow: ellipsis;
        white-space: nowrap;
    }
</style>
<p 这是一些文本这是一些文本这是一些文本这是一些文本这是一些文本这是一些文本这是一些文本这是一些文本这是一些文本这是一些文本</p>

image.png

多行文本溢出省略

基于高度截断

伪元素 + 定位

<style>
    .demo {
        position: relative;
        line-height: 20px;
        height: 40px;
        overflow: hidden;
    }
    .demo::after {
        content: "...";
        position: absolute;
        bottom: 0;
        right: 0;
        padding: 0 20px 0 10px;
    }
</style>

<body>
    <div class='demo'>这是一段很长的文本</div>
</body>

实现原理很好理解,就是通过伪元素绝对定位到行尾并遮住文字,再通过overflow:hidden隐藏多余的文字,这种实现有如下优点:
① 兼容性好,对各大主流浏览器有很好的支持
② 响应式截断,根据不同的宽度做出调整

一般在存在英文的时候,可以设置word-break: break-all使一个单词能够在换行时进行拆分

基于行数截断

<style>
    p {
        width: 400px;
        border-radius: 1px solid red;
        -webkit-line-clamp: 2;
        display: -webkit-box;
        -webkit-box-orient: vertical;
        overflow: hidden;
        text-overflow: ellipsis;
    }
</style>
<p>
    这是一些文本这是一些文本这是一些文本这是一些文本这是一些文本
    这是一些文本这是一些文本这是一些文本这是一些文本这是一些文本
</p>

-webkit-line-clamp: 2:用来限制在一个块元素显示的文本的行数,为了实现该效果,它需要组合其他的WebKit属性)
display: -webkit-box:和1结合使用,将对象作为弹性伸缩盒子模型显示
-webkit-box-orient: vertical:和1结合使用 ,设置或检索伸缩盒对象的子元素的排列方式
overflow: hidden:文本溢出限定的宽度就隐藏内容
text-overflow: ellipsis:多行文本的情况下,用省略号“…”隐藏溢出范围的文本

25、css常见动画有哪些,实现方式

是什么

CSS动画(CSS Animations)是为层叠样式表建议的允许可扩展标记语言(XML)元素使用CSS的动画的模块
即指元素从一种样式逐渐过渡为另一种样式的过程

常见的动画效果有很多,如平移、旋转、缩放等等,复杂动画则是多个简单动画的组合

实现方式

transition 实现渐变动画

transition的属性如下:
① property: 填写需要变化的css属性
② duration: 完成过渡效果需要的时间单位(s或者ms)
③ timing-function: 完成效果的速度曲线
④ delay: 动画效果的延迟触发时间

image.png

<style>
       .base {
            width: 100px;
            height: 100px;
            display: inline-block;
            background-color: #0EA9FF;
            border-width: 5px;
            border-style: solid;
            border-color: #5daf34;
            transition-property: width, height, background-color, border-width;
            transition-duration: 2s;
            transition-timing-function: ease-in;
            transition-delay: 500ms;
        }

        /*简写*/
        /*transition: all 2s ease-in 500ms;*/
        .base:hover {
            width: 200px;
            height: 200px;
            background-color: #5daf34;
            border-width: 10px;
            border-color: #3a8ee6;
        }
</style>
<div class="base"></div>

transform 实现转变动画

包含四个常用的功能:
① translate:位移
② scale:缩放
③ rotate:旋转
④ skew:倾斜

一般配合transition过度使用

注意的是,transform不支持inline元素,使用前把它变成block

<style>
    .base {
        width: 100px;
        height: 100px;
        display: inline-block;
        background-color: #0EA9FF;
        border-width: 5px;
        border-style: solid;
        border-color: #5daf34;
        transition-property: width, height, background-color, border-width;
        transition-duration: 2s;
        transition-timing-function: ease-in;
        transition-delay: 500ms;
    }
    .base2 {
        transform: none;
        transition-property: transform;
        transition-delay: 5ms;
    }

    .base2:hover {
        transform: scale(0.8, 1.5) rotate(35deg) skew(5deg) translate(15px, 25px);
    }
</style>
 <div class="base base2"></div>

animation 实现自定义动画

image.png

CSS动画只需要定义一些关键的帧,而其余的帧,浏览器会根据计时函数插值计算出来,
通过@keyframes来定义关键帧

因此,如果我们想要让元素旋转一圈,只需要定义开始和结束两帧即可:

@keyframes rotate{
    from{
        transform: rotate(0deg);
    }
    to{
        transform: rotate(360deg);
    }
}

from表示最开始的那一帧,to表示结束时的那一帧

也可以使用百分比刻画生命周期

@keyframes rotate{
    0%{
        transform: rotate(0deg);
    }
    50%{
        transform: rotate(180deg);
    }
    100%{
        transform: rotate(360deg);
    }
}

26、说说函数的节流和防抖,有什么区别,如何实现

是什么

本质上是一种优化高频率执行代码的一种手段

例如:浏览器的resize、scroll、keypress、mousemove等事件在触发时,会不断地调用绑定在事件上的回调函数,极大地浪费资源,降低前端性能

为了优化体验,需要对这类事件进行调用次数的限制,对此我们就可以采用throttle(节流)和debounce(防抖)的方式来减少调用频率

节流:n 秒内只运行一次,若在 n 秒内重复触发,只有一次执行
防抖:n 秒后在执行该事件,若在 n 秒内被重复触发,则重新计时

理解:

想象每天上班大厦底下的电梯。把电梯完成一次运送,类比为一次函数的执行和响应;假设电梯有两种运行策略debounce和throttle,超时设定为15秒,不考虑容量限制;

电梯第一个人进来后,15秒后准时运送一次,这是节流

电梯第一个人进来后,等待15秒。如果过程中又有人进来,15秒等待重新计时,直到15秒后开始运送,这是防抖

实现

节流

function throttled(fn, delay) {
    let timer = null
    let starttime = Date.now()
    return function () {
        let curTime = Date.now() // 当前时间
        let remaining = delay - (curTime - starttime)  // 从上一次到现在,还剩下多少多余时间
        let context = this
        let args = arguments
        clearTimeout(timer)
        if (remaining <= 0) {
            fn.apply(context, args)
            starttime = Date.now()
        } else {
            timer = setTimeout(fn, remaining);
        }
    }
}

防抖

function debounce(func, wait) {
    let timeout;

    return function () {
        let context = this; // 保存this指向
        let args = arguments; // 拿到event对象

        clearTimeout(timeout)
        timeout = setTimeout(function(){
            func.apply(context, args)
        }, wait);
    }
}

防抖如果需要立即执行,可加入第三个参数用于判断,实现如下:

function debounce(func, wait, immediate) {

    let timeout;

    return function () {
        let context = this;
        let args = arguments;

        if (timeout) clearTimeout(timeout); // timeout 不为null
        if (immediate) {
            let callNow = !timeout; // 第一次会立即执行,以后只有事件执行后才会再次触发
            timeout = setTimeout(function () {
                timeout = null;
            }, wait)
            if (callNow) {
                func.apply(context, args)
            }
        }
        else {
            timeout = setTimeout(function () {
                func.apply(context, args)
            }, wait);
        }
    }
}

区别

相同点:
① 都可以通过使用setTimeout实现
② 目的都是,降低回调执行频率。节省计算资源

不同点:
① 函数防抖,在一段连续操作结束后,处理回调,利用clearTimeout和setTimeout实现。函数节流,在一段连续操作中,每一段时间只执行一次,频率较高的事件中使用来提高性能
② 函数防抖关注一定时间连续触发的事件,只在最后执行一次,而函数节流一段时间内只执行一次

image.png

应用场景

防抖在连续的事件,只需触发一次回调的场景有:
① 搜索框搜索输入。只需用户最后一次输入完,再发送请求
② 手机号、邮箱验证输入检测
③ 窗口大小resize。只需窗口调整完成后,计算窗口大小。防止重复渲染。

节流在间隔一段时间执行一次回调的场景有:
① 滚动加载,加载更多或滚到底部监听
② 搜索框,搜索联想功能

27、说一下new操作符具体干了什么

是什么

在JavaScript中,new操作符用于创建一个给定构造函数的实例对象

function Person(name, age){
    this.name = name;
    this.age = age;
}
Person.prototype.sayName = function () {
    console.log(this.name)
}
const person1 = new Person('Tom', 20)
console.log(person1)  // Person {name: "Tom", age: 20}
t.sayName() // 'Tom'

从上面可以看到:

new通过构造函数Person创建出来的实例可以访问到构造函数中的属性

new通过构造函数Person创建出来的实例可以访问到构造函数原型链中的属性(即实例与构造函数通过原型链连接了起来)

现在在构建函数中显式加上返回值,并且这个返回值是一个原始类型

function Test(name) {
  this.name = name
  return 1
}
const t = new Test('xxx')
console.log(t.name) // 'xxx'

可以发现,构造函数中返回一个原始值,然而这个返回值并没有作用
下面在构造函数中返回一个对象

function Test(name) {
  this.name = name
  console.log(this) // Test { name: 'xxx' }
  return { age: 26 }
}
const t = new Test('xxx')
console.log(t) // { age: 26 }
console.log(t.name) // 'undefined'

从上面可以发现,构造函数如果返回值为一个对象,那么这个返回值会被正常使用

流程

从上面介绍中,我们可以看到new关键字主要做了以下的工作:
① 创建一个新的对象obj
② 将对象与构建函数通过原型链连接起来
③ 将构建函数中的this绑定到新建的对象obj上
④ 根据构建函数返回类型作判断,如果是原始值则被忽略,如果是返回对象,需要正常处理

function Person(name, age){
    this.name = name;
    this.age = age;
}
const person1 = new Person('Tom', 20)
console.log(person1)  // Person {name: "Tom", age: 20}
t.sayName() // 'Tom'

image.png

手写

function mynew(Func, ...args) {
    // 1.创建一个新对象
    const obj = {}
    // 2.新对象原型指向构造函数原型对象
    obj.__proto__ = Func.prototype
    // 3.将构建函数的this指向新对象
    let result = Func.apply(obj, args)
    // 4.根据返回值判断
    return result instanceof Object ? result : obj
}

function Person(name, age) {
    this.name = name;
    this.age = age;
}
Person.prototype.say = function () {
    console.log(this.name)
}

let p = mynew(Person, "huihui", 123)
console.log(p) // Person {name: "huihui", age: 123}
p.say() // huihui