飞道的博客

2020年3月份最新vue面试题汇总一、

532人阅读  评论(0)

1.谈一下你对MVVM原理的理解 

2.请说一下响应式数据的原理? 
理解: 
1.核心点: Object.defineProperty 2.默认 Vue 在初始化数据时,会给 data 中的属性使用 Object.defineProperty 重新定义所有属 性,当页面取到对应属性时。会进行依赖收集(收集当前组件的watcher) 如果属性发生变化会通 知相关依赖进行更新操作。

原理: 

 


  
  1. Object.defineProperty(obj, key, {    
  2. enumerable: true,    
  3. configurable: true,    
  4. get: function reactiveGetter () {      
  5. const value = getter ? getter.call(obj) : val      
  6. if (Dep.target) {        
  7. dep.depend() // ** 收集依赖 ** /        
  8. if (childOb) {          
  9. childOb.dep.depend()          
  10. if ( Array.isArray(value)) {            
  11. dependArray(value)         
  12. }       
  13. }     
  14. }      
  15. return value   
  16. },    
  17. set: function reactiveSetter (newVal) {      
  18. const value = getter ? getter.call(obj) : val      
  19. if (newVal === value || (newVal !== newVal && value !== value)) {        
  20. return     
  21. }      
  22. if (process.env.NODE_ENV !== 'production' && customSetter) {        
  23. customSetter()     
  24. }
  25.   val = newVal      
  26. childOb = !shallow && observe(newVal)      
  27. dep.notify() /**通知相关依赖进行更新**/  
  28. }
  29. })

3.Vue中是如何检测数组变化? 
理解: 
使用函数劫持的方式,重写了数组的方法 Vue 将 data 中的数组,进行了原型链重写。指向了自己定义的数组原型方法,这样当调用数组 api 时,可以通知依赖更新.如果数组中包含着引用类型。会对数组中的引用类型再次进行监控。

原理: 


  
  1. const arrayProto = Array.prototype
  2. export const arrayMethods = Object.create(arrayProto)
  3. const methodsToPatch = [   'push',   'pop',   'shift',   'unshift',   'splice',   'sort',   'reverse' ]
  4. methodsToPatch.forEach( function (method) { // 重写原型方法  const original = arrayProto[method]
  5. // 调用原数组的方法  
  6. def(arrayMethods, method, function mutator (...args) {    
  7. const result = original.apply( this, args)    
  8. const ob = this.__ob__    
  9. let inserted
  10. switch (method) {      
  11. case 'push':      
  12. case 'unshift':        
  13. inserted = args        
  14. break      
  15. case 'splice':        
  16. inserted = args.slice( 2)        
  17. break  
  18. }    
  19. if (inserted) ob.observeArray(inserted)     // notify change    
  20. ob.dep.notify() // 当调用数组方法后,手动通知视图更新    
  21. return result
  22. })
  23. })
  24. this.observeArray(value) // 进行深度监控


4.为何Vue采用异步渲染? 
理解: 
因为如果不采用异步更新,那么每次更新数据都会对当前组件进行重新渲染.所以为了性能考虑。 Vue 会在本轮数据更新后,再去异步更新视图!

原理: 


   


  
  1. update () {     /* istanbul ignore else */    
  2. if ( this.lazy) {      
  3. this.dirty = true  
  4. } else if ( this.sync) {      
  5. this.run()  
  6. } else {    
  7.   queueWatcher( this); // 当数据发生变化时会将watcher放到一个队列中批量更新  
  8. }
  9. }
  10. export function queueWatcher (watcher: Watcher) {  
  11. const id = watcher.id // 会对相同的watcher进行过滤  
  12. if (has[id] == null) {    
  13. has[id] = true    
  14. if (!flushing) {      
  15. queue.push(watcher)  
  16. } else {      
  17. let i = queue.length - 1      
  18. while (i > index && queue[i].id > watcher.id) {        
  19. i--   
  20. }      
  21. queue.splice(i + 1, 0, watcher)  
  22. }    
  23. // queue the flush    
  24. if (!waiting) {    
  25.  waiting = true
  26.       if (process.env.NODE_ENV !== 'production' && !config.async) {        
  27. flushSchedulerQueue()        
  28. return    
  29. }      
  30. nextTick(flushSchedulerQueue) // 调用nextTick方法 批量的进行更新  
  31. }
  32. }
  33. }


5.nextTick实现原理? 
理解:(宏任务和微任务) 异步方法 
nextTick 方法主要是使用了宏任务和微任务,定义了一个异步方法.多次调用 nextTick 会将方法存入 队列中,通过这个异步方法清空当前队列。 所以这个 nextTick 方法就是异步方法

原理: 

 


  
  1. let timerFunc   // 会定义一个异步方法
  2. if ( typeof Promise !== 'undefined' && isNative( Promise)) {   // promise  
  3. const p = Promise.resolve()  
  4. timerFunc = () => {    
  5. p.then(flushCallbacks)    
  6. if (isIOS) setTimeout(noop)
  7. }  
  8. isUsingMicroTask = true
  9. } else if (!isIE && typeof MutationObserver !== 'undefined' && ( // MutationObserver  
  10. isNative(MutationObserver) ||  MutationObserver.toString() === '[object MutationObserverConstructor]' )) {  
  11. let counter = 1  
  12. const observer = new MutationObserver(flushCallbacks)  
  13. const textNode = document.createTextNode( String(counter))  
  14. observer.observe(textNode, {     characterData: true })  
  15. timerFunc = () => {    
  16. counter = (counter + 1) % 2    
  17. textNode.data = String(counter) }  
  18. isUsingMicroTask = true
  19. } else if ( typeof setImmediate !== 'undefined' ) { // setImmediate  
  20. timerFunc = () => {    
  21. setImmediate(flushCallbacks)
  22. } } else {  timerFunc = () => {   // setTimeout    
  23. setTimeout(flushCallbacks, 0) } } // nextTick实现
  24. export function nextTick (cb?: Function, ctx?: Object) {  
  25. let _resolve  
  26. callbacks.push( () => {    
  27. if (cb) {      
  28. try {        
  29. cb.call(ctx)    
  30. } catch (e) {        
  31. handleError(e, ctx, 'nextTick')    
  32. }  
  33. } else if (_resolve) {      
  34. _resolve(ctx)  
  35. }
  36. })  
  37. if (!pending) {    
  38. pending = true    
  39. timerFunc()
  40. }
  41. }

6.Vue中Computed的特点 
理解: 默认 computed 也是一个 watcher 是具备缓存的,只要当依赖的属性发生变化时才会更新视图

原理: 


  
  1. function initComputed (vm: Component, computed: Object) {  
  2. const watchers = vm._computedWatchers = Object.create( null)  
  3. const isSSR = isServerRendering()   for ( const key in computed) {    
  4. const userDef = computed[key]    
  5. const getter = typeof userDef === 'function' ? userDef : userDef.get    
  6. if (!isSSR) {       // create internal watcher for the computed property.      
  7. watchers[key] = new Watcher(        vm,        getter || noop,        noop,        
  8. computedWatcherOptions     )   }
  9.   // component-defined computed properties are already defined on the    
  10. // component prototype. We only need to define computed properties defined
  11. // at instantiation here.    
  12. if (!(key in vm)) {      
  13. defineComputed(vm, key, userDef)  
  14. } else if (process.env.NODE_ENV !== 'production') {      
  15. if (key in vm.$data) {        
  16. warn( `The computed property "${key}" is already defined in data.`, vm)    
  17. } else if (vm.$options.props && key in vm.$options.props) {        
  18. warn( `The computed property "${key}" is already defined as a prop.`, vm)    
  19. }  
  20. }
  21. }
  22. }
  23. function createComputedGetter (key) {  
  24. return function computedGetter () {    
  25. const watcher = this._computedWatchers && this._computedWatchers[key]    
  26. if (watcher) {      
  27. if (watcher.dirty) { // 如果依赖的值没发生变化,就不会重新求值        
  28. watcher.evaluate()    
  29. }      
  30. if (Dep.target) {        
  31. watcher.depend()    
  32. }      
  33. return watcher.value  
  34. }
  35. }
  36. }


7.Watch中的deep:true 是如何实现的 
理解: 
当用户指定了 watch 中的deep属性为 true 时,如果当前监控的值是数组类型。会对对象中的每 一项进行求值,此时会将当前 watcher 存入到对应属性的依赖中,这样数组中对象发生变化时也 会通知数据更新

原理: 
    


  
  1. get () {    
  2. pushTarget( this) // 先将当前依赖放到 Dep.target上    
  3. let value    
  4. const vm = this.vm    
  5. try {      
  6. value = this.getter.call(vm, vm)  
  7. } catch (e) {      
  8. if ( this.user) {        
  9. handleError(e, vm, `getter for watcher "${this.expression}"`)    
  10. } else {        
  11. throw e    
  12. }  
  13. } finally {      
  14. if ( this.deep) { // 如果需要深度监控        
  15. traverse(value) // 会对对象中的每一项取值,取值时会执行对应的get方法    
  16. }      
  17. popTarget()  
  18. }
  19. return value
  20. }
  21. function _traverse (val: any, seen: SimpleSet) {  
  22. let i, keys
  23. const isA = Array.isArray(val)  
  24. if ((!isA && !isObject(val)) || Object.isFrozen(val) || val instanceof VNode) {    
  25. return
  26. }  
  27. if (val.__ob__) {    
  28. const depId = val.__ob__.dep.id    
  29. if (seen.has(depId)) {      
  30. return  
  31. }    
  32. seen.add(depId)
  33. }  
  34. if (isA) {    
  35. i = val.length    
  36. while (i--) _traverse(val[i], seen)
  37. } else {    
  38. keys = Object.keys(val)    
  39. i = keys.length    
  40. while (i--) _traverse(val[keys[i]], seen)
  41. }
  42. }


8.Vue组件的生命周期 


  

原理: 


9.ajax请求放在哪个生命周期中 
理解: 
1.在created的时候,视图中的 dom 并没有渲染出来,所以此时如果直接去操 dom 节点,无法找到相 关的元素

2.在mounted中,由于此时 dom 已经渲染出来了,所以可以直接操作 dom 节点 
一般情况下都放到 mounted 中,保证逻辑的统一性,因为生命周期是同步执行的, ajax 是异步执行的
服务端渲染不支持mounted方法,所以在服务端渲染的情况下统一放到created中

10.何时需要使用beforeDestroy 
理解: 
1.可能在当前页面中使用了 $on 方法,那需要在组件销毁前解绑。

2.清除自己定义的定时器

3.解除事件的绑定 scroll mousemove ....

11.Vue中模板编译原理 
将 template 转化成 render 函数

12.Vue中v-if和v-show的区别 
理解: 
v-if 如果条件不成立不会渲染当前指令所在节点的 dom 元素

v-show 只是切换当前 dom 的显示或者隐藏

原理: 


  

13.为什么V-for和v-if不能连用 

理解:

v-for 会比 v-if 的优先级高一些,如果连用的话会把 v-if 给每个元素都添加一下,会造成性能问题

14.用vnode来描述一个DOM结构 
虚拟节点就是用一个对象来描述真实的 dom 元素


15.diff算法的时间复杂度 
 两个树的完全的 diff 算法是一个时间复杂度为 O(n3) , Vue 进行了优化·O(n3) 复杂度的问题转换成 O(n) 复杂度的问题(只比较同级不考虑跨级问题)  在前端当中, 你很少会跨越层级地移动Dom元素。 所 以 Virtual Dom只会对同一个层级的元素进行对比。 16.简述Vue中diff算法原理 
理解: 
1.先同级比较,在比较子节点
2.先判断一方有儿子一方没儿子的情况

3.比较都有儿子的情况

4.递归比较子节点


原理: 
core/vdom/patch.js


未完待续.....

如果希望了解更多请关注公众号:「前端的那些事情」,提升前端技能!

 


转载:https://blog.csdn.net/qq_41646249/article/details/104644647
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场