# Vue

# 1. vue数据劫持(中级)

数据劫持: vue.js 是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。

# 2. 阐述一下你所理解的MVVM响应式原理(中级)

vue 是采用数据劫持配合发布者-订阅者的模式的方式,通过Object.defineProperty()来劫持各个属性的 getter 和 setter,在数据变动时,发布消息给依赖收集器( dep 中的subs),去通知(notify)观察者,做出对应的回调函数,去更新视图。

MVVM 作为绑定的入口,整合 Observer,Compile 和 Watcher 三者,通过 Observer 来监听 model 数据变化,通过 Compile 来解析编译模板指令,最终利用Watcher 搭起 Observer,Compile 之间的通信桥路,达到数据变化=>视图更新;视图交互变化=>数据 model 变更的双向绑定效果。

# 3. 说说vue的生命周期(中级)

  • beforeCreate

    • 创建之前,此时还没有 data 和 Method。
  • Created

    • 创建完成,此时 data 和 Method 可以使用了。

    • 在 Created 之后 beforeMount 之前如果没有el选项的话那么此时生命周期结束,停止编译,如果有则继续。

  • beforeMount

    • 在渲染之前。
  • mounted

    • 页面已经渲染完成,并且vm实例中已经添加完$el了,已经替换掉那些 DOM 元素了(双括号中的变量),这个时候可以操作 DOM 了(但是是获取不了元素的高度等属性的,如果想要获取,需要使用nextTick())。
  • beforeUpdate

    • data改变后,对应的组件重新渲染之前。
  • updated

    • data改变后,对应的组件重新渲染完成。
  • beforeDestory

    • 在实例销毁之前,此时实例仍然可以使用。
  • destoryed

    • 实例销毁后。

# 4. vue 中父子组件的生命周期(中级)

  • 父子组件的生命周期是一个嵌套的过程。

  • 渲染的过程。

    • beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted
  • 子组件更新过程。

    • beforeUpdate->子beforeUpdate->子updated->父updated
  • 父组件更新过程。

    • beforeUpdate->父updated
  • 销毁过程。

    • beforeDestroy->子beforeDestroy->子destroyed->父destroyed

# 5. Vue 中的nextTick(中级)

  • nextTick
    • 解释。
      • nextTick:在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。
    • 应用。
      • 想要在 Vue 生命周期函数中的created()操作 DOM 可以使用Vue.nextTick()回调函数。
      • 在数据改变后要执行的操作,而这个操作需要等数据改变后而改变 DOM 结构的时候才进行操作,需要用到nextTick

# 6. computed 和 watch 的区别(中级)

  • computed

    • 计算属性,依赖其他属性,当其他属性改变的时候下一次获取computed值时也会改变,computed的值会有缓存。
  • watch

    • 类似于数据改变后的回调。
    • 如果想深度监听的话,后面加一个deep:true
    • 如果想监听完立马运行的话,后面加一个immediate:true

# 7. Vue 优化方式(中级)

  • v-if 和v-show。

  • 使用Object.freeze()方式冻结data中的属性,从而阻止数据劫持。

  • 组件销毁的时候会断开所有与实例联系,但是除了addEventListener,所以当一个组件销毁的时候需要手动去removeEventListener

  • 图片懒加载。

  • 路由懒加载。

  • 为减少重新渲染和创建 dom 节点的时间,采用虚拟 dom。

# 8. Vue-router 的模式(中级)

  • hash模式。
    • 利用 onhashchange 事件实现前端路由,利用 url 中的 hash 来模拟一个 hash,以保证 url 改变时,页面不会重新加载。
  • history模式。
    • 利用 pushstate 和 replacestate 来将 url 替换但不刷新,但是有一个致命点就是,一旦刷新的话,就会可能404,因为没有当前的真正路径,要想解决这一问题需要后端配合,将不存在的路径重定向到入口文件。

# 9. MVC 与 MVVM 有什么区别(中级)

  • MVC

    • Model(模型)是应用程序中用于处理应用程序数据逻辑的部分。通常模型对象负责在数据库中存取数据

    • View(视图)是应用程序中处理数据显示的部分。通常视图是依据模型数据创建的。

    • Controller(控制器)是应用程序中

      处理用户交互的部分

      • 通常控制器负责从视图读取数据,控制用户输入,并向模型发送数据

# 10. diff 算法(中级)

diff 算法是指对新旧虚拟节点进行对比,并返回一个 patch 对象,用来存储两个节点不同的地方,最后利用 patch 记录的消息局部更新 DOM。

# 11. 虚拟 DOM 的优缺点(中级)

  • 缺点

    • 首次渲染大量 DOM 时,由于多了一层虚拟 DOM 的计算,会比 innerHTML 插入慢。
  • 优点

    • 减少了 DOM 操作,减少了回流与重绘。

    • 保证性能的下限,虽说性能不是最佳,但是它具备局部更新的能力,所以大部分时候还是比正常的 DOM 性能高很多的。

# 12. Vue 的 Key 的作用(中级)

  • key

    • key 主要用在虚拟 Dom 算法中,每个虚拟节点 VNode 有一个唯一标识 Key,通过对比新旧节点的 key 来判断节点是否改变,用 key 就可以大大提高渲染效率,这个 key 类似于缓存中的 etag。

# 13. Vue 组件之间的通信方式(中级)

  • 子组件设置props + 父组件设置v-bind:/:

    • 父传子
  • 子组件的$emit + 父组件设置v-on/@

    • 子传父
  • 任意组件通信,新建一个空的全局Vue对象,利用 e m i t 发 送 , emit发送, emit发送,on接收。

    • 传说中的$bus

    • 任意组件

      Vue.prototype.Event=new Vue();
      	
      Event.$emit(事件名,数据);
      Event.$on(事件名,data => {});
      
  • Vuex

    • 里面的属性有:

      • state

        • 存储数据的。
        • 获取数据最好推荐使用 getters。
        • 硬要使用的话可以用 MapState, 先引用,放在 compute 中...mapState(['方法名','方法名'])
      • getters

        • 获取数据的。
        • this.$store.getters.xxx。
        • 也可使用mapGetters 先引用,放在compute中,...mapGetters(['方法名','方法名'])
      • mutations

        • 同步操作数据的。
        • this.$store.commit(“方法名”,数据)。
        • 也可使用mapMutations ,使用方法和以上一样。
      • actions

        • 异步操作数据的。
        • this.$store.dispatch(“方法名”,数据)。
        • 也可使用mapActions ,使用方法和以上一样。
      • modules

        • 板块,里面可以放多个vuex。
  • 父组件通过v-bind:/:传值,子组件通过this.$attrs获取。

    • 父传子

    • 当子组件没有设置props的时候可以使用

    • this.$attrs获取到的是一个对象(所有父组件传过来的集合)

  • 祖先组件使用provide提供数据,子孙组件通过inject注入数据。

  • p a r e n t / parent/ parent/children。

  • refs—$ref。

  • 还有一个,这个网上没有,我自己认为的,我觉得挺对的,slot-scope,本身父组件使用slot插槽是无法获取子组件的数据的,但是使用了 slot-scope 就可以获取到子组件的数据(拥有了子组件的作用域)。

# 14. Vue router有哪几种钩子函数?(中级)

  • beforeEach
    • 参数有:
      • to(Route路由对象)
      • from(Route路由对象)
      • next(function函数) 一定要调用才能进行下一步
  • afterEach
  • beforeRouterLeave

# 15. Vue3 的设计目标

结论:大体上可以概括为:更小、更快、更友好。

在 vue3 之前,我们大致会面临以下几个问题:

1、随着功能的增长,复杂组件的代码变得越来越难以维护

2、缺少一种比较干净的在多个组件之间提取和复用逻辑的机制

3、类型推断不是很友好

4、bundle的时间太久

在 Vue3 中,加入TypeScript支持,提升了 API 设计的一致性、提高了自身的可维护性、开放更多底层功能。

分析

  • 更小:

    1、Vue3 移除一些不常用的 API.

    2、引入 tree-shaking,可以将无用模块“剪辑”,仅打包需要的,使打包的整体体积变小了。

  • 更快:

    主要体现在编译方面:

    1、diff 算法优化

    2、静态提升

    3、事件监听缓存

    4、SSR 优化

  • 更友好

    1、vue3 在兼顾 vue2options API的同时还推出了composition API,大大增加了代码的逻辑组织和代码复用能力。

    2、基于TypeScript编写,可以享受自动的类型定义提示。

# 16. Vue3 做了哪些优化

大体上可分为三个方面:源码、性能、语法 API.

  • 源码:

    1、源码管理

    通过monorepo方式对源码进行维护,根据功能,将不同的模块进行拆分,放到对应的package目录下面的子目录中。这样使得模块拆分更细化,职责划分更明确,模块之间的依赖关系也更加明确,开发人员也更容易阅读、理解和更改所有模块源码,提高代码的可维护性。

    2、TypeScript

    Vue3 是基于TypeScript编写的,提供了更好的类型检查,能支持复杂的类型推导。

  • 性能:

    主要通过体积优化编译优化数据劫持优化

    在 vue2 中,数据劫持是通过 Object.defineProperty,这个 API 有一些缺陷,并不能检测对象属性的添加和删除。在面对嵌套层级比较深的情况下,存在性能问题。

    相比之下,vue3 是通过 proxy 监听整个对象,那么对于删除还是监听当然也能监听到。

    同时 Proxy 并不能监听到内部深层次的对象变化,而 Vue3 的处理方式是在 getter 中去递归响应式,这样的好处是真正访问到的内部对象才会变成响应式,而不是无脑递归。

  • 语法 API:

    Vue3 中提供了Composition API,优化逻辑组织和逻辑复用。

    在 vue2 中,我们是通过mixin实现功能混合,如果多个 mixin 混合,会存在两个非常明显的问题:命名冲突数据来源不清晰

    而通过Composition API这种形式,可以将一些复用的代码抽离出来作为一个函数,只要的使用的地方直接进行调用即可。

# 17. Vue3 性能提升主要是通过哪几方面体现的

  • 编译阶段

    1、diff 算法优化

    vue3 在 diff 算法中相比 vue2 增加了静态标记

    关于这个静态标记,其作用是在会发生变化的地方添加一个flag 标记,下次发生变化的时候直接找该地方进行比较。

    2、静态提升

    Vue3 中对不参与更新的元素,会做静态提升,只会被创建一次,在渲染时直接复用。

    这样就免去了重复的创建节点,大型应用会受益于这个改动,免去了重复的创建操作,优化了运行时候的内存占用。

    3、事件监听缓存

    默认情况下绑定事件行为会被视为动态绑定,所以每次都会去追踪它的变化,开启了缓存后,将失去静态标记。也就是说下次 diff 算法的时候直接使用

    4、SSR 优化

    当静态内容大到一定量级时候,会用createStaticVNode方法在客户端去生成一个static node,这些静态 node,会被直接 innerHtml,就不需要创建对象,然后根据对象渲染。

  • 源码体积

    相比 Vue2,Vue3 整体体积变小了,除了移除一些不常用的 API,再重要的是Tree shanking.

    任何一个函数,如 ref、reavtived、computed 等,仅仅在用到的时候才打包,没用到的模块都被摇掉,打包的整体体积变小。

  • 响应式系统

    vue2 中采用 defineProperty 来劫持整个对象,然后进行深度遍历所有属性,给每个属性添加 getter 和 setter,实现响应式。

    vue3 采用 proxy 重写了响应式系统,因为 proxy 可以对整个对象进行监听,所以不需要深度遍历。

# 18. Vue3 所采用的 Composition Api 与 Vue2 使用的 Options Api 有什么不同?

  • Options API

    Options API,即大家常说的选项 API,即以 vue 为后缀的文件,通过定义 methods,computed,watch,data 等属性与方法,共同处理页面逻辑。

    当组件变得复杂,导致对应属性的列表也会增长,这可能会导致组件难以阅读和理解。

  • Composition Api

    在 Vue3 Composition API 中,组件根据逻辑功能来组织的,一个功能所定义的所有 API 会放在一起(更加的高内聚,低耦合)。

    即使项目很大,功能很多,我们都能快速的定位到这个功能所用到的所有 API。

  • 对比

    1、逻辑组织

    Options API 中,假设一个组件是一个大型组件,其内部有很多处理逻辑关注点。选项的分离掩盖了潜在的逻辑问题。此外,在处理单个逻辑关注点时,我们必须不断地跳转相关代码的选项块。

    Composition API 中,逻辑关注点相关的代码全都放在一个函数里,这样当需要修改一个功能时,就不再需要在文件中跳来跳去。

    2、逻辑复用

    在 Vue2 中,我们是用过 mixin 去复用相同的逻辑,使用单个 mixin 似乎问题不大,但是当我们一个组件混入大量不同的 mixins 的时候,会存在两个非常明显的问题:命名冲突、数据来源不清晰。

    Composition API 中,可将逻辑抽离出去,整个数据来源清晰,即使去编写更多的 hook 函数,也不会出现命名冲突的问题。

  • 总结

    1、在逻辑组织和逻辑复用方面,Composition API 是优于 Options API

    2、因为 Composition API 几乎是函数,会有更好的类型推断。

    3、Composition APItree-shaking 友好,代码也更容易压缩。

    4、Composition API 中见不到 this 的使用,减少了 this 指向不明的情况。

    5、如果是小型组件,可以继续使用 Options API,也是十分友好的。