2023前端开发最新面试题收集-Vue2/3篇

Vue整理

1、谈谈MVVM的理解

  • MVC(react):数据流是单向的,View和Model之间通过controller连接通信,
    • 用户操作会请求服务器,路由拦截分发请求,调用对应的控制器controller,控制器会获取数据,然后数据和模版结合,将结果返回给前端,页面重新渲染。
  • MVVM(Vue):数据流是双向的,View和Model是完全隔离,用户不需要手动操着dom元素,将数据绑定到viewmodel上,会自动将数据渲染到页面,视图发生变化会通知viewmodel层更新数据,viewmodel是MVVM的桥梁。
  • MVVM避免了频繁操作dome,降低性能消耗

 

为什么Vue组件中的Data必须是一个函数:

  • 由于数据是对象,都指向同一个data,当一个组件中修改data时,其他复用组件中的data也改变。
  • 因此使用函数返回对象,这个对象是新对象,挂载在对应的组件实例上,不会相互干扰。

 

2、响应原理

  • vue2 - Object.defineprope rty
    • 实例初始化时遍历data里的对象所有的property
      • 使用defineProperty把这些property全部转成getter/setter,
      • 访问对象的属性时getter函数触发,并通过depend()收集依赖,
      • 修改属性的值时setter函数触发并通过notify()通知watcher。
    • 每个组件实例都对应一个watcher实例,它会在组件渲染的过程把接触过的数据property记录为依赖,之后当依赖项的setter触发时,会通知watcher,从而使它关联的组件重新渲染
    • vue无法检测property的动态添加和移除,因此初始化后仍需添加响应式property,应使用set() 或者 vue.delete()
      • 没有对删除、新增的数据进行 defineProperty 劫持,
      • 模拟proxy, 需要通过 set 、get 递归劫持
    • 特点
      • 无法检测数组元素的新增和删除,需要对数组方法进行重写,无法检测数据长度的修改
      • 无法检测到对象属性的添加或删除
      • 必须遍历对象的每个属性,为每个属性都进行get/set拦截
      • 必须遍历深层嵌套的对象属性
    • vue3 - proxy(代理)
      • vue3中使用ref、reactive来创建响应数据,ref是reflmpl类的实例,在reflmpl类的get中收集依赖,在set出发依赖。而reactive则通过Proxy实现,用reactive创建数据后返回一个proxy代理对象,之后便可以在该代理对象上进行操作,Proxy在代理对象前架设类一层拦截器来控制对象的访问。相比defineProperty只提供对象属性的getter和setter拦截操作,ES6提供了13中拦截操作。
      • ref一般用来包裹基本数据类型,reactive用来包裹引用数据类型,如果用ref包裹引用数据类型,vue还是会将该引用数据先用reactive处理后再由ref处理,而如果用reactive包裹基本数据类型会直接保存,另外reactive默认深层监听。
      • computed 的处理方式和ref基本一致,computed里的参数为函数时,默认为getter函数,且返回一个不可变的响应式ref对象,而参数为一个具有get、set函数的对象时,可用来创建可写的ref对象
      • 特点
        • 不需要对数组的方法进行重载(解决vue2的问题)
        • 可以检测到对象属性的添加和删除,Proxy支持13种操作
        • 针对整个对象,而不是对象的某个属性(浅层,仍需递归)
        • 仍需要将嵌套对象属性进行遍历为响应式
      • 响应式处理发生在生命周期beforeCreate和created之间

2.2、Vue3的性能优化

  • 响应式系统的提升
    • proxy 比 definepoperty 性能更好,直接拦截整个对象,而不需要递归遍历对象
  • composition api 与 options api
    • options api:
      • 包含一个描述组件选项(data、methods、 props)的对象options
      • 开发复杂组件,同个功能逻辑拆分到不同选项中
      • 使用mixin 重用代码,命名冲突,数据源不清晰
    • composition api:
      • 基于函数的api,可以更加灵活的集中组件的逻辑
      • 解决大型项目中拆分和重用的问题
      • 更利于封装复用代码,解决mixin的重名冲突和数据源不清晰的问题
    • 编译优化
      • 标记和提升所有的静态节点,diff只需要比对动态节点
      • 可以多个根节点
      • 静态提升 hoistStatic
      • patch flag 打标动态的元素,追踪其属性的修改,不需要逐个逐层遍历,提到虚拟dom diff的性能
      • 缓存时间处理函数cacheHandler

3、异步更新

只要侦听到数据变化,Vue就会开始一个队列,并缓存在同一个事件循环中发生的所有数据变更。

在缓存时,去除重复的数据,避免不必要的计算和DOM操作是非常重要的。

如果同一个watcher被多次触发,只会被推入到队列中一次,然后在下一个的事件循环中vue刷新队列并执行

 

4、Vue生命周期流程

  • 每个Vue组件实例被创建后都会经过一系列的初始化步骤。如数据观测、模版编译、实例挂载到DOM以及数据变化时同步更新DOM视图,这个过程就叫着生命周期。
  • 分为:创建前后、挂载前后、更新前后、销毁前后
  • 创建vue根实例:vue2:new Vue()  vue3:  Vue.createApp();初始化events和生命周期:vue2: events包括 $on \ $emit \ $listener \ $off
  • beforeCreate
  • 初始化注入和响应式
    • vue2: provide \ inject \ props \ data \ computed \ watch \ methods
    • vue3: ref \ reactive
  1. created  第一次可以使用data \ methods 数据
  2. 判断有没有el选项作为挂在点,没有就使用$mount指定的元素作为挂载点
  3. beforemount  data已经初始化了,此时的$refs还是个空的、vnode挂载前的
  4. 调用render函数,生成虚拟dom,之前的挂载点el元素将被新生成的$el代替掉,$refs就在这一步被解析出来
  5. mounted 第一次可以操作dom , 获取$refs模版引用
  6. 响应式数据发生变化触发beforeUpdate
  7. beforeUpdate  可以拿到更新前的dom/数据,可以在这里继续对数据修改,需知异步更新过程
  8. 重新触发render函数生成新的vnode,与老的vnode进行diff算法比较,且更新到真实dom上
  9. update
  10. 组件卸载或离开页面触发beforeUnmount
  11. beforeUnmount  实例功能完全正常,应当在卸载前移除手动添加的监听器,如clearTimeout
  12. 卸载完成, data \ methods 都不能访问了
  13. unMounted  

 

5、Vue3生命周期的特点

  • 所有的生命周期在setup里面都是用onX导入后使用(因为支持webpack5的tree-shaking)
  • beforeDestroy 改名为 beforeUnmount, destroyed 改名为 unmounted
  • 没有beforeCreate和created,因为setup就是初始化过程
    • setup是先执行,此时组件实例在setup内部已经创建,没必要在setup中使用beforeCreate和created
  • nextTick在dom挂载完成或dom更新完成后立即调用

 

6、虚拟dom、key、diff算法

  • 虚拟dom(vnode)  发生在 beforeMount 中,keep-alive将组件保存在内存中,防止重新渲染dom
    • 将真实的dom节点用js对象的形式表示,当节点改变时,不是直接修改真实dom,而是创建一个新的vnode与旧的vnode进行diff算法比较后,将比较的结果映射到真实dom上,从而提高更新效率。
    • 对于页面首次加载,vnode生成过程时:
      • 模版语法template - 》 抽象语法树/ast树 -》 render函数 -》 虚拟dom。
      • render函数执行的结果是vnode,这一步发生在beforeMount和mounted之间。
      • 而在运行阶段,若响应式数据发生变化,会重新触发render函数生成新的vnode,与老的vnode进行diff算法比较,并更新到真实的dom上。
    • 好处;计算变更,去重操作、减少不必要的dom操作
    • 坏处:页面首次加载是由于需要生成新的vnode,因此会增加渲染时间,无法保留临时状态,如input框输入
  • key
    • 作用:给每个节点做一个标识符,当数据变动时,会生成新的vnode,diff计算比较新老vnode的身份标识
    • 使用:非template标签支持key,且当v-for一起使用时,v-for、key都要写在标签上,vue会自动为v-if提供key
  • diff算法:数据改变 -》 触发render函数 -》生成vnode -》通过映射操作真实dom -》视图更新
    • patch:复用后者更新,条件是类型和值,两者全部相同这复用,类型相同,值不同则更新
      • vue在更新节点首先会比较新旧节点数组的长度,遍历长度短的那个数组,接下来就是分成两种情况:
        • 没有key:使用的是就地更新策略,新旧vnode进行patch,如果两者类型不同,即不可复用,于是删掉旧的vnode并将新的vnode映射到真实dom上,如果两者类型相同,但值不同,就复用旧的vnode并更新旧vnode的新值后映射到真实dom上
        • 有key:先从短的数组头部遍历,判断旧的vnode的类型和key是否相同,如果是则patch上,不是的话就跳出循环,然后从尾部遍历,同样进行新旧vnode的类型和值判断,直到遇到遇到patch上的才跳出循环
          • 如果旧vnode全patch上并且有新vnode未patch的话,则根据新的vnode创建新节点
          • 如果新vnode的全patch上并且还有旧vnode未patch的话,则删除旧节点
          • 如果新旧vnode都没完全patch上,则将新vnode存在map里,以新的vnode的key作为map的键,然后与旧vnode进行映射对比,key能映射上就patch成功,到最后剩余的旧节点卸载掉,新节点创建出来。

7、vue3新增

  • Proxy替代defineProperty实现引用数据的响应式
    • defineProperty针对对象的某个属性,而Proxy针对整个对象,可以检测到对象属性的添加和删除,且Proxy不需要对数组的方法进行重载、省去很多hack
  • Composition API / setup
    • 代码逻辑集中、不像vue2那样分散,适合抽离、分装、复用
  • 支持tree- shaking
    • vue2中定义在vue实例上的API无论有没有使用都会打包进chunk,造成文件体积臃肿。vue3支持webpack的tree-shaking,所有API都需要导入后使用,未导入和导入未使用都会在打包时去除掉,从而减少打包后的体积,减少js加载时间,提高页面渲染速度。
  • 对typescript更好的支持
  • 生命周期不同。
  • 响应式数据定义不同
  • 可以使用多个节点,增加Fragment节点
    • 支持多个根节点,多个根节点时,vue3会创建一个Fragment对其包裹
    • Temoeleport

8、移除vue2

  • vue3移除了 .sync, 用 v-model: 代替
  • vue3移除了 .native 修饰符
  • vue3移除了 $listeners,事件监听器移到了$attrs上
  • vue3移除了 $children
  • vue3移除了 $on、$once、$off,因此eventBus不可使用了
  • vue3移除了 $scopedSlots, 作用域插槽数据已到了$slots里

9、setup 核心

  • vue3可以使用data \ methods,不过当setup和data同时存在时,会取值setup,setup必须有return 返回一个对象。
  • setup有两个参数props和context
    • props是接受父组件传递的参数,是个对象,具有响应式,同步更新父组件的值,因此不能解构取值
      • const { test } = toRefs(props)
    • context是该组件实例上相应值的代理,可以赋值解构 setup(props, { attrs,emit,slots }) { }
      • attrs:是取父组件传值但props没声明接受的值,是个对象
      • emit:调用父组件的方法,emits来接受:emits: ['say']
      • slots: 插槽功能, v-slot:name

10、watch 与 watchEffect

  • watch
    • 有惰性:运行时不会立即执行
    • 更加具体:需要添加监听的属性
    • 可以访问旧值:回调的时候返回新旧值
    • 可配置
      • immediate:是否立即执行
      • deep:是否深度监听,消耗性能,优化:字符串形式监听 'obj.a'
    • 简单数据类型,直接监听变量
    • 引用数据类型,
      • 监听整个对象,使用变量名
      • 监听对象属性,使用函数return,() => obj.name
    • watchEffect
      • 非惰性:一旦运行立即执行
      • 更加抽象:使用时不需要监听特定的值,有变动回调函数内直接运行业务逻辑
      • 不可访问旧值
    • 一般使用watch,避免watchEffect重复触发执行

11、vue2和vue3的重要API

  • vue2
    • data
      • vue在初始化时会为data中的所有数据绑定setter、getter函数,即响应式处理
      • vue中的$和_开头都是有特殊使用意义,因此data中的变量名不能使用这两个开头
    • computed
      • 依赖其他属性计算值,并缓存结果,当所依赖的数据变化时才会重新计算结果,否则直接返回上一次缓存的计算结果
      • 默认getter函数,也可以写成set、get两个函数形式
      • computed {
        
            a:function() {return x;}
        
            b: {
        
                get: function() {},
        
                set: function() {return y;}    
        
            }
        
        }
        
        watch
        监听数据项变化
        
        watch: {
        
            a: () => {}, //不需要this
        
            b: function(newVal, oldVal) {}, //函数写法
        
            c: 'someMethod',// methods选项里定义方法
        
            d: {
        
                handler: function(newVal, oldVal) {},
        
                deep: true,
        
                immediate: true    
        
            },
        
            e: [   // 监听e的改变,并执行多个回调函数
        
                'handle1',
        
                function handle2 (newVal, oldVal) {},
        
                {
        
                    handler: function handle3 (newVal, oldVal) {}        
        
                }    
        
            ],
        
            'e.f': function(newVal, oldVal) {}  //只监听对象里某个属性
        
        }
        
        
        
        
        // 也可以通过API调用
        
        var stop = this.$watch('a'. callback);
        
        stop();  // 取消监听

 

  • methods
    • computed、watch、methods都不可以用箭头函数定义,否则无法把this绑定到vue实例上
    • vue2会遍历methods里面所有方法,调用bind把他们的this帮到实例上,因此不要用箭头函数
  • computed 和 methods 区别:多次调用computed,如果所依赖的数据没有发生改变,则自己读取上一次缓存的计算结果,而methods多次调用多次执行。
  • computed 和 watch 区别:computed只能同步,可缓存,而watch是异步的
  • vue3
    • 支持tree-shaking,因此现在所有的API都需要导入后再使用
      • ref
        • 常用: ref()  isRef()  unRef() toRef() toRefs()
        • toRef是将解构后仍就有响应式,toRefs是批量将解构后仍就有响应
        • toRef和toRefs是将对象的属性提供给外部使用
        • ref的本质也是reactive,ref处理引用数据类型,vue会reactive处理,然后返回给ref;  ref(obj) ~ reactive({value: obj})
        • setup(props, context) {
          
              const person = {
          
                  name: 'zhangsan',
          
                  age: 18    
          
              }
          
              
          
              return {
          
                  person, // template中使用person.name
          
                  name: toRef(person.name), // template直接使用属性name,保持响应式
          
                  ...toRefs(person), // 批量解构后template直接使用person第一层的属性,保持响应式
          
              }
          
          }

 

  • reactive
    • 常用:reactive()  isReactive() shallowReactive()  isProxy() toRaw()
    • readonly: 只读、不可修改
    • computed
    • const a = computed(() => {});//返回一个不可变的响应式ref对象
      
       const b = computed({  // 创建可写的ref对象
      
           get: () => {},
      
           set: (newVal) => {}
      
       })
      
      watch
      
      const obj = reactive({ a: {b: 1}});
      
      const e = ref('e');
      
      const f = ref('f');
      
      watch(obj, (newVal, oldVal) => {}); // 监听对象
      
      watch(() => obj.a.b, (newVal, oldVal) = {}); //监听对象里的某一个属性
      
      watch(() => _.cloneDeep(obj), (newVal, oldVal) => {}) // 深度拷贝后监听,确保newVal和oldVal不一样
      
      watch([e, f], (newVal, oldVal) => {}); // 监听多个ref对象,写成数组形式
      
      const stop = watch(obj, () => {});
      
      stop(); // 停止监听

 

  • watchEffect:立即执行传入的一个函数,同时响应式追踪其依赖,并在依赖改变时重新运行该函数
  • const stop = watchEffect(() => {}, {flush: 'post'}); // 对写入回调函数里的所有数据监听
    
    stop() // 停止监听

 

  • flush 表示执行时间
    • pre: 默认值,挂载前或者更新前的数据
    • post:挂载后后者更新后的数据
    • sync: 同一个函数里改变多次值,不建议使用
  • vue2和vue3里的watch区别
    • 如果是监听引用类型数据,深度拷贝才能拿到oldValue,否则新旧值是一样的
    • vue3中监听对象的某个属性,必须写出getter函数返回形式 ()=>obj.a,否则会报错
    • 监听ref和reactive包裹的引用数据类型,默认深度监听,但ref监听的是value而不是ref
  • vue3 为 v-if自动分配 key

12、修饰符

  • 事件修饰符
    • .stop  阻止冒泡
    • .prevent 阻止默认行为
    • .self  自身为目标才出发,不冒泡,不捕获
    • .once 只执行一次
  • v-bind修饰符
    • .sync  
      • vue2是子组件可以修改,同步更新父组件传过来的prop值
      • vue3 使用 v-model: 代替
    • v-model修饰符
      • .lazy   监听change事件而不是input事件
      • .number 输入纯数字转成Number,输入非纯数据转化字符串
      • .trim  去除首尾空格
    • v-on修饰符
      • vue2中,添加了native修饰符对事件监听会添加在自组件根节点上,否则监听移到$listeners上
      • vue3中,移除了native,如果未在自组件的emits中定义,默认添加到自组件根节点上,且以onX形式存进$attrs对象里。因此希望监听自组件内部的原生事件,需要在子组件的emits中定义,否则会触发两次(原生一次,手动emit一次)

13、插槽

  • 定义:子组件用<slot>占位且可为该插槽提供备用内容,父组件负责分发<slot>里的内容,利用插槽可以实现组件复用
  • 缩写:#name   默认 v-slot=“default”可以缩写v-slot, 也可以缩写 #default
  • 分类
    • 默认插槽:v-slot=“default”  v-slot  #default  , 也可以不写即默认
    • 具名插槽: v-slot="test''   #test
    • 作用域插槽: 父组件的数据来自于子组件的数据,子组件设置 v-slot: xx = "yy",将yy传给父组件,父组件 v-slot=“scoped”  {{ scoped }}

14、Vue3中$attrs、ref、class\style

  • $attrs包含父组件中除去props和自定义事件外的所有属性集合
  • 父组件中访问子组件的方法,ref="childRef"
    • 需要在子组件中使用 defineExpose 的方法暴露出属性和方法才可以
  • class、style:vue2只能挂在子组件的根节点上,不能改变位置。vue3里,已经移到$attrs对象里,因此可以设置并调用指定的挂载位置

15、组件间通信

  • props、emit (父子)
    • props:父组件传值子组件,可设置type、require、default,只在子组件中修改推荐 computed
    • emits:可在emits对触发的事件进行验证,vue3所有未在emits定义的事件都会被存入组件的$attrs中,并绑定在组件的根节点上
  • .sync 子组件可以修改父组件数据,通过 $emit( "update.page", value ) 属性
  • $refs 获取组件的实例,父组件可调用子组件的方法和属性
    • 只能在组件DOM渲染之后生效,不是响应式的,避免在计算属性中访问
    • 在生命周期mouted 或者 nextTick 中调用
    • v-for的话,获取的是一个数组
  • $parent / $children
    • $parent 子组件可以获取父组件实例对象数组,从而访问父组件中数据和方法
    • $childre 父组件可以获取子组件实例对象数组,从而访问到子组件中数据和方法
  • $attrs / $listeners vue3已移除
    • $attrs 可获取子组件中除了class 和 style 外的非 props 属性的集合,如果继续向下传递 v-bind="$attrs"
    • $listeners 可以监听父作用域内除 .native 外的 监听时间集合,如果继续向下传递 v-bind="$listeners"
  • provide / inject  
    • 依赖注入,常见插件或者组件库,多个组件嵌套,
    • 顶层provide提供变量或者方法,后代组件通过inject来注入变量,
    • 缺点:
      • 传递数据不是响应式,作为默认常量。
    • $root 只可以访问根组件中的属性和方法
    • eventBus 中央事件总线(所有组件间) vue3 已移除
    • slot 插槽,子组件将数据传递给父组件template中
    • Vuex (所有组件间)
    • LocalStorage / SessionStorage  (所有组件间)持久化存储

16、内置组件、动态组件、异步组件

  • 内置组件:<slot>  <keep-alive>  <transition>  <transition-group>  <teleport>  <router-link>   <router-view>
    • keep-alive  在内存不会进行销毁
      • include、exclude、max、min
      • keep-alive组件独享的生命周期钩子: actived(从内存缓存中读取并渲染时执行) 、deactivated(缓存到内存中时执行)
    • 动态组件 :不分打包,打包进父组件js文件里
      • <template :is="curComponent"></template>
    • 异步组件:import引入,分包操作,使用/**/来定义分包的名称
    • // vue2 组件懒加载
      
      const asyncModal = () => import('../model.vue');
      
      const asyncModal = {
      
          component: () => import('../model.vue');  // component
      
          delay: 200,
      
          timeout: 3000.
      
          error: ErrorComponent, // 引入都模版
      
          loading: LoadingComponent
      
      }
      
      
      
      
      // vue3
      
      import { defineAsyncComponent } from 'vue'
      
      const asyncModel = defineAsyncComponent(() => import('../model.vue')})
      
      const asyncModel = defineAsyncComponent({
      
          loader: () => import('../model.vue'),  // loader
      
          delay: 200,
      
          timeout: 3000,]
      
          errorComponent: ErrorComponent, // 引入都模版
      
          loadingComponent: LoadingComponent
      
      })

 

17、自定义指令

  • 提供操作dom的一种途径,且可封装、复用
  • 含义: v-aa:bb.cc="dd"   aa为自定义指令名称,bb为参数,cc为修饰符,dd为值

18、过度与动画

  • v-enter-from    v-enter-to    v-leave-from   v-leave-to

19、其他知识点

  • 响应式发生在(beforeCreate-created \ setup)初始化阶段的data、computed,之后无法添加根级别响应式数据,除非Vue2的set(),Vue3在setup里无法访问vue实例;
  • data、computed可以访问props,反过来不行。因为先initProp,然后是initData,再是initComputed,因此computed可以访问data
  • 区分哪些是响应式数据,哪些不是响应式数据
    • vue3: isRef  \  isReactive  \ isReadonly   \  isProxy 判断是否是reactive或readonly创建
    • vue2:
      • 当vue组件的实例初始化的时候已有的数据就是响应式,否则不是
      • 通过definePorperty代理实例this身上
      • 响应式的值改变会触发视图更新
    • 自定义事件emit事件如果带有参数就会覆盖掉$event
    • vue2的SFC文件中的name选项作用:动态组件keep-alive、递归组件、vue-devtool
    • 因为setup是围绕beforeCreate和created生命周期钩子运行,所以不需要显示的定义它们,即可以在setup中直接编任何代码
    • "create app.$el and append it to el":vue会先在内存中创建编译好的模版,然后再挂载到页面html上。
    • 不能在defineProperty  get/set里直接return obj.a,而是返回监听的属性,return a; 因为会无限循环调用下去直到栈溢出
    • vue中删除数组里面的元素依旧能触发响应式,通过delete  \  set,不可用delete xx;
    • 输入框实时搜索应该怎么优化:
      • .lazy  该修饰符是当输入停止输入、失去焦点或按下enter,才会触发change,更新v-model
      • 防抖 delay='200'
    • 多个页面如何通讯
      • localStorage
      • cookie
      • vuex
    • 父子组件生命周期执行顺序
      • 父beforeCreate -> 父created -> 父beforeMount -> 子beforeCreate -> 子created -> 子beforeMount -> 子 Mounted -> 父Mounted
      • 更新:
        • 子组件更新:父beforeUpdate -> 子beforeUpdate -> 子updated -> 父updated
        • 父组件更新:
          • 影响到子组件:父beforeUpdate -> 子beforeUpdate -> 子updated -> 父updated
          • 不影响到子组件:父beforeUpdate -> 父updated
        • 销毁:父beforeDestory -> 子beforeDestory -> 子destory -> 父destory
        • 如果是异步组件或懒加载组件,即import引入,顺序不一样了
      • created和mounted分别请求数据有什么不同
        • 请求数据一般在初始化created进行,但对于一些需要操作dom的业务,需要使用nextTick里面进行
        • mounted请求数据缺点是可能会导致页面出现空白,直到数据请求成功在展示
      • 如何执行多个并发请求
        • all    premise.all
      • keep-alive原理
        • 会缓存不活动的组件实例,防止重复渲染DOM
        • vue缓存机制并不是直接存储DOM结构,而是将DOM节点抽象成一个个vnode。
        • vue将需要缓存的vnode节点保持到catch中,在触发render时,通过vnode的name符合在缓存条件下,用include和exclude控制,从catch中取出之前缓存的Vnode实例进行渲染
      • Vue路由守卫
        • beforeEach  全局前置路由守卫, 每次切换之前调用,用于页面权限拦截
        • afterEach  全局后置路由守卫,每次切换之后调用,用于切换document.title,仅其没有next参数
        • beforeEnter 独享路由守卫,只有前置,在routes中使用
        • beforeRouterEnter 组件内置路由守卫,进入组件时调用
        • beforeRouterLeave 组件内置路由守卫,离开组件之时调用
      • vue中this
        • 在vue生命周期里this指向该组件vue的实例
        • data在初始化时,Vue会将data中的属性转换为getter、setter,使data的属性具有响应式,此时如果访问原数据即可以通过vue实例代理映射到data属性,name == data.name
        • methods\computed定义的方法,都可以通过Vue实例来直接访问,即this

20、nextTick原理

  • 让我们在下次DOM更新循环结束之后执行延迟回调
  • 4之前使用microtasks,但是microtasks优先级太高,某些情况会出现事件冒泡问题,所以为了兼容这种情况又补充了macrotasks,默认还是microtasks
  • 对于实现macrostasks,会先判断是否能用setImmediate,如果不能的的话,降级为MessageChannel,以上都不行的话则使用setTimeout
  • nextTick同时还支持promise的使用,会判断是否实现了Promise

21、Vue.mixin 混入,如果有重复函数则覆盖,没有重复则追加

22、权限管理

  • 页面权限和按钮权限
    • 页面权限:如果用户没登录,手动输入地址访问,则会跳到登录页面,如果登陆了,但访问没有权限的页面,则跳404页面
    • 按钮权限:控制按钮或者跳转的展示与否,未确保非常规操作,选择删除而不样式隐藏。
  • 前端方案
    • 优点:降低非法操作,尽可能减少不必要的请求,减轻服务器压力,提高用户体验感
    • 前端权限只是锦上添花,本质还是后端控制,通过cookie、session、token判断该用户权限
    • 原理
      • 菜单权限:用户登录之后服务端返回一条数据,这条数据有菜单列表和token,把这条数据存到vuex中,然后菜单根据vuex中数据进行菜单列表渲染。
        • 权限的数据需要在多个组件之间共享,因此采用Vuex
        • 防止vuex数据刷新丢失,可以将数据存到sessionStorage中,并和Vuex中的数据进行同步处理
      • 页面权限:登陆成功后将token数据存储到sessionStorage中,判断是否登陆
        • 1、全局前置路由守卫,控制页面权限
        • router.beforeEach((to, from, next) => {
          
              if (to.path === './login') {
          
                  next()    
          
              } else {
          
                  const token = sessionStorage.getItem('token')
          
                  if (!token) {
          
                      next('/login')        
          
                  } else {
          
                      next()        
          
                  }
          
              }
          
          })

 

  • 2、动态路由
    • 问题:用户可以访问A,但不能访问B,于是用户手动输入B进行访问
    • 如过少量,可以通过全局前置路由守卫设置
    • 如果大量,使用动态路由
      • 根据用户所拥有的权限数据,来渲染添加所需的路由,这样无法访问的页面是找不到对应的路由,因此跳转404。addRoutes()
    • 按钮权限
      • 将用户无权限的按钮进行隐藏或者禁止,可通过自定义指令实现
      • 根据服务端返回的数据来判断哪些操作时不允许的
      • 自定义 v-permission,directive('permission', {  })

23、Vue scoped style私有化

  • 穿透 /deep/  >>>
  • 涉及到vue-loader处理
    • VueLoaderPlugin处理rule的,让loader能够和文件匹配,然后进行处理,将Vue文件按照template、script、css来拆分代码块。
    • 此时会根据文件的路径和文件内容生成hash值,并赋值给id。
    • 对于style代码块,vue-loader会在css-loader前加上stylePostLoad,stylePostLoad是Vue scoped的原理核心之一,它会给每个选择器增加属性值[data-v-hash=i]

24、拦截器intersceptor

  • 作用:主要用于拦截请求并作出相应处理
  • 场景:
    • 登录验证,判断用户是否登录
    • 权限验证,判断用户是否有权访问资源
    • 请求封装,封装统一的请求处理
    • 处理cookie、本地化
  • axios拦截请求封装
    • intersceptors.request.use(config => {})
    • intersceptors.response.use(res => {})

25、跳转传参

  • 在 template 中 router-link中 to="t/id=1" ,通过 $route.query.id 获取
  • $router.push( { path: url, query: {id: 1} } ),参数会带在url里面,刷新不丢失
  • $router.push( { name: 'Test', params: {id: 1} } ),参数不会带在url里面,刷新会丢失参数

26、hash与history

  • 改变router 里面 mode模式
  • history:pushState和replaceState,404需要服务端支持
    • 安装第三方包,npm install --save connect-history-api-fallback

const history = require("connect-history-api-fallback");

app.use(history());

27、Vue项目开发中,遇到的各种痛点问题和解决方案

  • 进入商详页传递参数问题
    • name params 刷新参数不见了,$route.params
    • 使用path query 可以刷新 $route.query
    • 也可以在route中url:“/:id”
  • 本地接口请求跨域问题
    • proxyTable代理服务 target / pathRewrite / changeOrigin(是否跨域) / ws
  • axios封装和api接口统一管理
    • axios的封装,主要来帮我们拦截请求和响应,
    • 统一配置接口公共的参数
    • 对参数进行序列化
    • 统一处理请求响应的状态码
  • 压缩文件太大问题
    • 组件按需加载,import
    • 开启productionGzip: true
    • webpack 提取公共代码、拆分代码配置
  • 修改UI框架样式问题
    • /deep/ 或者 >>>
  • 定时器在页面跳转后还在进行问题
    • 在组件中beforeDestory中进行清除,实例化保存了定时器,不太好
    • 或者在$once('hook:beforeDestroy', () => {}) ,后在beforeDestroy清除,
  • 双击延迟问题
    • fastClick安装, 导入后 attach(document.body)
  • 性能分析工具问题
    • hiper 可以看到DNS查询耗时,TCP链接好事,页面下载耗时,DOM Ready耗时等

28、动态路由:动态加载

  • 将路由存储到前端
  • 将路由存储到后端,更加安全,推荐后端配置和权限控制结合
    • Vuex配合storage来保持数据持久存储
    • 前端可以定义静态路由,如:登录页、忘记密码、404页面
    • 动态路由在全局前置路由守卫beforeEach中拦截并处理
      • 遍历解析,读取动态路由中的 children 、component的路径
      • 通过addRoute(v) 动态添加进去
    • 最后 将 动态路由存到vuex和storage中
    • accessRouteses.forEach(v => {
      
          v.children = routerChildren(v.children);
      
          v.component = routerCom(v.component)  //component的路径
      
          router.addRoute(v)
      
      })
      
      
      
      
      // 动态加载组件
      
      function routerCom(path) {
      
          // return () => import('@/views/${path}')
      
          return (resolve) => requie([`@/views/${path}`], resolve)
      
      }
      
      
      
      
      // 递归循环
      
      function routerChildren(children) {
      
          children.forEach(v => {
      
              v.component.routeCom(v.component)
      
              if (v.children != undefined) {
      
                  v.children = routerChildren(v.children)        
      
              }
      
          })
      
      }

 

29、History 和 Hash 区别

  • history
    • 利用 pushstate() 和 replacestate()
    • 需要服务器配合,设置对应的url,不然会返回404
  • Hash
    • url中有锚点 # ,依靠 onhashchange 事件,监听 hash变化
    • 不会被放进请求的http中,因此改变hash不会重新加载页面,因此不能在SSR中使用Hash
posted @ 2023-02-17 10:22  浪里小韭菜  阅读(3428)  评论(0编辑  收藏  举报