目录
13、说说你对Object.defineProperty()的理解?
16、说说你对git rebase 和git merge的理解?区别?
1、SPA(单页应用)首屏加载速度慢怎么解决?
首屏加载慢的原因:
1.网络延迟问题
2.资源文件体积是否过大
3.资源是否重复发送请求去加载了
4.加载脚本的时候,渲染内容堵塞了
解决方法:
1.减少入口文件体积
2.静态资源本地缓存
3.UI框架按需加载
4.图片资源的压缩
5.组件重复打包
6.开启GZip压缩
7.使用SSR
2、Vue中自定义指令的理解,应用场景有哪些?
一、什么是指令
指令系统是计算机硬件的语言系统,也叫机器语言,它是系统程序员看到的计算机的主要属性。因此指令系统表征了计算机的基本功能决定了机器所要求的能力
在vue
中提供了一套为数据驱动视图更为方便的操作,这些操作被称为指令系统
我们看到的v-
开头的行内属性,都是指令,不同的指令可以完成或实现不同的功能
除了核心功能默认内置的指令 (v-model
和 v-show
),Vue
也允许注册自定义指令
二、如何实现
注册一个自定义指令有全局注册与局部注册
全局注册注册主要是用过Vue.directive
方法进行注册
Vue.directive
第一个参数是指令的名字(不需要写上v-
前缀),第二个参数可以是对象数据,也可以是一个指令函数
局部注册通过在组件options
选项中设置directive
属性
三、应用场景
用自定义组件组件可以满足我们日常一些场景,这里给出几个自定义组件的案例:
-
防抖
-
图片懒加载
-
一键 Copy的功能
3、说说React生命周期中有哪些坑?如何避免?
哪些坑?
1.getDerviedStateFromProps容易编写反模式代码,使受控组件和非受控组件容易混乱
2.网络请求应该放在componentDidMount生命周期钩子函数中
3.ComponentWillReceivedProps被getDerviedStateFromProps所取代,主要因为性能问题
4.ShouldcomponentUpdate通过返回true或false来确定是否要渲染更新
5.componentWillUpdate 同样是由于新的异步渲染机制,而被标记废弃,不推荐使用,原先的逻辑可结合 getSnapshotBeforeUpdate 与 componentDidUpdate 改造使用。
如何避免?
不在恰当的时候调用不该调用的代码
在该调用的时候不要忘记调用
4、说说Real diff算法是怎么运作的?
虚拟DOM在react中不提倡使用真实的DOM节点,使用虚拟DOM可以提高渲染的速度,在界面发生变化时,通过diff算法对比新旧DOM树,如果用变化,则渲染变化的DOM节点,反之则不该
diff算法:是用来对比两个虚拟DOM树的,通过key属性,如果有key属性,就进行新旧DOM树的比对,如果没有就重新渲染,在进行新旧DOM树比对的时候,如果内容没发生改变,就不要重新渲染了,如果发生了改变,则只重新渲染发生改变的dom节点
5、调和阶段setState做了什么?
在组件中调用了this.setState这个函数之后,react会将传入的参数与当前组件的状态进行合并,然后触发所谓的调和过程。
经过调和之后,react会以高效地方式根据新的状态构建一个react元素数,并且着手渲染整个UI页面。
react得到元素树后,react会进行diff算法进行对比,对比出新树与旧树的差异,然后根据差异对界面进行最小化的更新渲染。
在差异计算算法中,react能够相对准确地知道那些位置放生了改变以及应该如何改变,这就保证了按需更新,而不是全部更新
6、为什么React元素有一个$$type属性?
这个 $$typeof 是什么?
各种前端框架出现之前,应用通常会构造 HTML 并将它们插入到 DOM 中,例如:
const messageEl = document.getElementById(‘message’);
messageEl.innerHTML = ‘<p>’ + message.text + ‘</p>’;
这代码一般能正常工作,除非你的 message.text 返回 ‘<img src οnerrοr=”stealYourPassword()”>’ 这样的字符串。
为了防止这种情况,可以使用类似于 document.createTextNode()或textContent 仅处理 text 的 api。也可以通过预处理替换 < > 这类特殊的字符。但是这样还是有很多潜在的问题。所以现代前端框架像 React 会为字符串转义成文字内容:
<p>
{message.text}
</p>
就算 message.text 返回 <img> 也会当成字符串处理。要在 React 元素中呈现 HTML ,则需要编写dangerouslySetInnerHTML={{ __html: message.text }},这有助于你在审查代码的时候重视它。
但是这样仍然无法完全禁止注入攻击,例如:<a href={user.website}>,注意 website 为 ‘javascript: stealYourPassword()’的情况。 对用户输入使用 … 运算符也很危险 <div {…userData}>。
为什么需要 $$typeof
通过上面的介绍我们知道,$$typeof 是为了防止 XSS 攻击。
React 0.14 使用 Symbol 标记每个 React 元素。
{
type: ‘marquee’,
props: {
bgcolor: ‘#ffa7c4’,
children: ‘hi’,
},
key: null,
ref: null,
$$typeof: Symbol.for(‘react.element’),
}
这样就可以规避这个问题,使用 Symbol 类型是因为 JSON 中无法传递 Symbol。React 会检查 element.$$typeof 然后拒绝处理非法的元素。
7、说说Connect组件的原理是什么?
原理
connect([mapStateToProps], [mapDispatchToProps])(component)
看connect使用方法,接收参数为mapStateToProps和mapDispatchToProps两个方法,返回的函数接收参数是组件,从而返回一个新的组件。
在入口文件 中使用Provider组件,在原应用组件上包裹一层,使原来整个应用成为Provider的子组件,接收Redux的store作为props,通过context对象传递给子孙组件上的connect
connect真正连接 Redux 和 React,它包在我们的容器组件的外一层,它接收上面 Provider 提供的 store 里面的 state 和 dispatch,传给一个构造函数,返回一个对象,以属性形式传给我们的容器组件。
总结:react-redux 库提供的一个 API,connect 的作用是让你把组件和store连接起来,产生一个新的组件(connect 是高阶组件)
作用
(1)原组件和store做连接
(2)connect方法使得UI组件变成了容器组件(包含了业务逻辑和数据状态管理)
(3)connect使得组件和store的数据同步关联,connect: store的state数据一变,组件的state会跟着变
(4)属于是一个高阶组件,本质上是一个函数
8、说说你对fiber架构的理解?解决了什么问题?
是什么?
Fiber 是 Facebook 花费两年余时间对 React 做出的一个重大改变与优化,是对 React 核心算法的一次重新实现。从Facebook在 React Conf 2017 会议上确认,React Fiber 在React 16 版本发布
在react中,主要做了以下的操作:
为每个增加了优先级,优先级高的任务可以中断低优先级的任务。然后再重新,注意是重新执行优先级低的任务
增加了异步任务,调用requestIdleCallback api,浏览器空闲的时候执行
dom diff树变成了链表,一个dom对应两个fiber(一个链表),对应两个队列,这都是为找到被中断的任务,重新执行
从架构角度来看,Fiber 是对 React核心算法(即调和过程)的重写
从编码角度来看,Fiber是 React内部所定义的一种数据结构,它是 Fiber树结构的节点单位,也就是 React 16 新架构下的虚拟DOM
一个 fiber就是一个 JavaScript对象,包含了元素的信息、该元素的更新操作队列、类型
如何解决?
Fiber把渲染更新过程拆分成多个子任务,每次只做一小部分,做完看是否还有剩余时间,如果有继续下一个任务;如果没有,挂起当前任务,将时间控制权交给主线程,等主线程不忙的时候在继续执行
即可以中断与恢复,恢复后也可以复用之前的中间状态,并给不同的任务赋予不同的优先级,其中每个任务更新单元为 React Element 对应的 Fiber节点
实现的上述方式的是requestIdleCallback方法
window.requestIdleCallback()方法将在浏览器的空闲时段内调用的函数排队。这使开发者能够在主事件循环上执行后台和低优先级工作,而不会影响延迟关键事件,如动画和输入响应
首先 React 中任务切割为多个步骤,分批完成。在完成一部分任务之后,将控制权交回给浏览器,让浏览器有时间再进行页面的渲染。等浏览器忙完之后有剩余时间,再继续之前 React 未完成的任务,是一种合作式调度。
该实现过程是基于 Fiber节点实现,作为静态的数据结构来说,每个 Fiber 节点对应一个 React element,保存了该组件的类型(函数组件/类组件/原生组件等等)、对应的 DOM 节点等信息。
作为动态的工作单元来说,每个 Fiber 节点保存了本次更新中该组件改变的状态、要执行的工作。
9、说说你对事件循环event loop的理解?
JavaScript是一门单线程的语言,也就是说同一时间只能去做一件事情,为了解决单线程的阻塞问题,JavaScript用到了一种计算机的运行机制,这种运行机制就叫做事件循环
在JavaScript中所有的任务都可以分为:
同步任务:立即执行的任务,同步任务一般会直接进入到主线程中进行执行
异步任务:同步任务执行完之后才会执行异步任务;比如ajax请求,setTimeout定时函数
10、前端跨域的解决方案?
1.JSONP
2.nginx代理
3.WebSocket协议跨域
4.CORS
11、说说你对vue中mixin的理解?
是什么?
混入 (mixins): 是一种分发 Vue 组件中可复用功能的非常灵活的方式。混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被混入该组件本身的选项。
特点?
1 方法和参数在各组件中不共享
2 值为对象的选项,如methods,components等,选项会被合并,键冲突的组件会覆盖混入对象的
3 值为函数的选项,如created,mounted等,就会被合并调用,混合对象里的钩子函数在组件里的钩子函数之前调用
与vuex的区别
-
vuex:用来做状态管理的,里面定义的变量在每个组件中均可以使用和修改,在任一组件中修改此变量的值之后,其他组件中此变量的值也会随之修改。
-
Mixins:可以定义共用的变量,在每个组件中使用,引入组件中之后,各个变量是相互独立的,值的修改在组件中不会相互影响。
与公共组件的区别
同样明显的区别来再列一遍哈~
-
组件:在父组件中引入组件,相当于在父组件中给出一片独立的空间供子组件使用,然后根据props来传值,但本质上两者是相对独立的。
-
Mixins:则是在引入组件之后与组件中的对象和方法进行合并,相当于扩展了父组件的对象与方法,可以理解为形成了一个新的组件。
12、for...in循环和for...of循环的区别?
for...in和for...of都是JavaScript中遍历数据的方法
for...in是为遍历对象属性而构建的,它以任意顺序遍历一个对象的除Symbol以外的可枚举属性,可用break或者throw跳出
for...of语句在可迭代对象上创建一个迭代循环,调用自定义迭代钩子,并为每个不同属性的值执行语句(包括Array,Map,Set,String,TypedArray,arguments等等,不包括Object),可用break或者throw跳出。
13、说说你对Object.defineProperty()的理解?
Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。
该方法接受三个参数:
obj:必需。目标对象
prop:必需。需定义或修改的属性的名字
descriptor:必需。目标属性所拥有的特性
var obj = {test:'sss'}
Object.defineProperty(obj,'test',{
value:任意类型的值, //不设置该属性 ,默认为undefined
configurable: true | false, // 是否可以delete删除目标属性或者是否可以再次修改属性的特性
enumerable: true | false, //是否可以被枚举(for..in |Object.keys())
writable: true | false //是否可以被重写
})
var obj = {}
var initValue = 'hello'
Object.defineProperty(obj,"newKey",{
get:function(){ return initValue;} | undefined, //不设置get方法,则默认undefined
set:function(value){ initValue = value; } | undefined,
configurable: true | false,
enumerable: true | false
})
14、说说你对webSocket的理解?
WebSocket是HTML5开始提供的一种浏览器与服务器间进行全双工通讯的网络技术。依靠这种技术可以实现客户端和服务器端的长连接,双向实时通信。
它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种。
其他特点包括:
- 建立在 TCP 协议之上,服务器端的实现比较容易。
- 与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。
- 数据格式比较轻量,性能开销小,通信高效。
- 可以发送文本,也可以发送二进制数据。
- 没有同源限制,客户端可以与任意服务器通信。
- 协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。
协议标识符是ws(如果加密,则为wss),服务器网址就是 URL
15、最少说出三种前端清除浮动的方法?
使用带clear属性的空元素
使用css的overflow属性
使用css的::after属性
16、说说你对git rebase 和git merge的理解?区别?
理解:
git rebase 和 git merge 都是将一个分支的提交合并到另一分支上
通过 merge 合并分支会新增一个 merge commit 然后将两个分支的历史联系起来
rebase 会将整个分支移动到另一个分支上,主要的好处是历史记录更加清晰,不好的是会丢失一些分支从何时创建及合并进来的一些信息。
区别:
merge
通过merge合并分支会新增一个merge commit,然后将两个分支的历史联系起来
其实是一种非破坏性的操作,对现有分支不会以任何方式被更改,但是会导致历史记录相对复杂
rebase
rebase会将整个分支移动到另一个分支上,有效地整合了所有分支上的提交
主要的好处是历史记录更加清晰,是在原有提交的基础上将差异内容反映进去,消除了 git merge所需的不必要的合并提交
17、说说你对栈、队列的理解?应用场景?
是什么?
栈:又名堆栈,它是一种运算受限的线性表,限定仅在表尾进行插入和删除操作的线性表,
队列:跟栈十分相似,队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作
应用场景
栈:借助栈的先进后出的特性,可以简单实现一个逆序数处的功能,首先把所有元素依次入栈,然后把所有元素出栈并输出
栈的特点就是跟坐电梯一样,先进的后出,后进的先出
队列:当我们需要按照一定的顺序来处理数据,而该数据的数据量在不断地变化的时候,则需要队列来帮助解题队列的使用广泛应用在广度优先搜索种
队列的特点就是跟排队一样
18、前端性能优化的手段有哪些?
前端性能优化分为两类:
1.文件加载更快:
① 让传输的数据包更小(压缩文件/图片):图片压缩和文件压缩
②减少网络请求的次数:雪碧图/精灵图、节流防抖
③减少渲染的次数:缓存(HTTP缓存、本地缓存、Vue的keep-alive缓存等)
2.文件渲染更快:
①提前渲染:ssr服务器端渲染
②避免渲染阻塞:CSS放在HTML的head中 JS放在HTML的body底部
③ 避免无用渲染:懒加载
④ 减少渲染次数:对dom查询进行缓存、将dom操作合并、使用减少重排的标签
19.大文件如何做断点续传?
1.客户端下载一个1024K的文件,已经下载了其中512K
2. 网络中断,客户端请求续传,因此需要在HTTP头中申明本次需要续传的片段:
Range:bytes=512000-
这个头通知服务端从文件的512K位置开始传输文件
3. 服务端收到断点续传请求,从文件的512K位置开始传输,并且在HTTP头中增加:
Content-Range:bytes 512000-/1024000
并且此时服务端返回的HTTP状态码应该是206,而不是200。
20.原生js如何实现上拉加载下拉刷新?
1.上拉加载
使用场景:移动端历史日志、内容列表等模块中使用
简述:当列表滑动到底部时,再做请求加载下一页列表,有利于减少http请求和浏览器渲染压力,提高页面首次加载速度
原理:
获取滑动内容外部盒子高度:document.querySelector(".container").clientHeight
获取整个内容的高度:document.querySelector(".ul").scrollHeight
获取卷曲出去的高度:let tops = document.querySelector(".container").scrollTop
外部盒子高度+卷曲出去的高度>=内容的高度 - 离底部距离高度(自定义)
监听滑动事件scroll 当满足以上条件时执行异步请求,这里需要加一个节流,当触发是禁止再次触发,等到异步事件加载完毕后,才允许再次上拉触发。
2.下拉刷新
简述:向下滑动页面刷新数据
原理:
监听touchstart、touchmove、touchend三个事件
通过touchstart记录手指触摸时位置
通过touchmove记录向下滑动的距离,同时通过设置transform:translateY(x)来设置内容向下移动,控制滑动距离到达某个值时,禁止页面再做滑动
通过touchend记录手指离开事件,对滑动距离达到限定值时,做刷新请求数据处理,未到限定值时自动回弹translateY(0px),并添加过渡动画
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<meta name="viewport"
content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
</head>
<style type="text/css">
* {
margin: 0;
padding: 0;
list-style: none;
}
.container {
width: 100vw;
height: 100vh;
overflow: scroll;
}
.ul li {
width: 100%;
height: 70px;
line-height: 70px;
border-bottom: 1px solid #000000;
text-align: center;
}
.tip {
width: 100%;
text-align: center;
height: 80px;
line-height: 80px;
position: absolute;
top: 0;
left: 0;
}
</style>
<body>
<div class="container">
<p class="p"></p>
<ul class="ul"> </ul>
</div>
</body>
<script type="text/javascript"> init()
function init() {
for (let i = 1; i <= 10; i++) {
var bd = document.createElement("li");
bd.innerHTML = i
document.querySelector(".ul").appendChild(bd)
}
} //上拉加载
let hook = true
document.querySelector(".container").addEventListener("scroll",
function () {
let Height = document.querySelector(".container").clientHeight
let height = document.querySelector(".ul").scrollHeight
let tops = document.querySelector(".container").scrollTop
if (Height + tops >= height - 50 && hook) {
hook = false
console.log("添加数据")
for (let i = 1; i <= 10; i++) {
var bd = document.createElement("li");
bd.innerHTML = i
document.querySelector(".ul").appendChild(bd)
} hook = true
}
}) //下拉刷新
var stratX = null
var endX = null
document.querySelector(".container").addEventListener("touchstart", function (e) {
if (this.scrollTop == 0) {
stratX = e.touches[0].clientY
}
})
document.querySelector(".container").addEventListener("touchmove", function (e) {
if (this.scrollTop == 0) {
endX = e.touches[0].clientY - stratX
if (endX < 100) { document.querySelector(".ul").style.transition = 'transform 0s ease'; document.querySelector(".ul").style.transform = 'translateY(' + endX + 'px)' }
}
})
document.querySelector(".container").addEventListener("touchend", function () {
if (endX < 100) {
document.querySelector(".ul").style.transition = 'transform 0.5s ease'; document.querySelector(".ul").style.transform = 'translateY(0px)'
return false
} document.querySelector(".p").innerHTML = "加载中..."; document.querySelector(".p").classList.add("tip")
setTimeout(() => {
while (document.querySelector(".ul").hasChildNodes()) { document.querySelector(".ul").removeChild(document.querySelector(".ul").firstChild); }
init()
document.querySelector(".ul").style.transition = 'transform 0.5s ease'; document.querySelector(".ul").style.transform = 'translateY(0px)'
document.querySelector(".p").innerHTML = ""; document.querySelector(".p").classList.remove("tip")
}, 2000)
})
</script>
</html>