接上篇
vue
1. 你了解diff算法吗?它的时间复杂度是怎样的?(字节跳动)
Diff算法的作用是用来计算出 Virtual DOM (虚拟DOM)中被改变的部分,然后针对该部分进行原生DOM操作,而不用渲染真实DOM(会引起整个DOM的重绘和重排)即不用重新渲染整个页面。
作用和过程: 我们先根据真实DOM生成一颗virtual DOM,当virtual DOM某个节点的数据改变后会生成一个新的Vnode,然后Vnode和oldVnode作对比,发现有不一样的地方就直接修改在真实的DOM上,然后使oldVnode的值为Vnode。
diff算法三大策略:
Tree Diff 对树每一层进行遍历,找出不同
Component Diff 进行组件级的比较,看它们是不是同一类型的组件,如果不是,就替换整个组件下的所有子节点
Element Diff 进行结构差异的比较,真实DOM渲染
三种策略的执行顺序也是顺序依次执行。
diff的比较方式: 只会同层比较,不会跨层比较;当节点处于同一层级时,Diff提供三种DOM操作:删除、移动、插入。
时间复杂度为O(n)
2. vuex的执行流程是怎样的?(字节跳动)
vuex是状态集中管理的仓库 广泛用于各组件传值和共享数据
它有五个核心概念:分别是 State、 Getter、Mutation 、Action、 Module
当组件进行数据修改的时候,我们需要调用dispatch来触发actions里面的方法。actions里面的每个方法中都会有一个commit方法,当方法执行的时候会通过commit来触发mutations里面的方法进行数据的修改。 mutations里面的每个函数都会有一个state参数,这样就可以在mutations里面进行state的数据修改 ,当数据修改完毕后,会传导给页面,页面的数据也会发生改变。
3. vue router hash和history模式有什么区别?(字节跳动)
hash模式下的url里含有#,history模式则没有,hash模式里的#号实际上只出现在路由中,请求时不予发送,hash时只有#前的东西会发送给后端,因此没有路由全覆盖也不会404,而且hash模式下只允许修改#后的值 ;而history模式时, 前端的 URL 必须和实际向后端发起请求的 URL 完全一致,因此极其容易发生404。
3.1 Vue Router的原理(阿里)
- 首先是数据驱动,所以我们可以通过一个route对象来表述当前路由状态,比如:
current = {
path: '/', // 路径
query: {}, // query
params: {}, // params
name: '', // 路由名
fullPath: '/', // 完整路径
route: {} // 记录当前路由属性
}
current.route
内存放当前路由的配置信息,所以我们只需要监听current.route
的变化来动态rende
r页面便可。
- 我们实现一个简单的订阅-发布器,所以我们需要对
current.route
做数据劫持。一旦current.route
更新,我们可以及时的更新当前页面 - 接下来是hash和history模式的实现, 当页面变化时,我们只需要监听
hashchange
和popstate
事件,做路由转换transitionTo
- 这样我们一方面通过
this.current.route = targetRoute
达到了对之前劫持数据的更新,来达到视图更新。另一方面我们又通过任务队列的调度,实现了基本的钩子函数beforeEach、beforeLeave、beforeEnter、afterEnter
。
4. vue2和vue3有哪些区别?(字节跳动) - 提出的新API setup()函数
- 了对于Typescript的支持
- 替换Object.defineProperty为 Proxy 的支持。(因为传统的原型链拦截的方法,无法检测对象及数组的一些更新操作,但使用Proxy又带来了浏览器兼容问题)
5. router-link与router-view有什么区别?(字节跳动)
- router-link相当于是类似a标签的导航链接,点击
<router-link :to="...">
时,内部调用router.push(...)
方法 - router-view 是命名视图,用来渲染通过路由映射过来的组件,当路径更改时, 中的内容也会发生更改,可以嵌套使用
- 二者配合使用,可以实现几个跳转链接跳到对应的子页面,程序运行的时候,会将
<template>
标签里面的内容都注入到App.vue页面中的router-view标签中,从而实现无刷新的路由跳转
6. vue双向绑定的原理是什么?(滴滴,腾讯,阿里)
采用 数据劫持 结合 发布者-订阅者模式 的方式,通过 Object.defineProperty() 来劫持各个属性的 setter、getter,在数据变动时发布消息给订阅者,触发相应的监听回调。
Object.defineProperty( )可以来控制一个对象属性的一些特有操作,比如读写权、是否可以枚举,重要的是它的两个描述属性get(依赖收集)和set(派发更新),对其get和set进行重写操作,顾名思义,get就是在读取属性这个值触发的函数,set就是在设置属性这个值触发的函数。
具体流程:
- 实现一个监听器Observer,用来劫持并监听所有属性,如果有变动的,就通知订阅者。
- 实现一个订阅者Watcher,可以收到属性的变化通知并执行相应的函数,从而更新视图。
- 实现一个解析器Compile,可以扫描和解析每个节点的相关指令,并根据初始化模板数据以及初始化相应的订阅器。
- 下图中dep 是消息订阅器
6.1 手写一个简单的订阅者发布者模型
class EventEmitter {
/**请补充你的代码***/
constructor() {
this.listOn = {}; // 可触发的事件放在一个对象中
}
on (dosomething, fun) { // 监听事件,该事件可以被触发
if (!this.listOn[dosomething]) {
this.listOn[dosomething] = [];
}
this.listOn[dosomething].push(fun);
// console.log(this.listOn);
}
fire() { // 触发指定的事件
let dosomething = [].shift.call(arguments);
let emit = false;
for(let i in this.listOn) {
if (i == dosomething) {
emit = true;
this.listOn[i].forEach(fun => {
fun.apply(this, arguments);
});
}
}
if (!emit) {
console.log('没有该事件! ')
}
}
}
const event = new EventEmitter()
const drank = (person) => {
console.log(person + '喝水')
}
event.on('drank', drank)
event.on('eat', (person) => {
console.log(person + '吃东西')
})
event.fire('drank', '我') // 我喝水
event.fire('drank', '我') // 我喝水
event.fire('eat', '其它人') // 其它人吃东西
event.fire('buy', '其它人') //其它人买东西
7. vue中组件与组件之间的通信方式(快手)
props
/$emit
$emit
/$on
vuex
$attrs
/$listeners
provide
/inject
$parent
/$children
与ref
8. vue的生命周期顺序是怎样的?Vue 的父组件和子组件生命周期钩子执行顺序是什么?(滴滴)
生命周期顺序:
beforeCreate(初始化界面前)–> created(初始化界面后)–> beforeMount(渲染dom前)–> mounted(渲染dom后)–> beforeUpdate(更新数据前)–> updated(更新数据后)–> beforeDestroy(卸载组件前)–> destroyed(卸载组件后)
父组件生命周期钩子执行顺序:
- 加载渲染过程:父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted
- 子组件更新过程:父beforeUpdate->子beforeUpdate->子updated->父updated
- 父组件更新过程:父beforeUpdate->父updated
- 销毁过程:父beforeDestroy->子beforeDestroy->子destroyed->父destroyed
9. route 和 router 的区别是什么?(字节跳动)
route是“路由信息对象”,包括path,params,hash,query,fullPath,matched,name等路由信息参数。
router是“路由实例对象”,包括了路由的跳转方法(push、replace),钩子函数等。
node
1. 介绍一下node中间件(滴滴)
中间件就是请求req和响应res之间的一个应用,请求浏览器向服务器发送一个请求后,服务器直接通过request定位属性的方式得到通过request携带过去的数据,就是用户输入的数据和浏览器本身的数据信息,这中间就一定有一个函数将这些数据分类做了处理,最后让request对象调用使用,这个处理函数就是我们所所得中间插件。比如生活中的租客和房主,中间需要一个中介来搭桥,这个中介就类似于中间件。一般来说,中间件用来封装底层细节,组织基础功能,分离基础设施和业务逻辑
2. node路由是什么?(字节跳动)
node中的路由由自己的框架处理,通过分析url路径分发到相应控制器中,一个路由对应的是一个或多个负责请求调用的js文件,里面包括业务逻辑(拦截,捕获,处理)
路由是一组映射关系,分析URL将访问的内容映射到实际的action或者controller上。
3. Node的Event Loop(腾讯)
轮询–>检查–>关闭事件回调–>定时器检测–>IO事件回调–>闲置阶段–>再往复(即轮询
- Node端,microtask 在事件循环的各个阶段之间执行
- 浏览器端,microtask 在事件循环的 macrotask 执行完之后执行
4. koa的中间件(洋葱圈模型)(腾讯)
// 1 2 3 4 5 6
const Koa = require('koa');
const app = new Koa();
app.use(async (ctx, next) => {
console.log(1);
await next();
console.log(6);
});
app.use(async (ctx, next) => {
console.log(2);
await next();
console.log(5);
});
app.use(async (ctx, next) => {
console.log(3);
ctx.body = "hello world";
console.log(4);
});
app.listen(3000, () => {
console.log('listenning on 3000');
});
就像一个洋葱一样,从外层进去,然后碰到next()就执行下一个中间件,以此类推,指向晚最内层后执行再从内到外返回回来。
5. 简单介绍下koa
Koa本质上是调用一系列的中间件,来处理对应的请求,并决定是否传递到下一个中间件去处理.
app.use(async (ctx, next) => {
const start = new Date()
await next()
const ms = new Date() - start
console.log(`${ctx.method} ${ctx.url} - ${ms}ms`)
})
- 初始化koa实例后,我们会用use方法来加载中间件(middleware),会有一个数组来存储中间件,use调用顺序会决定中间件的执行顺序。
- 每个中间件都是一个函数(不是函数将报错),接收两个参数,第一个是ctx上下文对象,另一个是next函数(由koa-compose定义)
- 在建立好http服务器后,会调用koa-compose模块对middleware中间件数组进行处理。原理就是:会从middleware数组中取第一个函数开始执行,中间件函数中调用next方法就会去取下一个中间件函数继续执行。每个中间件函数执行完毕后都会返回一个promise对象。(ps:调用next方法并不是表示当前中间件函数执行完毕了,调用next之后仍可以继续执行其他代码)
5. node多线程怎么管理(腾讯云)
Node.js 只支持单线程。但是可以开启多进程充分利用多核 CPU,
单个 Node.js 实例运行在单个线程中。 为了充分利用多核系统,有时需要启用一组 Node.js 进程去处理负载任务。可以使用node.js原生的cluster (集群)模块创建共享服务器端口的子进程,cluster 模块支持两种分发连接的方法。
第一种方法(也是除 Windows 外所有平台的默认方法)是循环法,由主进程负责监听端口,接收新连接后再将连接循环分发给工作进程,在分发中使用了一些内置技巧防止工作进程任务过载。
第二种方法是,主进程创建监听 socket 后发送给感兴趣的工作进程,由工作进程负责直接接收连接。
理论上第二种方法应该是效率最佳的。 但在实际情况下,由于操作系统调度机制的难以捉摸,会使分发变得不稳定。 可能会出现八个进程中有两个分担了 70% 的负载。
潜在问题: 因为每个进程的内存都是独立的,为了在多进程中共享数据,原来可能存储在内存中的数据,例如 token 等不能再存储在内存中,应该存储在 redis 等缓存中,以便保证不同的进程都可以访问该数据;
面经系列:
2020前端一些大厂面经系列———HTML,CSS,算法
2020前端一些大厂面经系列———JS
2020前端一些大厂面经系列———ES6
2020前端一些大厂面经系列———网络(上)
2020前端一些大厂面经系列———网络(下)
2020前端一些大厂面经系列———vue,node
2020前端一些大厂面经系列———综合实战篇