前端常见面试问题总结(未完待续)

283 阅读15分钟

前端常见面试问题

活跃用户双token

为什么要使用双token?

  • 总所周知,token是为了防止用户信息传来传去导致被劫持,但是假如token没有过期时间或者过期很长,那么显然token被劫持还是不安全的,token就失去了意义
    

双token的定义

  • 1.accessToken:真正用来获取数据的权限,它过期时间非常短,即使被拦截了,解密也需要时间,而且token本身也很快过期,因此这样的设计更加安全
  • 2.refreshToken:用来获取accessToken,不会频繁被用于请求

什么时候需要用户重新登陆?

主要有三种情况:

  • 1.用户长时间没有操作,就会被自动踢出,跳转到登陆页面,超时时间可以自定义设置
  • 2.token失效,通常是双token都失效后,会要求重新登陆获取新的双token
  • 3.当检测到有风险的时候,可以要求重新登陆,获取token

如何实现refreshToken获取token无感刷新?

有以下三种方法

  • 1.通过后端返回过期时间,前端根据当前时间与这个过期时间做判断,去调用刷新token接口

    缺点: 需要后端额外提供一个Token过期时间的字段;使用了本地时间判断,若本地时间被篡改,特别是本地时间比服务器时间慢时,拦截会失败

  • 2.定时任务,定时使用refreshToken获取accessToken

    这个方法浪费资源,消耗性能,不建议使用

  • 3.在响应拦截器中监听token过期情况,后端判断token返回过期后,调用刷新token接口

    项目中经常使用的方案, axios有做响应拦截的api

// 响应拦截器 (响应成功:剥离无效数据,响应失败:刷新token)
instance.interceptors.response.use(res => {
  //解构服务器返回的数据
  return res.data.data
}, 
  async err => {
  try {
    // 目的:刷新token
    if (err.response && err.response.status === 401) {
      // 未登录  跳转登录页面  阻止程序运行
      const { user } = store.state
      // 如果没有token没登录  如果没有refresh_token无法刷新token
      if (!user.token || !user.refresh_token) {
        router.push('/login')
        return Promise.reject(err)
      }
      // 刷新token,发请求,没有配置的axios,自己配置refresh_token
      const res = await axios({
        url: 'http://ttapi.research.itcast.cn/app/v1_0/authorizations',
        method: 'put',
        headers: {
          Authorization: `Bearer ${user.refresh_token}`
        }
      })
      // token获取  res.data.data.token
      // 更新 vuex 和 本地 token
      store.commit('setUser', {
        token: res.data.data.token,
        refresh_token: user.refresh_token
      })
      // 继续发送刚才错误的请求
      // instance({之前错误的请求配置})
      // err错误对象 包含(response 响应对象 |config 请求配置)
      return instance(err.config)
    }
  } catch{ 
    // 刷新token失败
    // 情况 vuex 和 localStorage
    store.commit('cleanState')
    // 强制跳转到登陆页
    router.push('/login')
  }
  return Promise.reject(err)
})

SPA单页面

单页面应用的简介

  • 单页Web应用(single-page application 简称为 SPA)是一种特殊的Web应用,它将所有的活动局限于一个Web页面中,仅在该Web页面初始化时加载相应的HTML、JavaScript和CSS,一旦页面加载完成了,SPA不会因为用户的操作而进行页面的重新加载或跳转,取而代之的是利用JavaScript动态的变换HTML的内容,从而实现UI与用户的交互,由于避免了页面的重新加载,SPA 可以提供较为流畅的用户体验,得益于ajax,我们可以实现无跳转刷新,又多亏了浏览器的histroy机制,我们用hash的变化从而可以实现推动界面变化,从而模拟元素客户端的单页面切换效果。

spa的优缺点

  • 优点
1.无刷新界面,给用户体验原生的应用感觉
2.节约原生(android和ios)app开发成本
3.提高发布效率,无需每次安装更新包
4.符合web2.0的趋势
  • 缺点
1.效果和性能确实和原生的有较大差距

2.各个浏览器的版本兼容性不一样

3.业务随着代码量增加而增加,不利于首屏优化

4.某些平台对hash有偏见,有些甚至不支持pushstate

5.不利于搜索引擎抓取

获取数据时加token和不加token的区别

  • token的定义: Token是服务端生成的一串字符串,以作客户端进行请求的一个令牌,当第一次登录后,服务器生成一个Token便将此Token返回给客户端,以后客户端只需带上这个Token前来请求数据即可,无需再次带上用户名和密码。
  • 不加token虽然也可以请求数据,但它比较麻烦和局限性,当我们需要请求一些需要验证身份的数据的时候会导致服务器不知道响应什么时候给我们
  • Token的目的是为了减轻服务器的压力,减少频繁的查询数据库,使服务器更加健壮。

路由跳转的原理

  • 定义: 前端路由,就是一个前端不同页面的状态管理器,可以不向后台发送请求而直接通过前端技术实现多个页面的效果。

  • 单页路由跳转有两种模式:

    • hash(哈希)模式: 使用 URL 的 hash 来模拟一个完整的 URL,于是当 URL 改变时,页面不会重新加载。
    • history模式: 这种模式充分利用 history.pushState API 来完成 URL 跳转而无须重新加载页面。
  • 使用history模式时需要进行的配置

    const router = new VueRouter({
      mode: 'history',
      routes: [...]
    })
    
  • history模式存在的问题

    • 这个模式特别依赖于后端的配合, 如果后台没有正确的配置,当用户在浏览器直接访问 http://oursite.com/user/id 就会返回 404
    • 在history下,你可以自由的修改path,当刷新时,如果服务器中没有相应的响应或者资源,会出现刷新404问题
  • 解决方法

    • 要在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面

怎么理解关键渲染路径

  • 关键渲染路径是浏览器将 HTML,CSS 和 JavaScript 转换为屏幕上的像素所经历的步骤序列。优化关键渲染路径可提高渲染性能。关键渲染路径包含了 文档对象模型(DOM),CSS 对象模型(CSSDOM) 渲染树和布局。

  • 浏览器的渲染机制:

    • 处理 HTML 并构建 DOM 树。
    • 处理 CSS 构建 CSSOM
    • 将 DOM 与 CSSOM 合并成一个渲染树
    • 根据渲染树来布局,计算每个节点的位置。
    • 调用 GPU 绘制,合成图层,显示在屏幕上。
  • 总结:

    • 一般情况下,html解析会被JS阻塞,JS执行完后,HTML才继续解析构建DOM,期间css的解析构建又会阻塞JS执行。所以最后DOM和CSSOM构建完成后,才形成渲染树。

域名和ip地址之间的转换

  • 域名与ip地址通过DNS进行转换。DNS(域名系统)是互联网的一项服务,它作为将域名和IP地址相互映射的一个分布式数据库,能够使人更方便地访问互联网

css优先级问题

不加scoped的style样式

  • 无论父子组件,不加scoped的style标签中的选择器不会增加[data-v-x]属性选择器
  • 对于同一个选择器,子组件不含scoped的style标签中的样式优先级高于父组件

加scoped的style样式

  • 无论父子组件,不加scoped的style标签中的选择器都会增加[data-v-x]属性选择器
  • 对于同一个选择器,父子组件含scoped的style标签中的样式互不影响

同一个组件,加或不加scoped

  • 在同一个组件中,对于同一个选择器,含scoped的style中设置的样式优先级高于不含scoped的style中设置的样式

总结:

  • 含scoped的样式:父子组件互不影响,原因是增加了属性选择器后缀,彼此不能匹配。
  • 不含scoped的样式:父子组件相互影响,但子组件优先级高于父组件。
  • 同一个组件:不含scoped和含scoped的样式,含scoped的优先级更高,原因也是含scoped的选择器增加了属性选择器后缀。

BFC是什么,怎么实现BFC

BFC(块级格式上下文)的定义:

  • 决定了元素如何对其内容进⾏定位,以及与其他元素的关系和相互作⽤。简⾔之,就是⼀个特殊的块,内

    部的元素和外部的元素不会相互影响。BFC内的盒⼦会在垂直⽅向上⼀个接⼀个地放置,垂直⽅向上也会发⽣外边距重叠

BFC的应用场景:

  • ⾃适应布局(BFC不与float box重叠)、清除浮动(计算BFC的⾼度时,内部的浮动元素也被计算在

    内)、防⽌外边距重叠

如何触发BFC:

  • float属性(不为none)、overflow属性(不为visible)、position属性(absolute,

    fixed)、display属性(inline-block,table-cell,table-caption,flex,inline-flex)

谈谈对vue中的$nextTick的理解

  • 定义: 在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM
  • 应用场景:点击按钮显示原本以 v-show = false 隐藏起来的输⼊框,并获取焦点
  • 原理: vue⽤异步队列的⽅式来控制DOM更新和nextTick回调先后执行。简单来说,nextTick是做了promise加上setTimeout的封装,利用事件换行机制,来确保当nextTick出现时,都是在我们所有操作DOM更新之后。

DNS协议是什么?说说DNS完整的查询过程

DNS的定义:

  • 域名系统,是互联网一项服务,是进行域名和与之对应的IP地址进行转换的服务器简单来说就是一个翻译官,负责将域名翻译成ip地址
  • IP 地址:一长串能够唯一地标记网络上的计算机的数字
  • 域名:是由一串用点分隔的名字组成的 Internet 上某一台计算机或计算机组的名称,用于在数据传输时对计算机的定位标识

域名缓存:

定义:

  • 在域名服务器解析的时候,使用缓存保存域名和IP地址的映射

计算机中DNS的记录分成了两种模式:

  • 浏览器缓存:浏览器在获取网站域名的实际 IP 地址后会对其进行缓存,减少网络请求的损耗
  • 操作系统缓存:操作系统的缓存其实是用户自己配置的 hosts 文件

查询过程:

  • 首先搜索浏览器的 DNS 缓存,缓存中维护一张域名与 IP 地址的对应表
  • 若没有命中,则继续搜索操作系统的 DNS 缓存
  • 若仍然没有命中,则操作系统将域名发送至本地域名服务器,本地域名服务器采用递归查询自己的 DNS 缓存,查找成功则返回结果
  • 若本地域名服务器的 DNS 缓存没有命中,则本地域名服务器向上级域名服务器进行迭代查询
    • 首先本地域名服务器向根域名服务器发起请求,根域名服务器返回顶级域名服务器的地址给本地服务器
    • 本地域名服务器拿到这个顶级域名服务器的地址后,就向其发起请求,获取权限域名服务器的地址
    • 本地域名服务器根据权限域名服务器的地址向其发起请求,最终得到该域名对应的 IP 地址
  • 本地域名服务器将得到的 IP 地址返回给操作系统,同时自己将 IP 地址缓存起来
  • 操作系统将 IP 地址返回给浏览器,同时自己也将 IP 地址缓存起
  • 至此,浏览器就得到了域名对应的 IP 地址,并将 IP 地址缓存起

前端如何进行SEO优化

SEO定义

  • 搜索引擎优化,是一种通过分析搜索引擎得排名规律,了解各种搜索引擎进行搜索,怎样抓取互联网页面、怎样确定特定关键词得搜索结果排名的技术。

title

  • title,就是浏览器上显示的那些内容,不仅用户能看到,也能被搜索引擎检索到(搜索引擎在抓取网页时,最先读取的就是网页标题,所以title是否正确设置极其重要),title一般不超过80个字符,而且词语间要用英文"-"隔开,因为计算机只对英文的敏感性较高,对汉语的敏感性不高。

  • 用法:

  • 首页title写法,一般是"网站名称-主关键字词或一句含有主关键字的描述"。一般网站名称放在后面,因为搜索引擎给予标题最前面的词比后面高。比如:"冰箱_变频冰箱-海尔官网”

  • 文章页title写法,一般有3种:“文章标题-网站名称”、“内容标题-栏目名称-网站名称”、“内容标题-网站名称”。其中,“内容标题-栏目名称-网站名称”的写法最规范,它能给用户很好的提示,让用户知道他在访问哪篇文章,并且是在哪个网站的哪个栏目下。

description(内容摘要)

  • description是对于一个网页的简要内容概况。description一般不超过150个字符,描述内容要和页面内容相关。

  • 用法:

  • <meta name="description" content="你网页的简述">
    
  • 首页description写法,一般是讲首页的标题、关键字和一些特殊目的的内容融合到里面,写成简单的介绍。

  • 文章页keywords写法,建议提取文章中的关键字。

keywords(关键词)

  • keywords,主要作用是告诉搜索引擎本页内容是围绕哪些词展开的。因此keywords的每个词都要能在内容中找到相应匹配,才有利于排名。keywords一般不超过3个,每个关键字不宜过长,而且词语间要用英文","隔开。尽量将重要的关键字靠前放,因为靠后的关键字排名较差,除非你占有很高的权重。

  • 用法:

  • <meta name="keywords" content="关键字1,关键字2,关键字3,关键字4">
    
  • 首页keywords写法,一般是"网站名称,主要栏目名,主要关键字"

<meta name="keywords" content="IT培训,IT培训机构,编程培训">

说说你对事件循环的理解

  • 首先js是一门单线程语言,同一时间内只能做一件事,这样很容易造成阻塞,实现单线程非阻塞的方法就是事件循环
  • 在javascript中,所有任务分为:
    • 同步任务:立即执行的任务,同步任务一般会之间进入到主线程中执行
    • 异步任务:异步执行的任务,比如ajax请求,setTimeout定时函数等

  • 总结:同步任务会进入主线程即主执行栈,异步任务则会进入任务队列,主线程中的任务执行完毕后,会去任务队列中读取对应的任务,推入主执行栈中执行,上述过程不断重复则为事件循环

宏任务和微任务

  • 异步任务又分为宏任务和微任务

  • 常见的微任务:promise.then、MutaionObserve、process.nextTick(Node.js)

  • 常见的宏任务:script、setTimeout、setInterval、

  • 总结:
  • 先执行script标签中的第一个宏任务,如果遇到微任务就把它放入微任务的事件队列中
  • 当前宏任务执行完毕后,先依次清空微任务事件队列里面所有的微任务,再执行下一个宏任务(即先微再宏)

HTTP和HTTPS的区别

  • HTTP 是超文本传输协议,信息是明文传输 ; HTTPS则是具有安全性的 SSL加密传输协议
  • HTTP 和 HTTPS 使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443
  • HTTP 的连接很简单,是无状态的 ; HTTPS 协议是由 SSL+HTTP 协议构建的可进行加密传输、身份认证的网络协议,比HTTP协议安全

什么是重定项

  • 重定向就是重新定义跳转的url,比如登陆后重定向回之前的页面

什么是协商缓存?

  • 协商缓存是服务器的一种缓存策略,服务器提供一个标记,用来判断客户端资源和服务器是否一样,如果一致返回304,否则返回200和新资源
  • 简单来说就是通过服务器来判断当前页面缓存是否可用

什么是强缓存?

  • 强缓存是根据返回头中的Expires 或者 Cache-Control两个字段来控制的,都是表示资源的缓存有效时间
  • 简单来说就是服务器通过设置http中的header的Expires和Cache-control字段告诉浏览器缓存的有效期

前端暂时性死区

  • 定义:在代码块内,使用let命令声明变量之前,该变量是不可用的。这在语法上,称为暂时性死区
  • 暂时性死区的本质就是,只要一进入当前作用域,所要所有的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量

判断数据类型的四种方法

  • typeof

    • 使用于判断(除null)之外的基础类型
    • 判断引用类型,除了function全返回object类型
  • instanceof

    • 只能用来判断 变量的原型链上是否又构造函数的prototype属性,不一定能获取对象的具体类型
    • instanceof不适用判断原始类型的值,只能判断对象是否又从属关系
  • constructor

    • 每一个实例对象都可通过constructor来访问它的构造函数,其实也是根据原型链的原理来的
  • object.prototype.tostring.call

    • Object.prototype.toString方法返回对象的类型字符串,因此可用来判断一个值的类型。
    • 因为实例对象有可能会自定义toString方法,会覆盖Object.prototype.toString,所以在使用时,最好加上call
    • 所有的数据类型都可以使用此方法进行检测,且非常精准

虚拟列表

  • 定义:是按需显示的一种实现,即只对可见区域进行渲染,对非可见区域中的数据不渲染或者部分渲染,从而达到极高的渲染功能
  • 实现思路
    • 在首屏加载的时候,只加载可视区域内需要的列表项,当滚动发送时,动态通过计算获得可视区域内的列表项,并将非可视区域内存在的列表项删除
      • 计算当前可视区域起始数据索引(startIndex)
      • 计算当前可视区域结束数据索引(endIndex)
      • 计算当前可视区域数据,并渲染到页面中
      • 计算startIndex对应的数据在整个列表中的偏移位置startOffset并设置到列表上
<template>
  <div ref="list" class="infinite-list-container" @scroll="scrollEvent($event)">
    <div class="infinite-list-phantom" :style="{ height: listHeight + 'px' }"></div>
    <div class="infinite-list" :style="{ transform: getTransform }">
      <div ref="items"
        class="infinite-list-item"
        v-for="item in visibleData"
        :key="item.id"
        :style="{ height: itemSize + 'px',lineHeight: itemSize + 'px' }"
      >{{ item.value }}</div>
    </div>
  </div>
</template>
export default {
  name:'VirtualList',
  props: {
    //所有列表数据
    listData:{
      type:Array,
      default:()=>[]
    },
    //每项高度
    itemSize: {
      type: Number,
      default:200
    }
  },
  computed:{
    //列表总高度
    listHeight(){
      return this.listData.length * this.itemSize;
    },
    //可显示的列表项数
    visibleCount(){
      return Math.ceil(this.screenHeight / this.itemSize)
    },
    //偏移量对应的style
    getTransform(){
      return `translate3d(0,${this.startOffset}px,0)`;
    },
    //获取真实显示列表数据
    visibleData(){
      return this.listData.slice(this.start, Math.min(this.end,this.listData.length));
    }
  },
  mounted() {
    this.screenHeight = this.$el.clientHeight;
    this.start = 0;
    this.end = this.start + this.visibleCount;
  },
  data() {
    return {
      //可视区域高度
      screenHeight:0,
      //偏移量
      startOffset:0,
      //起始索引
      start:0,
      //结束索引
      end:null,
    };
  },
  methods: {
    scrollEvent() {
      //当前滚动位置
      let scrollTop = this.$refs.list.scrollTop;
      //此时的开始索引
      this.start = Math.floor(scrollTop / this.itemSize);
      //此时的结束索引
      this.end = this.start + this.visibleCount;
      //此时的偏移量
      this.startOffset = scrollTop - (scrollTop % this.itemSize);
    }
  }
};

vue2双向绑定的原理

  • Vue是结合发布者-订阅者模式,通过 Object.defineProperty()为各个属性定义get、set特性方法,在数据发生改变时给订阅者发布消息,触发相应的监听回调,更新dom,另⼀⽅⾯通过编译模板⽂件,为控件的v-model绑定input事件,从⽽⻚⾯输⼊能实时更新相关data属性

vue2响应式原理

  • Vue 的响应式原理是核⼼是通过 ES5 的 Object.defindeProperty 进⾏数据劫持,然后利⽤ get 和 set

    ⽅法进⾏获取和设置,data 中声明的属性都被添加到了get和set中,当读取 data 中的数据时⾃动调⽤ get ⽅

    法,当修改 data 中的数据时,⾃动调⽤ set ⽅法,检测到数据的变化,会通知观察者 Wacher,观察者 Wacher

    ⾃动触发重新render 当前组件(⼦组件不会重新渲染),⽣成新的虚拟 DOM 树,Vue 框架会遍历并对⽐新虚拟

    DOM 树和旧虚拟 DOM 树中每个节点的差别,并记录下来,最后,加载操作,将所有记录的不同点,局部修改到真实

    DOM 树上

对SSR的了解

  • 1、SSR也就是服务端渲染,也就是将Vue在客户端把标签渲染成HTML的⼯作放在服务端完成,然后再把html直接返回

    给客户端。

    1. SSR有着更好的SEO、并且⾸屏加载速度更快等优点。不过它也有⼀些缺点,⽐如我们的开发条件会受到限制,服务

    器端渲染只⽀持beforeCreate和created两个钩⼦,当我们需要⼀些外部扩展库时需要特殊处理,服务端渲染应⽤

    程序也需要处于Node.js的运⾏环境。还有就是服务器的压⼒⽐较⼤。

    1. 在vue中一般使用nuxt.js实现SSR服务端渲染

说说对TS的理解

  • TypeScript 简称:TS,可以理解为 是增强版的JavaScript,是 JavaScript 的超集

  • JS 的类型系统存在“先天缺陷”弱类型,JS 代码中绝大部分错误都是类型错误

  • 从编程语言的动静来区分,TypeScript 属于静态类型的编程语言JavaScript 属于动态类型的编程语言

    • 静态类型:编译期做类型检查
    • 动态类型:执行期做类型检查
  • 代码编译和代码执行的顺序:1 编译 2 执行

  • 对于 JS 来说:需要等到代码真正去执行的时候才能发现错误(晚)

  • 对于 TS 来说:在代码编译的时候(代码执行前)就可以发现错误(早)

JSnew做了什么事情

  • 创建一个新对象

  • 将构造函数this指向这个新对象

  • 将新对象的 proto 指向构造函数的 prototype

  • 返回新创建的对象

如何解决JS中setTimeout和setInterval实现倒计时出现的偏差问题

  • 造成的原因:由于setTimeout和setInterval的作用是隔一段时间将回调事件加入到事件队列中,因此事件并不是立即执行,它会等到当前执行栈为空的时候再取出事件执行,因此事件会有等待执行的事件

  • 解决方法:

    • 前端通过向服务器发送请求获取最新的时间差,以此来计算时间差
    • 前端根据偏差时间来自动调整间隔时间的方式来实现的。这一种方式首先是以 setTimeout 递归的方式来实现倒计时,然后通过一个变量来记录已经倒计时的秒数。每一次函数调用的时候,首先将变量加一,然后根据这个变量和每次的间隔时间,我们就可以计算出此时无偏差时应该显示的时间。然后将当前的真实时间与这个时间相减,这样我们就可以得到时间的偏差大小,因此我们在设置下一个定时器的间隔大小的时候,我们就从间隔时间中减去这个偏差大小,以此来实现由于程序执行所造成的时间误差的纠正

服务端渲染(ssr)和客户端渲染(csr)的区别

  • 服务端渲染:DOM树在服务端生成,然后返回给前端
  • 客户端渲染:前端去后端取数据生成DOM树
  • 区别:
    • 本质区别:服务端渲染是在服务端生成DOM,客户端渲染是在客户端生成DOM树
    • 响应速度:服务端渲染会加快页面的响应速度,客户端渲染页面响应速度慢
    • SEO优化:服务端渲染是多个页面,更有利于SEO优化,客户端渲染不利于SEO优化
    • 开发效率:服务端渲染不利于前后端分离,开发效率低,客户端渲染是采用的前后端分离的方式开发,开发效率高