面试常问考点

vue2响应式数据的理解

可以监控一个数据的修改和获取操作。针对对象格式给每个对象的属性通过Object.defineProperty()进行劫持
从源码层面,先调用initData对数据初始化,在调用observe方法将数据变成响应式,变成响应式内部用的是definedReactive方法(内部对所有属性进行了重写,用性能问题)。递归给对象添加getter和setter方法
我们在使用Vue的时候,层级过深(要考虑优化),如果数据不是响应的,就不要放在data中了,我们属性取值的时候避免多次取值(比如:for(let i = 0;i<10;i++){this.a += i});如果有些对象是放到 data中的,但是不是响应式的,可以采用Object.freeze()冻结对象

2 vue2中如何检测数组的变化

vue2中检测数组的变化并没有采用defineProperty,因为修改索引的情况不多(如果直接使用defineProperty会浪费大量性能)采用重写数组方法来实现对数组的监测(函数劫持)
从源码层面,先调用initData对数据初始化,在调用observe方法,对我们传入的数组进行原型链修改,后续调用的方法都是重写后的方法-》数组中每个对象也会再次进行代理
注意:修改数组索引,修改数组长度无法进行监控

vue中如何进行依赖收集的?

所谓依赖收集(观察者模式) 被观察者指代的是数据(data),观察者(watcher 3个watcher 渲染、计算属性、用户watcher)
(每个属性都有一个dep)
一个watcher中可能对应着多个数据,watcher中还需要保存dep(重新渲染的时候可以让属性重新记录watcher)计算属性也会用到
多对多的关系 一个dep对应多个watcher,一个watcher有多个dep。默认渲染的时候会进行依赖收集(会触发get方法),数据更新了就找到属性对应的watcher去触发更新
取值的时候依赖收集,设置的时候更新视图

如何理解vue2中模版翻译原理

我们用户传递的是template属性,我们需要将这个template编译成render函数
代码层面:
template转换成ast语法树,对语法树进行标记,将ast语法树生成render函数
最终每次渲染都可以调render函数返回对应的虚拟节点

vue2生命周期钩子是如何实现的

就是内部利用了发布订阅模式,将用户写的钩子维护成了一个数组,后续依次调用 callHook,主要靠的是mergerOptions
内部就是一个发布订阅

vue2生命方法有哪些,一般在哪一步发送请求

beforeCreate 合理没有实现响应式 vue3去掉了
created 拿到响应数据(但是没有dom渲染) 这个api可以在服务端渲染中使用
beforeMount
mounted 可以获取dom 组件挂载完成
beforeUpdate
updated
activated
deactivated
beforeDestory 还就有响应式数据
destroyed
errorCaptured 捕获错误

一般最多在mounted调用接口(服务端没有dom,也就没有mounted钩子)

created执行完之后再执行mounted,如果在created中调用接口,是否接口调用完成之后,在执行mounted?
不对,因为生命周期是按顺序调用的(同步的),请求是异步的,所以最终获取到数据肯定是在mounted之后在调用

vue.mixin的应用场景

可以用过vue.mixin来实现逻辑复用,问题在于数据来源不明确。声明的时候可能会导致命名冲突 (vue3采用compositionAPI解决复用问题)

vue2 组件中的data为什么必须是一个函数

针对根实例而言,只能new一次。组件是通过同一个构造函数多次创建实例,如果同一个对象的话,那么数据就会被互相影响,每个组件的数据源都是独立的,那就每次都调用data返回一个新对象
(组件是调用的Vue.extend方法,会将用户的选项放到子类上)

nextTick是在哪里使用?原理是?

nextTick内部采用了异步任务进行了包装(多个nextTick调用会被合并成一次 内部会合并调用)最后在异步任务中批量处理
主要应用场景就是异步更新(默认调度的时候,就会添加一个nextTick任务)用户为了获取最终的渲染结果需要在内部任务执行之后再执行用户逻辑,这时候用户需要将对应的逻辑放到nextTick中

computed和watch区别

computed和watch的相同点:底层都会创建一个watcher
区别:
1 用法区别: computed定义的属性可以在模版中使用,watch不能在视图中使用

  • computed默认不会立即执行,只能取值的时候才执行,内部有一个唯一的dirty属性来控制依赖的值是否发生变化。默认计算属性需要同步返回结果。
  • watch默认用户会提供一个回调函数,数据变化了就调用这个回调。我们可以监控某个数据的变化,数据变化了执行某些操作

vue.set方法是如何实现的

Vue.set方法是vue中一个补丁方法(正常我们添加的属性是不会触发更新的,数组无法监控到索引和长度)
实现: 属性添加或者删除是,手动触发对象本身的dep来进行更新
vm.age.ob.dep.notify()
数组:vm.age[0] = 100 => vm.$set(vm.age,‘b’,100)=> vm.age.splice(0,1,100)
对象: 调用响应式方法,出发dep.notify()更新视图

vue中diff算法原理

diff算法的特点就是平级比较,内部采用了双指针方式进行了优化,优化了常见的操作,采用了递归比较方式
针对一个节点的diff算法

  • 先拿出根节点来进行比较,如果是同一个节点则比较属性,如果不是用一个节点则直接换成最新的即可
  • 同一节点比较属性后,复用老节点
    比较儿子
  • 一方有儿子 一方没儿子 (删除、添加)
  • 两方都有儿子
    1 优先比较头头 尾尾 交叉比较
    2 就做一个映射表,用新的去映射表中查找此元素是否存在,存在则移动不存在则插入,最后删除多余的

13 vue中为什么要有虚拟dom

好处?
我们写的代码可能要针对不同的平台使用(weex、web、小程序),可以跨平台,不用考虑平台问题。
不用关心兼容问题,可以在上层将对应的渲染方法传递给我,我来通过虚拟dom渲染即可
diff算法,针对跟新的时候,有了虚拟dom之后我们可以通过diff算法来找到最后的差异进行修改真实dom

既然vue通过数据劫持可以精准探测数据变化,为什么还需要虚拟dom进行diff检测差异

如果给每个属性都去增加watcher,而且力度太小也是不好控制的。降低watcher的数量(每个组件都有一个watcher)通过diff算法来优化渲染过程。通过diff算法和响应式原理折中处理

请说明 vue中key的作用和原理,谈谈你对它的理解

isSameVode中会根据key来判断两个元素是否是同一个元素,key不同说明不是同一个元素(key在动态列表中不要使用索引–>有bug)我们使用key,尽量保证key的唯一性(这样可以优化diff)

16 谈谈对vue组件化的理解

组件的优点:组件的复用,可以根据数据渲染对应的组件,把组件相关的内容放在一起(方便服用),合理规划组件,可以做到更新的时候是组件级更新

vue中怎样处理组件 1 vue.extend 根据用户传入的对象生成一个组件的构造函数 2 根据组件产生对应的虚拟节点 data:{hook:init} 3 组件初始化 将虚拟节点转换成真实节点(组件init方法) new Sub().$mount()

vue的组件渲染过程

1 vm. o p t i o n s . c o m p o n e n t s [ ′ m y ′ ] = m y : 模版 2 创造组件的虚拟节点 c r e a t e C o m p o n e n t t a g : ′ m y ′ , d a t a : h o o k : i n i t , c o m p o n e n t O p t i o n s : C o t r : V u e . e x t e n d ( m y : 模版 ) 3 创造真实节点的 c r e a t e C o m p o n e n t i n i t − > n e w 组件 ( ) . options.components['my'] = {my:模版} 2 创造组件的虚拟节点 createComponent {tag:'my',data:{hook:{init}},componentOptions:{Cotr:Vue.extend({my:模版})}} 3 创造真实节点的 createComponent init-> new组件(). options.components[my]=my:模版2创造组件的虚拟节点createComponenttag:my,data:hook:init,componentOptions:Cotr:Vue.extend(my:模版)3创造真实节点的createComponentinit>new组件().mount()-> vm.componentInstance
4 vm.$el插入到父元素中

18 vue中组件的更新

组件更新会触发组件的prepatch方法(复用组件的实例,并且可以更新属性、事件和插槽)
父组件给子组件传递的属性是(props)响应式的,在模版中使用会做依赖收集,收集自己的watcher
稍后组件更新了会重新给props赋值,赋值完成后会触发watcher重新更新
1 data数据更新,就是依赖收集
2 属性更新 可以给组件传入属性,属性变化后触发更新
属性本身就是响应式的,将响应式数据传递给儿子,儿子在模版中使用,稍后属性变化会自动更新视图
3 插槽变化也会更新

19 vue中异步组件原理

vue中异步组件的写法有很多,主要用作大组件可以异步加载 markdown组件 editor组件。
就是先渲染一个注释标签,等组件加载完毕,最后重新渲染 forceUpdate
原理:异步组件默认不会调用vue.extend方法 所有Ctor上没有cid属性,没有cid属性的就是异步组件。会先渲染一个占位符组件。但是如果有loading就先渲染loading,第一轮结束。如果用户调用resolve,会将结果赋予给factory.resolved上面,强制重新渲染。重新渲染的时候再次进入到resloveAsyncComponent中,会直接拿到factory.resloved结果渲染

20 函数式组件的优势和原理

React中也区分两种组件 一种是类组件,一种是函数组件 (函数组件 没有类就没有this,也没有所谓的状态,也就没有所谓的生命周期,好处就是性能好,不需要创建watcher)
函数式组件就是调用render拿到返回结果来渲染,所以性能高

21 vue组件之间的传值方式及之间的区别

1 props 父传递数据给儿子 属性的原理就是解析后的props—
2 emit 儿子触发组件
3 $attrs(所有的组件上的属性 不含盖props) $listeners(组件上的所有事件)
4 vue.observalbel 可以创建一个全局的对象用于通信(少)
5 vuex

22 v-if和v-for那个优先级高?

v-for的优先级更高,在编译的时候会讲v-for 渲染成_l函数 v-if会变成三元表达式
v-if和v-show的区别
v-show:控制样式 display:none (编译成指令)
1 v-show 会是span变成块元素么?
不会,默认v-show会保存默认的display属性,如果条件满足会采用原始的display
2 为什么不用visibility:hidden (占位)不能响应事件
3 为什么不用opacity (透明度 也占位)可以响应事件
v-if:控制是否渲染 (在编译的时候变成三元表达式)

23 v-if v-model v-for的实现原理

v-if会被编译成三元表达式
v-for 会被编译成_l 循环
v-model 放在不同的元素上会编译出不同的结果,针对文本来说会处理文本(会被编译成 value+ input +指令)
value和input实现双向数据绑定阻止中文的触发 指令作用就是处理中文输入完毕后 手动出发更新
v-model绑定在组件上
这里会编译一个model对象,组件在创建虚拟节点的时候会有这个对象。会看一下里面是否有自定义的prop和event,如果没有则会被解析成value+input的语法糖

24 vue中slot是如何实现的?什么时候使用它

普通插槽:渲染的数据作用域采用的是父组件
具名插槽:
1 在解析组件的时候会将组件的children放到componentOptions上作为虚拟节点的属性
2 将children取出来放到组件的 vm. o p t i o n s . r e n d e r C h i l d r e n 中 3 做出一个映像表放到 v m . options._renderChildren中 3 做出一个映像表放到vm. options.renderChildren3做出一个映像表放到vm.slots上 -> 将结果放到 vm. s c o p e S l o t s 上 v m . scopeSlots 上 vm. scopeSlotsvm.scopeSlots={a:fn,b:fn,default:fn}
4 渲染组件的时候会调用_t方法 此时会去vm.$scopeSlots找到对应的函数来渲染内容
作用域插槽
渲染插槽选择的作用域是组件的
作用域插槽渲染的时候不会作为children,将作用域插槽做成了一个属性scopedSlots
制作一个映射关系 $scopedSlots = {default:fn:function(msg){retrun _c(‘div’,{},[_v(_s(msg))])}}
稍后渲染组件的模版的时候 会通过name找到对应的函数,将数据传入到函数中此时才渲染虚拟节点,用这个虚拟节点替换_t(‘default’)

普通插槽和具名插槽 是在父组件中渲染;作用域插槽在子组件中渲染。
vm. s c o p e S l o t s = k e y : f n v m . scopeSlots={key:fn} vm. scopeSlots=key:fnvm.slots={key:[vnode]}

25 vue.use是做什么的?原理是什么

1 use方法:目的是将vue的构造函数传递到插件中,让所有的插件依赖的是同一个vue版本
2 默认调用插件 默认调用插件的install方法
3 vue-router和vuex里面的package的依赖里面没有vue,是通过参数传入的

Vue事件修饰符有哪些?原理是什么

实现主要靠的是模版编译原理

addEventListener(stop) once....
编译的时候做好的
<div @click.prevent>
<div @click.stop>
编译的时候增加前缀 !~ &
<div @click.capture>  !
<div @click.once> ~
<div @click.passive> &
键盘事件 (也是通过模版编译实现的)
<input @keydown.enter>

27 如何理解自定义指令

自定义指令就是做全局映像 Vue.options = {name:definition}
自定义指令就是用户定义好对应的钩子,当元素在不同的状态时会调用对应的钩子(所有的钩子会被合并到cds对应的方法上,但时候依次调用)

29 keep-alive 在哪里使用?原理是?

是一个组件,组件初始化的时候混入
默认在切换的时候复用之前的老节点
缓存的是dom元素
1 keep-alive在路由中使用
2 在compon:is中使用(缓存)

keep-alive的原理是默认缓存加载过的组件对应的实例 内部采用了LRU算法
下次组件切换加载的时候,此时会找到对应缓存的节点来进行初始化,但是会采用上次缓存的$el来触发(不用做虚拟节点转换成真实节点)
更新和销毁会触发 actived和deactived

30 组件中写name属性有哪些好处和作用

可以实现递归
1 vue中有name属性的组件可以被递归调用(写模版语法的时候,可以通过name属性来递归调用自己)
2 在声明组件的时候 Sub.options.components[name] = sub
起到标识作用
用来标识组件,通用name找到对应的组件。自己封装跨级通信
name属性可以作用devtool调试工具,来表明具体组件

28 vue中 .sync修饰符的作用,用法和实现原理

和v-model一样,这个API 是为了实现状态同步的 vue3去掉了

4道经典面试题

1 数据类型检测

1> typeof 返回的结果是字符串
1.1 在计算机底层基于数据类型的值(二进制)进行监测
1.2 typeof null => ‘object’
对象存储在计算机中都是以 000开始的二进制存储,null也是以000开始的二进制
2> instanceof 检测当前实例是否属于这个类 返回布尔值
底层机制:只要当前类出现在实例的原型链上,结果都为true
问题1: arr instanceof Object => true
问题2: 由于可以修改肆意的修改原型链指向,所以检测出来的结果是不准的
问题3: 不能检测基本数据类型
自己写instanceof
3> constructor
1 与instanceof相比,支持基本类型的判断
问题:
1 constructor可以随便改,不准
arr.constructor === Array
4> Object.prototype.toString.call(value) 标准检测数据类型
原理: 不是转化为字符串,是返回当前实例所属类的信息 " [object Object]"
obj.toString()方法执行,this是obj,所以检测是obj它的所属类信息
只要把Object.prototype.toString执行,让它里面的this变为要检测的值,就能返回当前值所属的类信息

4道interface

3类循环对比和性能分析
for循环及forEach底层原理(for循环是自己控制循环过程)

1 var声明的时候,for和while性能差不多(不确定循环次数的是否用for)
2 let声明的时候,for循环性能更好(原理:没有创造全局不释放的变量)
3 forEach比for性能差
函数式编程和命令是编程
命令式编程(for):how 如何去做,看中的是过程,过程的管控灵活
函数式编程(forEach):what 关注的是结果。无法管控过程。但用起来方便,性能上有所消耗

for…in 性能非常差

迭代当前对象中所有可枚举的属性(会对所有属性尽行筛查)。查找机制上一定会找到原型链上
问题一:遍历顺序以数字优先
问题二:symbol属性无法遍历
问题三:可以遍历到公有中可枚举属性

for…of

for…of 比for…in快
iterator迭代器,部分数据结构实现了迭代器规范(symbol.iterator)。 有symbol.iterator接口的可以使用for…of
实现迭代器规范的:数组/部分类数组(set Map) 有length属性的(对象没有实现 )

迭代器规范:

var arr = [1,2,3]
arr[Symbol.iterator] = function(){
  let self = this,
      index = 0,
    return {
      // 必须具备next方法,执行一次next方法,拿到结构中某一项的值
      // done 是否完成迭代; value:每一次获取的值
      next(){
        if(index>self.length-1){
          return {
            done:true;
            value:undefined
          }
        }
        return {
          done:false,
          value:self[index++]
        }
      }
    }
}

// for…of 执行过程

  1 let itor = arr[Symbol.iterator]();
  2 itor.next()
    ...
    一直执行next 直到 done=true

this的了解

this是执行主体,谁把它执行的【和在哪创建没有关系】
1 函数执行,看方法前面是否有“点”,没有“点”,this是window严格模式下是undefined,有“点”前面是谁this是谁
2 给当前元素的某个事件行为绑定方法,当事件行为触发,方法中this是当前元素本身【排除attachEvent】
3 构造函数中的this是当前类的实例
4 箭头函数中没有执行主体,所有用的this都是所处上下文中的this
5 可以基于Function.prototype上的 call/apply/bind 去改变this指向

类数组->数组
1 Array.from / […arguments]
2 arr.slice() 不传参数 克隆arr返回一个新数组
Array.prototype.slice.call(arguments)

项目亮点:

  • 性能优化:
    1 webpack层面
    2 HTTP层面
    3 页面渲染层面(包含代码渲染)
    4 骨架屏
    5 延迟/异步加载
    6 大数据渲染优化
    7 大文件传输处理
    。。。
  • 新技术
    1 Hybrid
    2 可视化
    3 uni-app/flutter
    4 ts
    5 node
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值