vue面试题01

原创
09/02 23:36
阅读数 1
1、谈一下你对MVVM原理的理解
  • 传统的mvc指的是,用户操作会请求服务端路由,路由会调用对应的控制器来处理,控制器会获取数据。将结果返回给前端,页面重新进行渲染。
  • mvvm:传统的前端会将数据手动渲染到页面上,mvvm模式不需要用户收到操作dom元素,将数据绑定到viewModel层上,会自动将数据渲染到页面中,视图变化会通知viewModel层更新数据。viewModel就是我们mvvm模式中的桥梁。
2、请说一下响应式数据的原理?
理解:
  • 1、核心点:object.defineProperty
  • 2、默认vue在初始化数据时,会给data中的object.defineProperty重新定义所有属性,当页面取到对应属性时。会进行依赖收集(收集当前组件的watcher)如果属性发生变化会通知相关依赖进行更新操作。
原理:
  • initData 初始化用户传入的data数据
  • new Observer 将数据进行观测
  • this.walk(value) 进行对象的处理
  • defineReactive 循环对象属性定义响应式变化
  • Object.defineProperty 使用Object.defineProperty重新定义数据

拦截属性的获取-进行依赖收集 拦截属性的更新操作,对相关依赖进行通知

Object.defineProperty(obj,key,{
    enumerable:true,
    configurable:true,
    get: function reactiveGetter () {
        const value = getter ? getter.call(obj) : val
        if (Dep.target) {
            dep.depend() // ** 收集依赖 **/
            if (childOb) {
                childOb.dep.depend()
                if(Array.isArray(value)){
                    dependArray(value)
                }
            }
        }
        return value
    },
    set: function reactiveSetter (newVal) {
        const value = getter ? getter.call(obj) : val
        if(newVal === value || (newVal !== newVal && value !== value)) {
            return 
        }
        if(process.env.NODE_ENV !== 'Production' && customSetter) {
            countomSetter()
        }
        
        val = newVal
        childOb = shallow && observe(newVal)
        dep.notify() /** 通知相关依赖进行更新 **/
    }

)
3、vue中是如何检测数组变化?
理解:
  • 使用函数劫持的方式,重写了数组的方法
  • Vue将data中的数组,进行原型链重写。指向了自定义的数组原型方法,这样当调用数组api时,可以通知依赖更新。如果数组中包含着引用类型。会对数组中的引用类型再次进行监控。
原理:
  • initData 初始化用户传入的data数据
  • new Observer 将数据进行观测
  • protoAugment(value,arryMethods) -> 对数组的原型方法进行重写
  • observeArray 商都观察数组中的每一项(对象类型)
const arrayProto = Array.prototype
export const arrayMethods = Objcet.create(arrayProto)
const methodsToPatch = [
    'push',
    'pop',
    'unshift',
    'splice',
    'sort',
    'reverse'
]

methodsToPath.forEach(function(method){ // 重写原型方法
    const original = arrayProto[method] // 调用原数组的方法
    def(arrayMethods, method, function mutator(...args){
        const result = original.apply(this,args)
        const ob = this.__ob__
        let inserted
        swtch(method){
            case 'push':
            case 'unshift':
                inserted = args
                break
            case 'splice':
                inserted = args.slice(2)
                break
        }
        if (inserted) ob.observeArray(inserted)
        
        ob.dep.notify() // 当调用数组方法后,手动通知视图更新
        return result
    })
})

this.observeArray(value) // 进行深度监控
4、为何vue采用异步渲染视图
理解:

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

原理:
  • dep.notify() 通知watcher进行更新操作
  • subs[i].update() 依次调用watcher的update
  • queueWatcher 将watcher去重放到队列中
  • nextTick(flushSchedulerQueue) 异步清空watcher队列
  
  notify () { // 通知存储的依赖更新
    // stabilize the subscriber list first
    const subs = this.subs.slice()
    if (process.env.NODE_ENV !== 'production' && !config.async) {
      // subs aren't sorted in scheduler if not running async
      // we need to sort them now to make sure they fire in correct
      // order
      subs.sort((a, b) => a.id - b.id)
    }
    for (let i = 0, l = subs.length; i < l; i++) {
      subs[i].update() // 依赖中的update方法
    }
  }
}

  update () {
    /* istanbul ignore else */
    if (this.lazy) { // 计算属性
      this.dirty = true;
    } else if (this.sync) {
      this.run();
    } else {
      queueWatcher(this); // 将watcher放入队列
    }
  }
  
  function queueWatcher (watcher) {
  const id = watcher.id; // 过滤watcher 多个属性依赖同一个watcher
  if (has[id] == null) {
    has[id] = true;
    if (!flushing) {
      queue.push(watcher); // 将watcher放到队列中
    } else {
      // if already flushing, splice the watcher based on its id
      // if already past its id, it will be run next immediately.
      let i = queue.length - 1;
      while (i > index && queue[i].id > watcher.id) {
        i--;
      }
      queue.splice(i + 1, 0, watcher);
    }
    // queue the flush
    if (!waiting) {
      waiting = true;

      if (!config.async) {
        flushSchedulerQueue();
        return
      }
      nextTick(flushSchedulerQueue); // 在下一个tick中刷新watcher队列
    }
  }
}
展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部