面试随记

139 阅读7分钟

toFixed

Vue的diff算法

vue 的diff 算法 以及虚拟DOM

JavaScript 生成名为 Virtual Dom 的 DOM 副本,这样做的原因是用 JavaScript 直接操作 DOM 的计算成本很高。虽然用 JavaScript 执行更新很快,但是找到所需的 DOM 节点并用 JavaScript 更新它们的成本却很高。所以我们批量处理调用,并一次性更改 DOM。

虚拟 DOM 是轻量级的 JavaScript 对象,由渲染函数创建。它包含三个参数:元素,具有数据、prop、attr 等的对象,以及一个数组。数组是我们传递子级的地方,子级也具有所有这些参数,然后它们也可以具有子级,依此类推,直到我们构建完整的元素树为止。

如果需要更新列表项,我们可以借助前面提到的响应性在 JavaScript 中进行。我们将更改应用至 JavaScript 副本、虚拟 DOM 中,然后在它们和实际 DOM 之间执行 diff。只有这样,我们才能对已更改的内容进行更新。虚拟 DOM 允许我们对 UI 进行高效的更新!

patch 的函数,比较新旧节点,一边比较一边给 真实的DOM 打补丁

具体怎么实现的不太清楚

v-if v-show 的区别

v-if 更倾向于逻辑判断,v-show 更倾向于动画渲染 相同点:v-if v-show 都可以动态控制dom元素的显示与隐藏

不同点:v-if 是将dom元素添加或者删除 v-show 则是display: none 将dom元素隐藏

  • v-show 有着更高的首次渲染开销,而v-if则有着更小的首次渲染开销

  • v-show切换的开销较小,v-if 的切换开销更大

v-show适合频繁切换

sessionStorage localStorage cookie

共同点: 都是保存在浏览器端,且是同源的

区别:

  • cookie数据始终在同源的http请求中携带,即这个cookie会在浏览器和服务器之间传递,而sessionStorage 和localStorage不会自动把数据发送给服务器,仅在本地保存。cookie数据还有路径的概念
  • 存储大小限制不同cookie数据不能超过4K,同时因为每次http请求都会携带cookie、所以cookie只适合保存很小的数据,如会话标识或者是身份信息。sessionStorage和localStorage虽然也有存储大小的限制,但比cookie大得多,可以达到5M或更大
  • 数据有效期不同,localStorage:始终有效,窗口或浏览器关闭也一直保存,因此用作持久数据;cookie:只在设置的cookie过期时间之前有效,即使窗口关闭或浏览器关闭。而sessionStorage 则是窗口关闭就没有了
  • 作用域不同,sessionStorage不在不同的浏览器窗口中共享,即使是同一个页面;localstorage在所有同源窗口中都是共享的;cookie也是在所有同源窗口中都是共享的

状态码

blog.csdn.net/banana96053…

伪类和伪元素的概念

伪类: 伪元素::

  • 伪类存在的意义通过选择器找到那些不存在DOM树中的信息以及不能被常规CSS选择器获取到的信息

: link :visited :hover :focus

伪元素 在DOM树中创建一些本不存在DOM中的一些抽象元素 伪元素可以使得开发者获取到不存在于源文档内的内容 ::before ::after

区别在于有没有在DOM树中创建新的元素

异步

promise

promise 是ES6的语法,为了解决地狱回调的问题

promise 是一个状态机。初始是 pending 状态,可以通过函数 resolve 和 reject,将状态转变为 resolved 或者 rejected状态,状态一旦改变就不能再次变化。

async await

Generator 函数的语法糖。有更好的语义、更好的适用性、返回值是 Promise。

async/await的优势在于处理 then 的调用链,能够更清晰准确的写出代码,并且也能优雅地解决回调地狱问题。当然也存在一些缺点,因为 await 将异步代码改造成了同步代码,如果多个异步代码没有依赖性却使用了 await 会导致性能上的降低。

async/await语法糖就是使用Generator函数+自动执行器来运作的

Promise 的状态流转 目的是为了将多个promise异步请求并行操作 all raced allSettled any

  • all 当所有的结果发挥成功才会返回参数执行状态
  • allSettled 无论结果是否成功,都会返回每个参数的执行状态
  • any 参数中只要有一个结果返回成功,就返回该成功的执行结果
  • raced 最先返回执行成功的参数的执行结果

Proxy

在目标对象的外层建立一层拦截,对于该对象的操作必须先经过这层拦截

let p = new Proxy(target,handler)

handler 函数对应的get set 操作

 var logHandler = {
   get: function(target, key) {
     console.log(`${key} 被读取`);
     return target[key];
   },
   set: function(target, key, value) {
     console.log(`${key} 被设置为 ${value}`);
     target[key] = value;
   }
 }

作用:

  1. 拦截或监视外部对对象的访问
  2. 在复杂操作前对操作进行校验或者预处理

Ajax

是一种异步通信的方式,通过由js脚本向服务器发起http通信。更新相应部分而不更新整个页面的方法

创建xhr对象 -> 配置ajax请求地址 -> 发送请求 -> 监听请求

//1:创建Ajax对象
var xhr = window.XMLHttpRequest?new XMLHttpRequest():new ActiveXObject('Microsoft.XMLHTTP');// 兼容IE6及以下版本
//2:配置 Ajax请求地址
xhr.open('get','index.xml',true);
//3:发送请求
xhr.send(null); // 严谨写法
//4:监听请求,接受响应
xhr.onreadysatechange=function(){
     if(xhr.readySate==4&&xhr.status==200 || xhr.status==304 )
          console.log(xhr.responsetXML)
}

接口

axios 配置的 get post delete

data computed watch 的适用范围以及区别

blog.csdn.net/q761830908/… data 数据相关的属性一般放在data中,包括数据的初始化 computed 则是计算属性,则更多应用于计算值的场景 其中还包括数据缓存,只有computed依赖的属性发生变化,computed才会重新计算 watch 则更多使用的是观察的作用,类似于数据的监听回调。当数据发生变化时才进行相关操作

vue2 与 vue3 的版本区别

v-model 这个我最熟悉 可以提一下 value input modelValue update:modelValue

重构响应式系统,使用Proxy替换Object.defineProperty,使用Proxy优势: 可直接监听数组类型的数据变化 监听的目标为对象本身,不需要像Object.defineProperty一样遍历每个属性,有一定的性能提升 可拦截apply、ownKeys、has等13种方法,而Object.defineProperty不行 直接实现对象属性的新增/删除 新增Composition API,更好的逻辑复用和代码组织 destroyed 生命周期选项被重命名为 unmounted 直接监听对象而非属性 直接监听数组的变化 拦截方式较多(有13种方式)

Vue2 中如何实现对于数组元素的响应式绑定

  1. 首先式重写了数组,以及数组的多个方法。当使用者执行这些方法时,我们就可以监听到数据的变化,然后做些跟新操作,下面我们在observer中加上关于对数组的判断。

Object.defineProperty实现数据响应式时我们必须要遍历所有的数据,还需要重写数组的方法,性能消耗也比较大

变量提升

变量提升从字面上理解就是将声明的代码移动到了顶部。 具体的解释就是: 在生成执行环境时,会有两个阶段。第一个阶段是创建的阶段,JS 解释器会找出需要提升的变量和函数,并且给他们提前在内存中开辟好空间,函数的话会将整个函数存入内存中,变量只声明并且赋值为 undefined,所以在第二个阶段,也就是代码执行阶段,我们可以直接提前使用

let const 并不是没有进行变量提升,而是let const 会进入一个暂时性死区,声明前使用会报错 ReferenceError

iframe 此外可能还涉及有异步请求的东西

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

js遵循同源策略,即同协议,同域名,同端口号,否则都算跨域。

同协议,同域名,同端口号

防抖节流

说明白其具体的定义,以及相关应用场景 防抖:事件触发N秒后再执行回调,如果事件在这N秒内又被触发,则重新计时。可以用在一些点击事件上。避免用户因为多次点击向后端发送多次请求 节流:函数节流 是指规定一个单位时间,在这个单位时间内,只能有一次触发事件的回调函数执行,如果在同一个单位时间内某事件被触发多次,只有一次能生效。

loadsh 是一个JS工具库

文字省略号

关键词: overflow: hidden white-space: nowrap text-overflow: ellipsis

div {
	width: 200px;
	overflow: hidden;
	white-space: nowrap;
	text-overflow: ellipsis;
}

vue-router 的模式 hash模式 和 历史模式

hash 模式 history模式 其中有一特征就是url里面有一个#

使用URL的hash来模拟一个完整的URL 会触发hasChange这个事件,通过监听hash值的变化来实现更新页面部分内容的操作 对于hash模式会创建hashHistory对象 HashHistory.push()将新的路由添加到浏览器访问的历史的栈顶,和HasHistory.replace()替换到当前栈顶的路由

history模式使用的是: 主要使用HTML5的pushState()和replaceState()这两个api来实现的,pushState()可以改变url地址且不会发送请求,replaceState()可以读取历史记录栈,还可以对浏览器记录进行修改

二叉树的前中后序遍历,至少记一下递归的方法

  • 前 根左右
  • 中 左根右
  • 后 左右根

px em rem 的区别

  • px是像素值
  • em 是子元素相对于父元素来讲的
  • rem 则是相当于根元素来讲的

菜鸟教程

有哪些方式可以将元素隐藏

opacity:0:本质上是将元素的透明度将为0,就看起来隐藏了,但是依然占据空间且可以交互 visibility:hidden: 与上一个方法类似的效果,占据空间,但是不可以交互了 overflow:hidden: 这个只隐藏元素溢出的部分,但是占据空间且不可交互 display:none: 这个是彻底隐藏了元素,元素从文档流中消失,既不占据空间也不交互,也不影响布局 z-index:-9999: 原理是将层级放到底部,这样就被覆盖了,看起来隐藏了 transform: scale(0,0): 平面变换,将元素缩放为0,但是依然占据空间,但不可交互

实习所遇到的项目难点以及业务的解决方式

实习项目遇到的难点

登陆界面

  • 登录:当用户在Form 表单向服务端提交数据。由服务端验证数据的正确性,同时服务端会返回一个Token,拿到token账号之后(将这个token的信息存储在cookie中)。这样就保证了刷新页面后能够记住用户的状态。同时前端会根据token去拉取一个user_info 的接口来获取用户的详细信息(用户权限,用户名称)
  • 权限验证: 通过token 获取用户对应的role,动态的根据role算出其对应有权限的路由,通过router.addRoutes 动态挂载这些路由

上述所有的数据和操作都是通过vuex全局管理控制的。(补充说明:刷新页面后vuex的内容也会丢失,所以需要重复上述的那些操作)

登录成功后,服务端返回token(token就是能唯一标识用户身份的key) ,之后我们将token。存储在本地cookie中,这样下次打开页面或者刷新页面的时候能记住用户的登录状态,不用再去登录页面重新登录了。

PS: 为了保证安全性,我司现在后台所有token有效期(Expires/Max-Age)都是Session,就是当浏览器关闭了就丢失了。重新打开游览器都需要重新登录验证,后端也会在每周固定一个时间点重新刷新token,让后台用户全部重新登录一次,确保后台用户不会因为电脑遗失或者其它原因被人随意使用账号

如同前面所说的,为什么我只存储了token 而没有存储用户信息(权限,用户名,用户头像等)。对于后台管理系统来说,主要有以下考量: 页面会先从 cookie 中查看是否存有 token,没有,就走一遍上一部分的流程重新登录,如果有token,就会把这个 token 返给后端去拉取user_info,保证用户信息是最新的。 当然如果是做了单点登录得功能的话,用户信息存储在本地也是可以的。当你一台电脑登录时,另一台会被提下线,所以总会重新登录获取最新的内容。

单点登录

权限篇

vue-admin 权限控制的主体思路,前端会有一份路由表,它表示了每一个路由可访问的权限。当用户登录之后,通过 token 获取用户的 role ,动态根据用户的 role 算出其对应有权限的路由,再通过router.addRoutes动态挂载路由。但这些控制都只是页面级的,说白了前端再怎么做权限控制都不是绝对安全的,后端的权限验证是逃不掉的。 我司现在就是前端来控制页面级的权限,不同权限的用户显示不同的侧边栏和限制其所能进入的页面(也做了少许按钮级别的权限控制),后端则会验证每一个涉及请求的操作,验证其是否有该操作的权限,每一个后台的请求不管是 get 还是 post 都会让前端在请求 header里面携带用户的 token,后端会根据该 token 来验证用户是否有权限执行该操作。若没有权限则抛出一个对应的状态码,前端检测到该状态码,做出相对应的操作。

项目不断的迭代你会异常痛苦,前端新开发一个页面还要让后端配一下路由和权限,让我们想了曾经前后端不分离,被后端支配的那段恐怖时间了。 其次,就拿我司的业务来说,虽然后端的确也是有权限验证的,但它的验证其实是针对业务来划分的,比如超级编辑可以发布文章,而实习编辑只能编辑文章不能发布,但对于前端来说不管是超级编辑还是实习编辑都是有权限进入文章编辑页面的。所以前端和后端权限的划分是不太一致。

具体实现
  1. 创建vue实例的时候将vue-router挂载,但这个时候vue-router挂载一些登录或者不用权限的公用的页面。
  2. 当用户登录后,获取用role,将role和路由表每个页面的需要的权限作比较,生成最终用户可访问的路由表。
  3. 调用router.addRoutes(store.getters.addRouters)添加用户可访问的路由。
  4. 使用vuex管理路由表,根据vuex中可访问的路由渲染侧边栏组件。

后面的代码部分还需要再看一下

响应头响应体 请求头请求体 状态码

cookie 的状态码是什么鬼

Github actions

从字面意思理解就是 actions 就是一系列操作的集合

持续集成又很多的操作:抓取代码 运行测试 登录远程服务器 发布到第三方服务

又因为操作的类似性,可以共享。所以Github就提供了这种服务,允许开发者将每个操作写成独立的脚本文件。提供给他人使用

同理你可以引入别人的action 进行组合变成一个actions组合。这就是Github Actions 特别的地方

配置文件主要有几个部分构成 workflow 工作流程 持续集成一次运行的过程,就是一个workflow job 任务 可以包含多个 step step 步骤 可以包含多个action action 动作

workflow 文件

采用的是YAML 文件 .yml 文件

阮一峰

多人协作的项目版本不一致的问题

实习遇到的项目难点 说一下项目 状态码 cookie sessionStorage localStorage 接口相关知识 登录界面大概的思路是什么 虚拟DOM树以及Diff算法 无重复字符的最长子串 promise 看代码写结果 nextTick() vue2 与 vue3 的区别

bug 是在前端还是后端

那就是看后端返回的数据和约定的数据格式是否一致,在有限的业务实践中主要涉及到的是后台代码。逻辑比较简单。就是查看

nextTick()

nextTick() Node.js 中的一部分内容 process.nextTask()比Promise.then的优先级高

HTTP 请求头与响应头

blog.csdn.net/weixin_4442…

线程与进程

Vue组件通信 父子组件,兄弟组件

这他妈之前答得跟坨屎一样

我TM不知道DaoCloud之前是怎么面过的 靠

props emit/on vuex attrs provide/inject

组件A 通过props向向子组件传递 B to A 通过在 B 组件中 $emit, A 组件中 v-on 的方式实现。

vuex 3.x

vuex 是vue的状态管理器,Vuex应用的核心就是store仓库。store本质上为一个容器。存储了应用中的大部分状态

  1. Vuex的状态存储时响应性的
  2. 不能直接改变store中的状态。mutation 改变 显式地提交(commit) mutation
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment (state) {
      state.count++
    }
  }
})

store.commit('increment')

console.log(store.state.count) // -> 1

Getter

核心概念

State

单一状态树 唯一数据源,用一个对象包含全部地应用层级状态

Getter

类似于计算属性,getter地返回值会根据依赖被缓存起来,只有依赖地值发生变化才会被重新计算

mutation

更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler) 通过提交commit 来改变状态值,接收state作为第一个参数

vuex的核心包括:state(存放状态)、mutations(同步的更改状态)、actions(发送异步请求拿到数据)、getters(根据之前的状态发布新的状态)、modules(模块划分)

应用场景: 购物车的数据共享

Vuex 与localStorage

vuex 是 vue 的状态管理器,存储的数据是响应式的。但是并不会保存起来,刷新之后就回到了初始状态,具体做法应该在vuex里数据改变的时候把数据拷贝一份保存到localStorage里面,刷新之后,如果localStorage里有保存的数据,取出来再替换store里的state。

let defaultCity = "上海"
try {   // 用户关闭了本地存储功能,此时在外层加个try...catch
  if (!defaultCity){
    defaultCity = JSON.parse(window.localStorage.getItem('defaultCity'))
  }
}catch(e){}
export default new Vuex.Store({
  state: {
    city: defaultCity
  },
  mutations: {
    changeCity(state, city) {
      state.city = city
      try {
      window.localStorage.setItem('defaultCity', JSON.stringify(state.city));
      // 数据改变的时候把数据拷贝一份保存到localStorage里面
      } catch (e) {}
    }
  }
})

这里需要注意的是:由于vuex里,我们保存的状态,都是数组,而localStorage只支持字符串,所以需要用JSON转换:

Action

Action 类似于mutation

  • 更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)

Vuex实现了一个单向数据流,在全局拥有一个Statecuncun

$attrs

$listeners 则包含了父作用域中的v-on事件监听器.

多级组件嵌套需要传递数据时

attrs 包含了父作用域中不被prop所识别的特性绑定。 当一个组件没有声明任何prop时,这里会包含所有父作用域的绑定(class,style除外)

provide/inject

允许一个祖先向其所有的子孙后代注入一个依赖,无论组件层次有多深 父组件provide 子组件 inject

provide 和 inject 绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的属性还是可响应的

ref

ref: ref 如果用在普通的元素上,指向的就是普通元素。 如果用在组件上用到的就是指向组件实例

总结:

父向子传递数据是通过 props,子向父是通过 events(emit);通过父链/子链也可以通信(emit);通过父链 / 子链也可以通信(parent / children);ref也可以访问组件实例;provide/injectAPIchildren);ref 也可以访问组件实例;provide / inject API;attrs/$listeners

兄弟通信: eventBus;Vuex ; 子传父,父传子 eventBus 在Vue2 中的表现就是创建新的Vue实例 跨级通信: Bus;Vuex;provide / inject API、attrs/attrs/listeners