前端语法&规范(五)

73 阅读6分钟

虽然之前介绍过什么是mvvm了,今天更深入的讲一讲mvvm框架

简述 MVVM

MVVM,即 Model-View-ViewModel,model代表数据模型,view代表视图,viewmodelview连接model通信的桥梁,当数据发生改变时会通知到视图层渲染,视图变化的时候也会通知数据发生改变。

Vue 底层实现原理

vue.js采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter和getter,在数据变动时发布消息给订阅者,触发相应的监听回调。
Observer(数据监听器):Observer的核心是通过Object.defineProperty()来监听数据的变动,这个函数内部可以定义一个setter方法和getter方法,当数据发生变化是会触发setter,此时observer就会通知订阅者(watcher)
watcher(订阅者):watcher订阅者作为observer和compile之间通信的桥梁,,主要做的事情是:

  1. 在自身实例化时往属性订阅器(dep)里添加自己
  2. 自身必须有一个update()方法
  3. 待属性变动dep.notice()通知时,能调用自身的upload()方法,并触发compile中绑定的回调 compile(指令解析器):compile主要做的事情是解析模板指令,将模板中变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加鉴定数据的订阅者,一旦数据有变动,收到通知,更新视图

Vue 的生命周期

  • created阶段:vue实例被创建
    beforeCreate:创建前,data和methods中的数据还没有初始化
    created:创建完毕,data中有值,未挂载到dom
  • mount阶段:vue实例被挂载到dom节点 beforeMount:开始挂载钩子,此时模板数据还没有生成
    mounted:dom有数据,可以操作dom
  • update阶段:当vue实例里面的data数据发生改变时触发组件的重新渲染 beforeDestory:实例被销毁前,此时可以手动销毁一些方法
    destoryed:销毁完毕。

组件生命周期

生命周期(父子组件)父组件beforeCreate --> 父组件created --> 父组件beforeMount --> 子组件beforeCreate --> 子组件created --> 子组件beforeMount --> 子组件mounted --> 父组件 mounted --> 父组件beforeUpdate --> 子组件beforeDestory --> 子组件destoryed --> 父组件updated

computed 与 watch
  1. watch
    watch属性监听是一个对象,键是需要观察的属性,值是对应回调函数,主要用来监听某些特定数据的变化,从而进行某些具体的业务逻辑操作,监听属性的变化,需要在数据变化时执行异步或开销较大的操作时使用。
  2. computed
    computed计算属性属性的结果会被缓存,当computed中的函数所依赖的属性没有发生改变时,那么调用当前函数的时候结果会从缓存中读取。除非依赖的响应式属性发生变化时才会重新计算,主要当做属性来使用,computed中的函数必须用return返回最终的结果。
那么什么时候用watch,什么时候用computed呢?

当一个属性受多个属性影响时,使用 computed ;
当一条数据影响多条数据的时候用 watch
在既能使用 computed 又能用 watch 的时候,推荐使用 computed,重点重点重点在 computed 的缓存功能 computed 计算属性是用来生命是的描述一个值依赖了其他的值
watch 监听的是已经在 data 中定义的变量,变量变化就会触发 watch,有可能影响性能消耗。

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

  1. 一个组件被复用多次的话就会创建多个实例,本质上这些实例用的都是同一个构造函数;
  2. 如果data是对象的话,对象属于引用类型,会影响到所有实例,为了保证不同组件之间的data不冲突,data必须是一个函数;

为什么 v-for 和 v-if 不建议一块使用

v-forv-if在一个节点时,v-for的优先级比v-if高,这意味着v-if将在每一个v-for中运行,如果要遍历的数据量很大,而真正要展示的数据很少时,会造成很大的性能浪费!

注意:3.x版本中v-if总是优先于v-for生效。但是由于语法上存在歧义,建议避免在同一元素上同时使用二者!

key 的作用

  • key 的作用是为了在diff算法执行时更快地找到对应的节点,提高diff速度,更高效的更新虚拟dom vue 和 react 都是采用 diff 算法来对比新旧虚拟节点,从而更新节点。在 vue 的 diff 函数中,会根据新节点的 key 去对比旧节点数组中的 key,从而找到相应旧节点。如果没有找到就认为是一个新增的节点,但是如果没有key,那么就会采用遍历查找的方式去找到对应的旧节点。一种是map映射,另一种是遍历查找。
  • 为了在数据变化时强制更新组件,以避免“就地复用”带来的副作用。 当vuejs用 v-for 更新已渲染过的元素列表时,它默认用“就地复用”策略。如果数据项的顺序被改变,vue将不会移动dom元素来匹配数据项的顺序,而是简单复用此处每个元素,并且确保它在特定索引下显示已被渲染的每个元素。key值重复会造成渲染错误!

Vue组件的通信方式

  • 我们常用的方法有props/$emit用来父子间的通信
  • 兄弟组件或其他自定义组件中通信一般使用$emit/$on
  • 有时也会使用Event Bus实现跨组件通信Vue.prototype.$bus = new Vue()自定义事件
  • vuex中跨级组件通信一般使用$attrs/$listenersProvide/inject

nextTick

nextTick是什么?
  • 因为 vue 采用的 异步更新策略,当监听到数据发生变化的时候不会立即更新DOM,而是开启一个任务队列,并缓存在同一事件循环中的所有数据变更。
  • 这种做法的好处是可以将多次数据更新合并成一次,减少操作DOM的次数,提升性能。
什么时候使用nextTick?

想要操作基于最新数据生成的DOM时,这个操作就应该放在nextTick的回调中

nextTick的实现原理是什么?

将传入的回调函数包装成异步任务,异步任务又分为宏任务和微任务,为了尽快执行所以优先选择微任务 ~
nextTick提供 四种 异步方法:

  1. Promise.the
  2. MutationObserver
  3. setImmediate
  4. setTimeout