文章目录
- 1. vue的diff算法和数据劫持,进一讲你的理解
- 2. 项目中的分片上传,如何实现的?(简历中的项目)
- 3. 如果用户上传两个一样的文件,一个文件上传上半段,一个文件上传下半段,如何拼起来优化,而不是直接返回上传失败?
- 4. 瀑布流如何实现的长列表?
- 5. 瀑布流布局中,你是如何判断谁是底部元素的?
- 6. 如果一次只请求10条数据,前端只展示了5条数据,如何判断底部元素的交叉状态?
- 响应式怎么做的?
- 7. 如何通过bootstrape实现的?
- 8. 怎么监听移动端设备?
- 9. bootsreape底层怎么实现的?
- 10. 讲一讲浏览器缓存?
- 11. 详细说一说Cookie?使用场景?
- 12. sessionStorage中你所说的不能在所有同源窗口中共享是什么意思?多个tab栏不可以共享?浏览器同源策略是什么?会话级别的储存方式的会话是什么意思?
- 13. 讲一讲get和post的区别?
- 14. 因为讲到了资源浪费,如果post携带简单的参数转为get通过url传会不会造成资源的浪费?到底什么情况下用post和get?
- 15. 缓存,get请求会缓存,请问有哪些方式清除缓存?除了开发者工具外还有哪些清除缓存(为用户角度)?
- 16. script标签中的defer和async的区别?
- 17.看我熟悉ES6,问我Symbol用过吗,使用场景?
- 18. BigInt用过吗,Number和BigInt可以相加吗?
- 19. 熟悉typeScript还是了解?如果一个interface 里面有abc需要定义一个新的并且拿到里面的ab 如何拿到?
- 20. v-for和v-if的优先级?在外层嵌套template,那在template里面v-if还是v-for?
- 21. 常见的css布局单位有哪些?你到底用过哪些?
- 22. css文本省略···如何实现的?
- 23. 手写promise.all方法,为什么要用push?会有什么问题?为什么要用forEach?循环的方法有哪些? forEach和for循环的区别是什么? forEach会中断吗?
1. vue的diff算法和数据劫持,进一讲你的理解
vue的diff算法
-
diff算法基本原理:
- 比较新旧节点差异:diff算法首先对比新旧虚拟Dom树的节点,找出他们的差异
- 生成差异操作(patch): 然后,diff算法会生成一组操作(也成为patch),这些操作可以将旧的虚拟DOM树转变成新的虚拟DOM树。
- 应用差异操作更新DOM树:最后,这些操作被应用到实际的DOM树上,从而更新视图。
-
diff算法的实现方法:
-
使用递归遍历的方式:发现节点不存在,则标记为删除;发现新节点,标记为添加;发现节点存在但内容改变,标记为更新。
-
vue采用了多种策略来优化diff算法,采用了原地复用、按key值比较、采用双端队列等方式,提高了性能,减少不必要的DOM操作。
-
-
diff算法局限性和注意事项:
-
复制的嵌套结构
-
对于组件内部的更新,使用vue 的watch 监听可以更高效处理
-
注意应当正确使用key值
-
数据劫持
- vue采用了数据劫持的方式来实现响应式数据绑定。当你将普通的Javascript对象传递给Vue示例作为data选项时,Vue就会遍历这个对象的所有属性,并使用Object.defineProperty方法将他们转成getter和setter。
- 这样一来,当你修改了这些属性的值,vue就能监测到变化,并自动更新相关视图,这就是vue的响应式数据绑定的基本原理。
2. 项目中的分片上传,如何实现的?(简历中的项目)
-
分片上传:将一个大文件分成多个小块,然后将这些小块逐个上传至服务器的一种上传方式。
-
一般文件大于5M为大文件。
3. 如果用户上传两个一样的文件,一个文件上传上半段,一个文件上传下半段,如何拼起来优化,而不是直接返回上传失败?
- 创建文件标识
- 服务器端临时存储文件,而不是立刻进行拼接
- 当所有文件上传后再进行拼接
- 校验文件完整性
- 返回上传成功信息
4. 瀑布流如何实现的长列表?
5. 瀑布流布局中,你是如何判断谁是底部元素的?
在瀑布流布局中,判断哪些元素是底部元素的方法通常涉及到计算每列的高度,然后比较它们的高度来确定。一般来说,当新元素要添加到最短列时,它就成为底部元素。
以下是一种简单的方法来判断底部元素:
-
计算列的高度:首先,需要计算每列的高度,这可以通过遍历每个列中的元素,并累加它们的高度来实现。在实际编码中,可以创建一个数组来存储每列的高度。
-
找到最短列:通过比较列的高度,找到最短的列。这意味着该列是当前最适合添加新元素的列,因为它的高度最小。
-
将新元素添加到最短列:将新元素添加到最短列中,并更新该列的高度。
-
判断是否为底部元素:如果新元素被添加到了最短列,那么它就是底部元素。
// 获取所有列及其高度 column是所有子元素的类名 var columns = document.querySelectorAll('.column'); var columnHeights = []; columns.forEach(function(column) { columnHeights.push(column.offsetHeight); }); // 找到最短列的索引 var shortestColumnIndex = columnHeights.indexOf(Math.min(...columnHeights)); // 新元素将要添加到的列 var bottomColumn = columns[shortestColumnIndex]; // 判断是否为底部元素 if (bottomColumn === event.target.parentElement) { console.log("This is a bottom element."); }
6. 如果一次只请求10条数据,前端只展示了5条数据,如何判断底部元素的交叉状态?
响应式怎么做的?
7. 如何通过bootstrape实现的?
8. 怎么监听移动端设备?
9. bootsreape底层怎么实现的?
10. 讲一讲浏览器缓存?
浏览器缓存:是指在访问网页时,将一些数据保存在本地的过程,提高网页加载速度和用户体验。
-
HTTP缓存:HTTP缓存是通过HTTP协议来控制的,主要分为强缓存和协商缓存。
浅谈 强制缓存/协商缓存 怎么用? - 掘金- 强缓存:浏览器在请求资源时,首先判断本地缓存是否存在有效的缓存副本,如果存在且未过期,则直接使用缓存副本,不发送请求到服务器。常见的强缓存控制头:Cache-Control和Expires.
- 协商缓存:当强缓存失效时,浏览器会向服务器发送请求,服务器根据请求头中携带的信息来判断资源是否有更新。如果资源未发生变化,服务器返回304状态码,通知浏览器使用本地缓存,如果资源已经更新,服务器返回新的资源内容。常见的协商缓存控制头有
Last-Modified
和ETag
。
-
LocalStorage和SessionStorage:本地存储,将数据保存至浏览器
- LocalStorage:保存的数据在浏览器关闭后仍然存在,除非手动清除或者使用js清除。
// 注意:移除特定的localStorage数据项
//方式一、
localStorage.removeItem('yourKey');
//方式二:直接f12键,点到选择"Application"选项卡,展开"LocalStorage"一栏,右键选择要清除的项目,选择"Clear"即可清除
- SessionStorage:保存的数据仅在当前会话中有效,浏览器关闭数据清除。
- Cookie:存储少量的用户信息,与特定的域名相关联。可以设置Cookie的过期时间,控制信息的有效期。
- Service Worker缓存:Service Worker 是运行在浏览器背后的脚本,可以拦截网络请求并且缓存资源,实现离线访问,提高浏览速度。
11. 详细说一说Cookie?使用场景?
cookie和session:cookie和session都是用来跟踪浏览器用户身份的会话方式。
区别:
1.保持状态:cookie保存在本地浏览器端(俗称:客户端),session保存在服务器端
2.使用方式:
cookie机制
-
cookie机制:如果不在浏览器中设置过期时间,cookie被保存在内存中,生命周期随浏览器的关闭而结束,这种cookie简称会话cookie。如果在浏览器中设置了cookie的过期时间,cookie被保存在硬盘中,关闭浏览器后,cookie数据仍然存在,直到过期时间结束才消失。
-
Cookie不能进行跨域
-
cookie是服务器发给客户端的特殊信息,cookie是以文本的方式保存在客户端,每次请求时都带上它
cookie机制 应用场景:
- 保存用户登录状态。判断用户是否登陆过网站,以便下次登录时能够实现自动登录(或者记住密码)。如果我们删除cookie,则每次登录必须从新填写登录的相关信息。
- 保存上次登录的时间等信息。
- 跟踪用户行为。通过cookie记住用户操作行为,当下次再打开该页面时,自动默认该行为。比如说:不同用户所在地区的天气情况只要设置一次,下次打开就是之前设置的地方的天气。或者说网站的换肤功能等。
- 浏览计数
12. sessionStorage中你所说的不能在所有同源窗口中共享是什么意思?多个tab栏不可以共享?浏览器同源策略是什么?会话级别的储存方式的会话是什么意思?
sessionStorage是一种会话级别的储存方式,它与当前会话(即当前浏览器标签页或窗口)相关联。当用户在浏览器中打开一个页面时,会话开始;当用户关闭该页面时,会话结束。在同一个会话中,不同页面之间可以共享sessionStorage中的数据,但是不能在不同会话(不同浏览器标签页或窗口)之间共享数据。
换句话说,sessionStorage中的数据只能在同一个浏览器标签页或窗口中共享,而不能跨越不同的浏览器标签页或窗口。如果用户在同一浏览器中打开了多个标签页,每个标签页都拥有自己独立的sessionStorage,它们之间不能共享数据。
这与浏览器的同源策略相关。同源策略是一种安全机制,限制了来自不同源(域、协议、端口)的页面之间的交互。在同源策略下,一个浏览器标签页只能访问和操作属于同一源的数据,而不能访问其他源的数据。因此,sessionStorage中的数据也受到同源策略的限制,只能在同一源的页面之间共享。
综上所述,会话级别的储存方式中的“会话”指的是用户在浏览器中打开的一个会话期间,即从用户打开浏览器开始到用户关闭浏览器为止。在同一个会话期间,sessionStorage中的数据可以在同一浏览器标签页或窗口中共享,但不能跨越不同的会话(不同浏览器标签页或窗口)。
13. 讲一讲get和post的区别?
- get和post都是网络请求的方法,post安全性更高
- get请求
- 用来请求数据的,参数附在url中,显示在地址栏以明文的显示参数信息,容易被中间人窃取。
- post 请求
- 一般用来提交数据,参数放在请求体中,不会暴露在地址栏,在请求抓包时,相对于get请求更难获取到明文数据,提升了数据的安全性。
14. 因为讲到了资源浪费,如果post携带简单的参数转为get通过url传会不会造成资源的浪费?到底什么情况下用post和get?
使用 POST 方法将简单参数转换为 GET 方法通过 URL 传递可能会导致一些资源浪费和安全风险,这取决于你的具体情况和需求。
首先,让我们来看一下 POST 和 GET 方法的区别:
- GET 方法:通常用于请求获取服务器上的某个资源,参数会附加在 URL 的末尾,以查询字符串的形式传递。GET 请求可以被缓存,可以被书签保存,但是传输的数据量有限制(由浏览器和服务器设定,通常不超过 2048 个字符),因此适合传递少量的非敏感信息,比如搜索关键词等。
- POST 方法:通常用于向服务器提交数据,参数不会附加在 URL 中,而是作为请求的实体内容发送给服务器。POST 请求通常用于提交表单数据、上传文件等场景,传输的数据量没有限制,而且更安全,因为参数不会明文显示在 URL 中。
现在来看一下何时使用 POST 和 GET 方法:
- 使用 GET 方法:当你需要请求获取服务器上的资源,并且传递的参数是少量的、非敏感的信息时,可以使用 GET 方法。比如进行搜索、获取页面等场景。
- 使用 POST 方法:当你需要向服务器提交大量数据、敏感信息(比如密码)、或者需要进行状态更新、文件上传等操作时,应该使用 POST 方法。POST 方法比 GET 方法更安全,因为参数不会暴露在 URL 中,而且传输的数据量没有限制。
总的来说,要根据具体的场景和需求来选择使用 GET 还是 POST 方法。如果只是简单的传递少量的非敏感信息,可以考虑使用 GET 方法;如果涉及到大量数据、敏感信息或者需要进行状态更新等操作,应该使用 POST 方法。
15. 缓存,get请求会缓存,请问有哪些方式清除缓存?除了开发者工具外还有哪些清除缓存(为用户角度)?
- 使用手动清除浏览器缓存
- 使用一些自动清楚缓存的扩展程序
- 使用无痕模式浏览页面不会留下缓存
16. script标签中的defer和async的区别?
defer和async都是控制脚本加载和执行顺序的两个属性,它们可以在
区别主要是在脚本加载和执行时机上。
defer
属性用于确保脚本在 DOM 构建完成之前执行,而 async
属性则用于异步加载并立即执行脚本,不会阻塞页面的加载和渲染。
17.看我熟悉ES6,问我Symbol用过吗,使用场景?
Symbol 数据类型,用于表示独一无二的值。每个通过 Symbol() 函数创建的 Symbol 值都是唯一的
Symbol 的主要使用场景包括:
-
作为对象属性的键:可以使用 Symbol 作为对象的属性键,以确保属性名的唯一性,避免因为属性名冲突而导致意外覆盖或混淆的情况。例如:
const mySymbol = Symbol('description'); const obj = { [mySymbol]: 'This is a symbol property' };
-
定义常量:可以使用 Symbol 来定义常量,保证常量的唯一性。例如,可以使用 Symbol 定义一些操作的状态或类型
const STATUS_PENDING = Symbol('pending'); const STATUS_SUCCESS = Symbol('success'); const STATUS_ERROR = Symbol('error');
-
使用内置 Symbol 值:
- ES6 提供了一些内置的 Symbol 值,如
Symbol.iterator
、Symbol.toStringTag
等,用于定义对象的行为或特性。比如,可以使用Symbol.iterator
实现自定义的迭代器。
- ES6 提供了一些内置的 Symbol 值,如
-
私有属性和方法:
- 使用 Symbol 可以模拟私有属性和方法,因为外部无法直接访问到使用 Symbol 定义的属性或方法,从而实现一定程度的封装。
总的来说,Symbol 主要用于解决属性名冲突、定义常量、实现对象行为等场景,通过其唯一性和不可变性的特点,能够提高代码的可维护性和安全性。
18. BigInt用过吗,Number和BigInt可以相加吗?
BigInt 是 JavaScript 中的一种数据类型,用于表示任意精度的整数。相比于普通的 Number 类型,BigInt 可以表示更大范围的整数值,不受最大安全整数的限制。
//BigInt 可以通过在整数数字后面添加 n 后缀来创建
const bigIntNum = 1234567890123456789012345678901234567890n;
JavaScript 中的 Number 和 BigInt 类型是不同的类型,它们不能直接相加。会类型错误。
如果要相加,就需要类型一致。
- 使用
BigInt()
函数将 Number 转换为 BigInt。 - 使用
Number()
函数将 BigInt 转换为 Number。// 注意:转换 BigInt 到 Number 可能会丢失精度,因为 Number 类型有其表示范围的限制。
// 将 Number 转换为 BigInt 进行相加:
const num = 123;
const bigIntNum = 456n;
const result = BigInt(num) + bigIntNum;
console.log(result); // 输出:579n
19. 熟悉typeScript还是了解?如果一个interface 里面有abc需要定义一个新的并且拿到里面的ab 如何拿到?
20. v-for和v-if的优先级?在外层嵌套template,那在template里面v-if还是v-for?
21. 常见的css布局单位有哪些?你到底用过哪些?
- 像素(Pixel,px):最常见的单位之一,表示屏幕上的一个像素。它是一个绝对单位,不受屏幕或浏览器设置的影响。
- 百分比(Percentage,%):相对于父元素的百分比。例如,
width: 50%;
表示元素宽度为父元素宽度的一半。 - 视窗宽度和高度(Viewport Width,vw):相对于视窗(浏览器窗口)宽度的百分比。例如,
width: 50vw;
表示元素宽度为视窗宽度的一半。 - 视窗高度(Viewport Height,vh):相对于视窗(浏览器窗口)高度的百分比。例如,
height: 80vh;
表示元素高度为视窗高度的 80%。 - 根元素字体大小(Root em,rem):相对于根元素的字体大小(
html
元素)。这种单位的值不受父元素影响,方便响应式设计。
22. css文本省略···如何实现的?
通过 text-overflow
属性来实现文本的省略。结合 overflow
和 white-space
属性,可以实现不同场景下的文本省略效果。
.ellipsis {
white-space: nowrap; /* 禁止文本换行 */
overflow: hidden; /* 溢出隐藏 */
text-overflow: ellipsis; /* 文本溢出时显示省略号 */
}
<div class="ellipsis">这是一个很长的文本,如果超出容器宽度就会显示省略号。</div>
23. 手写promise.all方法,为什么要用push?会有什么问题?为什么要用forEach?循环的方法有哪些? forEach和for循环的区别是什么? forEach会中断吗?
//这个有问题(看下面的!!)
function custonPromiseall(promises)
{
return new Promise((resolve,reject)=>
{
const results=[]
let completCount=0
promises.forEach((promise,index)=>
{
promise.then(result=>
{
results[index] =result
completCount++
if(completCount===promises.length)
resolve(results)
})
.catch(error=>{
reject(error)
})
})
})
}
//手写promise.all可能写的不够全,有几个问题(针对上面的写的那个)
1. 传入的数据不一定是数组,任何具有迭代器属性的值都可以传给all方法,因此获取长度最好不要用length,循环也不要用forEach,用for of,并且定义一个变量在外面,每循环一次就加一用来计算传入值的长度
2. 传入值内部不一定都是promise,因此在调用.then方法前,最好给包一层Promise.resolve()
3. 没有考虑传入空数据的问题,如果为空的则应该直接resolve一个空数组
//这个是大佬在评论区进行更改后的代码(***************)
Promise.myAll = function(pros) {
return new Promise((resolve, reject) => {
let proCount = 0
let completedCount = 0
for (const pro of pros) {
let index = proCount
proCount++
Promise.resolve(pro).then(res => {
completedCount++
results[index] = res
if (completedCount === proCount) {
resolve(results)
}
}, (err) => {
reject(err)
})
}
if (proCount === 0) {
resolve([])
}
})
}