2020前端一些大厂面经系列———vue,node

12 篇文章 1 订阅

接上篇

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的变化来动态render页面便可。

  • 我们实现一个简单的订阅-发布器,所以我们需要对current.route做数据劫持。一旦current.route更新,我们可以及时的更新当前页面
  • 接下来是hash和history模式的实现, 当页面变化时,我们只需要监听hashchangepopstate事件,做路由转换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/$childrenref

具体了解戳这里

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前端一些大厂面经系列———综合实战篇

面经系列:
2020前端一些大厂面经系列———HTML,CSS,算法
2020前端一些大厂面经系列———JS
2020前端一些大厂面经系列———ES6
2020前端一些大厂面经系列———网络(上)
2020前端一些大厂面经系列———网络(下)
2020前端一些大厂面经系列———vue,node
2020前端一些大厂面经系列———综合实战篇

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值