面试官你好 我叫XXX 今天应聘公司的前端开发工作,从事前端开发大概五年左右时间,有一年uniApp经验,四年vue开发经验,最近的开发项目是给wj开发内网军人值班页面,主要涉及人员值班打卡,领导排班,文件签批,文件互传等功能。最近受到公司欠薪影响 开始重新找工作 比较擅长vue全家桶开发。我的自我介绍完了 谢谢。
判断字符串是否符合XXX-XXX-XXXX的格式,其中X为number类型
function matchesPattern(str) {
let reg = /^\d{3}-\d{3}-\d{4}$/
return reg.test(str)
}
const bl = matchesPattern('333-222-1111')
console.log(bl);
css隐藏元素的方法
.element {
visibility: hidden;
// 隐藏元素 但保留占用空间
}
.element {
// 文档流中删除该元素
display: none;
}
.element {
color: rgba(0, 0, 0, 0);
background-color: rgba(0, 0, 0, 0);
border-color: rgba(0, 0, 0, 0);
}
.element {
// 绝对定位 移出到视口外面
position: absolute;
left: -9999px;
}
.element {
// 使用z-index 权限盖掉
position: relative; z-index: 1;
}
元素垂直居中的几种方法
.div{
position: relative;
width:100px;
height:50px;
top:50%;
left:50%
transform:translateY(-50%);
background:#095;
}
.div{
position: absolute;
width:100px;
height:50px;
top:0; right:0;
bottom:0;
left:0;
margin:auto;
background:#f60;
}
.div{
display:flex;
align-items:center;
justify-content:center;
width:200px;
height:150px;
border:1px solid #000;
}
.div {
display: grid;
place-items: center;
/* 同时实现水平和垂直居中 */
height: 100vh; /*
可以根据需要设置高度 */
}
js数据类型BigInt和Synbol区别
BigInt用于处理大整数,而Symbol用于解决全局变量命名冲突问题,提供了一种独一无二的标识符
- bigInt:它可以表示任意精度格式的整数。使用BigInt可以安全地存储和操作大整数,即使这个数已经超出了Number能够表示的安全整数范围
- Symbol:Symbol代表创建后独一无二且不可变的数据类型。每个通过Symbol()生成的值都是唯一的,Symbol值能作为对象属性的标识符。Symbol的设计主要是为了解决可能出现的全局变量冲突的问题,
== 和 === & 对于两个对象比较的区别
- ==用于一般比较(值相等),在比较的时候可以转换数据类型
- ===用于严格比较(值相等+类型相等),严格比较,只要类型不匹配就返回flase。
- 把两个对象进行比较,得到的结果都是不相等的,对于引用类型来说,默认是比较两个对象引用的地址,每个对象的引用有自己唯一的地址,所以,是不相等。
箭头函数和普通函数的区别
this的指向不同:
- 在普通函数中,this的值是在函数被调用时确定的,它指向调用该函数的对象。而在箭头函数中,this的值是在函数定义时确定的,它指向定义箭头函数的上下文。这意味着箭头函数没有自己的this,它继承父级作用域的this
- 在箭头函数中,this指向的是定义箭头函数的上下文,而不是调用箭头函数的对象
不适用于构造函数
- 箭头函数不能用作构造函数,不能通过new关键字来实例化对象。而普通函数可以用作构造函数,可以通过new关键字来创建对象实例
无arguments对象
- 在普通函数中,可以使用arguments对象来访问所有传入的参数,它是一个类数组对象。而箭头函数没有自己的arguments对象,它继承父级作用域中的arguments对象
总结:箭头函数和普通函数在语法上的区别主要体现在简洁性和this指向上。箭头函数的语法更加简洁,但不能用作构造函数,并且没有自己的this和arguments对象。普通函数的语法相对复杂一些,但可以用作构造函数,并且有自己的this和arguments对象。
闭包介绍和使用场景
闭包是什么?
闭包让你可以在一个内层函数中访问到其外层函数的作用域
function init() {
var name = "Mozilla"; // name 是一个被 init 创建的局部变量
function displayName() { // displayName() 是内部函数,一个闭包
alert(name); // 使用了父函数中声明的变量
}
displayName();
}
init();
// `displayName()` 没有自己的局部变量。然而,由于闭包的特性,它可以访问到外部函数的变量
闭包的使用
- 创建私有变量
- 延长变量的生命周期
- 通过使用闭包来定义公共函数,并令其可以访问私有函数和变量
- 函数柯里化
浏览器强缓存和协商缓存
强缓存
- 可以理解为强制缓存的意思,即浏览器在访问某个资源时会判断是否使用本地缓存里已经存在的资源文件,使用本地缓存的话则不会发送请求到服务器,从而达到减轻服务器访问压力的作用,且由于直接从本地缓存读取资源文件,大大提高了加载速度。
- 浏览器第一次请求远程服务器的某个资源时,如果服务器希望浏览器得到该资源后一段时间内不要再发送请求过来,直接从浏览器里的缓存里取,则服务器可以通过在响应头里设置
Cache-Control: max-age=31536000,max-age代表缓存时间,单位为秒,这里的数据换算过来就是一年,意味着在一年内浏览器不会再向服务器发送请求。 - 使用缓存的话,状态码200后面会标明情况。浏览器缓存资源的地方有两个:磁盘缓存(disk cache)和内存缓存(memory cache)
- 一般来说,浏览器会将较大的资源缓存到disk cache,而较小的资源则被缓存到memory cache里。内存缓存与磁盘缓存相比,访问速度要更快一些!
协商缓存
在强缓存里,是否使用缓存是由浏览器来确定的,而协商缓存则是由服务器来告诉浏览器是否使用缓存资源,也就是浏览器每一次都要发送请求到服务器询问是否使用缓存,协商缓存的具体流程如下:
- 浏览器初次请求资源,服务器返回资源,同时生成一个
Etag值携带在响应头里返回给浏览器,当浏览器再次请求资源时会在请求头里携带If-None-Match,值是之前服务器返回的Etag的值,服务器收到之后拿该值与资源文件最新的Etag值做对比。 - 如果没有变化则返回304,告诉浏览器继续使用缓存(不返回资源文件)。
- 如果发生变化,则返回200和最新的资源文件给浏览器使用。
- 强缓存优先级大于协商缓存,即两者同时存在时,如果强缓存开启且在有效期内,则不会走协商缓存。
总结: 强缓存就是浏览器本地根据服务器设置的过期时间来判断是否使用缓存,未过期则从本地缓存里拿资源,已过期则重新请求服务器获取最新资源。
协商缓存则是浏览器本地每次都向服务器发起请求,由服务器来告诉浏览器是从缓存里拿资源还是返回最新资源给浏览器使用。
前端跨域
- 关闭浏览器同源策略在命令行窗口输入chrome --disable-web-security
- 在项目开发中常常会引入外链的图片、样式文件、插件等资源,但这些请求并没有导致跨域错误,因为这些请求都属于
http请求并不是会引发跨域 问题的Xhr,请求简单来说script标签没有跨域限制的特性,把script脚本的src改成我需要跨域请求的url,就能实现跨域获取资源,且不触发浏览器的同源策略,这就是JSONP的原理 - 服务器代理ProxyServer
Vue2和Vue3的主要区别
双向绑定原理
- Vue2使用ES5的Object.defineProperty()对数据进行劫持,结合发布订阅模式实现双向数据绑定,只能监听某个属性,不能对全对象进行监听
- Vue3使用ES6的ProxyAPI对数据进行代理,实现双向数据绑定,可以监听整个对象,包括数组的变化
多根节点支持
- Vue2不支持多根节点,即组件的模板中必须有一个根标签。
- Vue3支持多根节点,即组件的模板中可以有多个根节点。
Composition API
- Vue2使用选项类型API(Options API),将数据、方法等属性分别放在不同的选项中。
- Vue3使用合成型API(Composition API),允许使用方法来分割代码,使代码更加简洁
登录流程
- 1、首次登录,前端调用后端登录接口,发送用户名密码
- 2、后端收到请求,验证用户名密码。成功,返回给前端token和一个用户信息的值
- (token:后台发的唯一标识,用来验证用户是否登录)
- 3、前端拿到token,将token存储到sessionStorage中,并跳转路由页面
- 4、前端每次跳转路由,判断cookies中是否有token。没有跳转登录页,有跳转对对应路由首页
- 5、每次调用后端接口,都要在请求头中加入token【在项目utils/service.js中添加全局拦截器】
- 6、后端判断请求头中有无token,有则拿到token并验证token。
- 成功返回数据,失败如:token过期,则前端会返回无效的token,跳转到登录页面并且清除本地用户的信息
token过期:
- 在请求响应拦截器中拦截,判断token 返回过期后,调用刷新token接口。
- 为了防止多次刷新token,可以通过一个变量isRefreshing 去控制是否在刷新token的状态
- 同时发起两个或者两个以上的请求时,需要借助Promise,刷新token
vite webpack 区别
构建速度
Vite的构建速度比Webpack更快。Vite使用原生ES模块进行开发,不需要像Webpack那样在编译时将所有代码转换为JS打包,因此构建速度显著提升
热更新机制
Webpack的热更新需要整个模块链重新打包和替换,对于大型项目可能会有延迟。而Vite的热更新则只会针对改动的模块进行更新,提高了更新速度。
打包效率
Webpack在打包时会把所有模块打包成一个bundle,初次加载速度较慢。Vite则利用了浏览器对ES Module的原生支持,只打包和缓存实际改动的模块,提高了打包效率
watch和computed区别
功能上的区别
- computed 是 计算属性:适用于需要根据已有的属性计算和处理,得出一个新值的场景;
- watch 是 监听一个值的变化:适用于需要对数据变化做出响应或者执行异步操作的场景。
是否会被缓存
- computed 属性的值会被缓存,只有在依赖数据发生变化时才会重新计算:如果所依赖的属性没有发生变化,那么调用当前的函数的时候会从缓存中读取;
- 而 watch 属性不会缓存计算结果: 在每次监听的值发生变化的时候都会执行回调。
是否调用 return
computed 中的函数必须要用 return 返回;watch 中的函数不是必须要用 return。
第一次加载时区别
- computed 默认第一次加载的时候就开始监听;可以依赖于其他的响应式数据,当这些数据发生变化时,computed 属性会自动更新。
- watch 默认第一次加载不做监听: 如果需要第一次加载做监听,添加 immediate 属性,设置为 true(immediate:true)
重绘和回流
- 重绘(repaint):当元素的某些属性发生变化,这些属性又只影响元素的外观和风格,而不改变元素的布局、大小比如颜色、背景。此时触发的浏览器行为称作重绘。
- 回流(reflow):回流也叫重排,当元素的布局、大小规模和显示方式发生改变时,触发的浏览器行为叫回流。而且,每个页面都会在第一次加载时触发回流。
- 回流必将引起重绘,而重绘不一定伴随回流。 回流对性能的影响要大于重绘。
vue数据双向绑定原理
初始化阶段
- Observer 劫持 data 对象的属性,给每个属性添加 getter 和 setter 方法。
- Compile 遍历 DOM,解析指令,如 v-model 和 v-bind。
- 为每个解析到的指令创建对应的 Watcher,并添加到对应属性的 Dep 中。
- 将数据渲染到页面。
视图到数据
- 当用户与页面上的表单元素交互时,事件监听器(如 v-model 的输入监听器)触发并修改 data 对象的值。
- 由于表单元素绑定了 data 对象的某个属性,因此修改 data 的值会触发该属性的 setter 方法(Observer 执行 set 和 get)。
数据到视图:
- 当后端传来新数据或者数据发生变化时,Observer 会执行 setter 方法,通知 Dep 更新视图。
- Dep 根据通知找到对应的一组 Watcher,并调用它们的 update 方法来更新视图
v-for v-if 不建议一起使用
- 在 Vue2 中,
v-for和v-if在一起使用的话,v-for的优先级高于v-if,这会导致v-for遍历多少次就会执行多少次v-if的判断,这对性能来说不够友好,所以不建议v-for和v-if放在一起使用。 - 在 Vue3 中,
v-for和v-if在一起使用的话,v-if的优先级高于v-for,v-if的条件判断表达式会提升到v-for前执行,如果v-if中的条件表达式依赖了v-for中定义的局部变量,会导致v-if中的条件表达式无法访问到v-for中定义的局部变量,从而导致运行时报错,因此也不建议v-for和v-if放在一起使用。
节流防抖
// 如果一个事件被无限制触发、会加重浏览器和服务器的负担
// 那么此时我们就需要用到节流(throttle)和防抖(debounce) 减少事件的触发时间和次数同时又不影响实际效果。
// 首先想到的是用第三方库来实现lodash
// 可以手写节流防抖
// 手写 防抖(debounce)
// 防抖为每次点击时 延迟时间都会重新执行 直到最后一次点击
function My_debounce(fn,delay){
let time;
return function(){
// 每次进来都会先结束掉setTimeout函数
// 所以如果频繁触发 只会执行最后一次调用
if(time){
clearTimeout(time)
}
time = setTimeout(()=>{
// 改变fn this指向调用者
fn.call(this)
},delay)
}
}
// 手写 节流(throttle)
// 节流为在间隔时间段内只能执行一次
function My_throttle(fn,delay){
let time;
return function(){
// 由于判断time为空才执行 所以重复点击无法进入if判断
if(!time){
time = setTimeout(()=>{
fn.call(this)
// 函数执行完毕才给time为空
// 所以必须等setTimeout完毕才能下次点击执行
time = null
},dalay)
}
}
}