VUE要点—— 面试题总结3.13

98 阅读6分钟

1、什么是MVVM ?

是一种设计模式, Model(数据层) View(视图层) ViewModel(视图数据控制层)

数据变化 => ViewModel监听到 => 更新视图 视图变化 => ViewModel监听到 => 更新数据

双向数据绑定

  1. 双向数据绑定的原理 数据变化的监听? Vue2 Object.defineProperty Vue3 proxy

  2. 视图变化的监听? 给元素注册事件监听 @change @input ...

    问: Object.defineProperty 和 proxy的差异?

    Object.defineProperty:

    (1) Object.defineProperty 是对于对象属性的劫持, 需要一个个递归劫持, 效率低

    (2) Object.defineProperty 对于数组的更新检测, 比较麻烦

    prxoy: proxy对于整个对象的劫持, 对象内部的任何属性的修改, 都会先经过外层proxy, 无需递归, 效率高 proxy也可以直接监听数组的变化

  3. Vue的响应式系统 => 观察者模式 监听到, 更新谁? 如何运作的? Vue采用的是观察者模式, 以Vue2为例, 通过 Object.defineProperty 完成对于数据的劫持 通过观察者模式, 完成对于节点的数据更新

    观察者模式 :

    是一种 一对多的 设计模式, 一个对象变化了, 其他所有依赖的, 跟着变 一上来 vue 解析渲染, 会进行依赖收集, 会将渲染watcher, 计算属性watcher, 侦听器watcher, 都会收集到对应 dep 中 (依赖收集完成)

    当 Object.defineProperty 监听到数据变化, 就会根据依赖关系, 派发更新, 每个watcher侦听器 都是一个对象, 都有着callback回调, 执行回调 (调用函数, 派发更新)

2、Vue生命周期

  1. 开始构建
  2. 初始化数据
  3. 编译模板
  4. 挂载dom
  5. 渲染 / 更新
  6. 卸载
8大钩子 Vue2                       Vue3
beforeCreate                      setup
created                           setup
beforeMount                       onBeforeMount
mounted                           onMounted
beforeUpdate                      onBeforeUpdate
updated                           onUpdated
beforeDestroy                     onBeforeUnmount
destroyed                         onUnmounted

3、Vue组件通信

  1. 父传子 子传父 (1) 通过 props 父传子, 给组件添加属性传值 - - 组件内部props接收

      (2) 子传父, 利用 $emit 和 @ 注册事件, 实现数据传递**
      
        - 子组件: this.$emit('事件名', 参数)
      
        - 父组件: @事件名="处理函数"
      
          
    
  2. 事件总线 (Vue3被移除)

    通过EventBus进行信息的发布和订阅更新 (1) 创建事件总线 const eventBus = new Vue() export default eventBus (2) 在A组件中, 监听, 订阅变化 eventBus.on(事件,function()...)(3)B组件中,触发bus的事件eventBus.on('事件名', function() { ... }) (3) 在B组件中, 触发 bus 的事件 eventBus.emit('事件名', 参数)

  3. provide inject (Vue3被增强)

    用于父组件向子孙后代组件共享传递数据 Vue2, 其实也有 provide 和 inject, 但是不太好用 Vue3, 大大的增强了 provide 和 inject, 广泛应用

    Vue3: (1) provide('共享的键名', '值') provide('共享的方法名', function() { ... }) (2) let result = inject('共享的键名' , 默认值) let fn = inject('共享的方法名') // 子孙后代, 调用祖辈的方法, 传参更新数据

  4. childrenchildren parent $refs

    children:用于获取模板中的子组件集合children: 用于获取模板中的子组件集合 children[0] 第一个组件 parent:用于获取父组件parent: 用于获取父组件 refs: 用于配合ref, 获取具体哪一个组件

  5. attrsattrs listeners

    组件二次封装用到 => 需要进行 批量 的跨层级数据传递 (使用起来, 就像父传子, 子传父一样) v-bind="attrs"批量获取props,批量往下传递von="attrs" 批量获取props, 批量往下传递 v-on="listeners" 批量监听事件, 批量向上触发

4、Vuex 5个核心概念:

(1) state: 存放数据

(2) mutations: 存放操作数据的方法

(3) actions: 存放一些异步操作, 注意: actions 不可以直接操作state, 需要提交mutation

(4) getters: 计算属性

(5) modules: 分模块

同步情况: 页面中 commit('模块名/某mutation', '参数')

异步情况: 页面中 dispatch('模块名/某action', '参数')

​ action中 commit('模块名/某mutation', '参数')

1.pinia (Vue3) 官方推荐的状态库 将来Vuex5的语法, 就会和pinia的语法类似 核心概念: (1) state: 存数据 data

 ```
     state () {
        return {
           count: 100,
           money: 1000
        }
     }
 ```
 
 
 
 

 **(2) actions: 存放方法, 既可以同步, 也可以异步, 且可以直接操作状态   methods**

 ```
  actions: {
        addCount () {
           this.count++
        },
        addCountAsync () {
           setTimeout(() => {
              this.count++
           }, 1000)
        },
        async getMoney () {
           const res = await xxApi()
           this.money = res.data.money
        }
     }
 ```



 

 (3) getters: 计算属性
  --------------------------------------------------------------
  完整 pinia仓 库写法: store/count.js 一个文件, 就是一个仓库

 ```
   export const useCounterStore = defineStore('counter', {
        state() {
           return { count: 0 }
        },
        actions: {
           add() {
              this.count++
           },
        },
     })
 ```
 
 
 

 页面中, 无论同步还是异步, 直接导入对应的仓库, 直接调用方法即可
  1. **导入仓库**
     import { useCounterStore } from '@/store/count.js'
     const counter = useCounterStore()

  2. **直接访问使用仓库的数据, 获取调用仓库的方法**
     
     <div>
        {{ counter.count }}
        <button @click="counter.add">按钮</button>
     </div>

5、Vue中key的作用? / React中key的作用?

作用: 给虚拟dom添加标识, 优化复用对比策略, 优化渲染性能

  1. Vue/React的更新机制: 差异化更新, 对比新旧虚拟dom, 找出新旧dom不同的部分, 进行更新视图

  2. 为什么对比虚拟dom? 不对比真实dom?

    真实dom的结构, 太复杂, 有着很多无用的属性, 无需对比, 如果对比了浪费性能

    虚拟dom, 是一个js对象, 通过js对象, 描述真实的dom, 只记录关键属性, 对比起来性能更高

  3. 就算是进行了虚拟dom的优化, 但是dom树, 是树形结构! 虚拟dom树, 也是树形结构, 树形结构的对比? => diff算法

    diff算法策略:

    (1) tree diff 同层比较, 同层对比, 如果发现根级别的元素类型不同, 直接整棵树销毁重建

    (2) component diff 同层级别元素类型相同, 按照策略, 对比差异, 记录差异

    (3) element diff 子节点列表, 同层兄弟节点, 默认按照下标进行对比, 如果加上key, 就相当于给虚拟dom加上了标识

    对比策略: 就是对相同key的元素, 进行比较

子节点列表, 顺序下标, 会发生变化时, 推荐都加上key, 便于正确复用! (列表 v-for 往列表前面加, 中间加)

6、Vue 跳转路由时的传参方式 (Vue2 / Vue3)

3种 => query传参, params传参, 动态路由传参

  1. query传参: 会在地址栏显示, 刷新不会丢失

    router.push('/login?username=zs&age=18')
    router.push({
       path: '/login',
       query: {
          username: 'zs'
          age: 18
    }
    })
    
    获取: route.query.username
    
  2. params传参: 内存中传递, 不显示地址栏, 刷新会丢失, 必须配合命名路由使用 如果要处理刷新会丢失的问题, 需要配合 localStorage

      { path: '/login', name: 'login', component: () => import('@/views/login/index.vue') }
      
      router.push({
         name: 'login',
         params: {
            car: '小车车'
         }
})
      获取: route.params.car
      ```
      
      
      
      
 3. 动态路由传参: 地址栏传递, 显示在地址栏, 刷新不会丢失, 必须配动态路

  { path: '/user/:id', component: () => import('@/views/user/index.vue') }
  
  router.push({
   path: '/user/92377447',
  })
  
  获取: route.params.id
  
  
  

7、Vue权限控制

RBAC: role based access control 基于角色的权限控制 给用户分配角色, 给角色分配权限

核心点:

  1. 按钮权限控制 / 表单 / 表格 登录成功时, 获取用户的权限相关信息, 角色roles: [{ xx }, { xx }] 按钮规则btnRules: [{ xx }, { xx }] 根据返回的权限信息, 调用封装好的函数, 判断按钮, 是否需要禁用启用 / 显示隐藏

  2. 菜单权限控制 (1) 登录成功时, 获取用户的权限相关信息, menus: ['xx', 'xx', 'xx']

      [         {            "moduleGroupId": 1001,            "moduleGroupName": "部署管理",            "requestMapping": "deploy-manage",         },         {            "moduleGroupId": 1100,            "moduleGroupName": "系统管理",            "requestMapping": "sys-manage",            "moduleList": [               {                  "moduleId": 1101,                  "moduleName": "系统日志",                  "requestMapping": "system-log",                  "moduleGroupId": 1100,               },               {                  "moduleId": 1102,                  "moduleName": "系统告警",                  "requestMapping": "sys-alert",                  "moduleGroupId": 1100,               },            ],
             }
          ]
    

    (2) 将路由拆分成, 动态路由和静态路由, 动态路由需要权限才能访问的路由, 静态路由默认即可访问的路由 (3) 用户一登录成功, 会动态的新增一些动态路由 addRoutes, 此时不同用户的路由已经不一样了 (4) 但是为了菜单的响应式展示, 动态使用 vuex 维护路由数组

  3. api接口url权限控制: 后端控制多一点

8、首屏渲染优化

1)网站性能优化实战——从12.67s到1.06s的故事 (首屏优化) 量化数据, 更有说服力 juejin.cn/post/684490…

  1. 移动spa商城优化记(一)---首屏优化篇 (首屏优化) juejin.cn/post/684490…

  2. 前端黑科技:美团网页首帧优化实践 (白屏问题优化) juejin.cn/post/684490…