vue面试面试

MVVM
ViewModel是一个与View和Model交互的中间层,它通过双向绑定机制将View的变化及时反映到Model中,同时将Model的数据变化同步到View中。

双向绑定
(1) 定义: 数据变化视图会自动更新,视图变化数据也会更新
(2) 原理: 数据劫持 + 发布订阅者模式
(3) 实现:采用Object.defineProperty()对数据进行劫持来监听数据变化,并通过getter/setter方法对数据进行读写。其次vue通过发布订阅者模式,维护了一个订阅者数组,当数据发生改变时,vue会通知所有订阅者进行更新,因此当用户在页面上进行修改时。Vue会更新对应的数据,并通知所有订阅者更新视图,同时当数据发生变化时,Vue也会更新对应视图,通过这样的机制,Vue实现了双向绑定
(4) 订阅者是一个概念,它是用于管理更新视图的对象,当数据发生变化时,Vue会通知所有的订阅者进行更新。每一个挂载到视图上的组件或者每一个watcher都可以被看作是一个订阅者,他们订阅了某一个数据的变化,并等待数据发生变化时进行更新,订阅者是Vue实现双向数据绑定的关键组成部分

数据代理
通过一个对象obj2代理对另一个对象obj1中属性x的操作,在vue中,就是把_data中的属性直接放到vm上,是通过vm对象来代理data对象中的属性,好处就是方便操作data

v-model、v-bind
1、v-bind是单向绑定,用来绑定数据和属性以及表达式,数据只能从data流向页面。:
2、v-model是双向绑定,数据能从data流向页面,也能从页面流向data。
3、v-bind可以给任何属性赋值(包括绑定class样式),v-model只能给表单类,也就是具有value属性的元素进行数据双向绑定,注意,v-model不能修饰表达式

compute、watch
compute:有缓存,不用多次读取,所依赖的数据变化时才重新执行计算属性,method没有缓存要多次读取
watch:监视属性变化时,回调函数handler自动调用,监视属性必须存在,可以开启深度监视
区别:计算属性不能开启异步任务,监视属性可以开启异步任务

绑定class和style样式
用:class   :style绑定样式  数组写法,对象写法,字符串写法

v-if v-show
v-if 指令会动态地创建或移除 DOM 元素
v-show display:none 控制元素的显示与隐藏
v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此:
如果需要非常频繁地切换,则使用 v-show 较好
如果在运行时条件很少改变,则使用 v-if 较好

v-for key  v-for="(p,index) in person" :key="index/p.id"v-for="(val,k) in object"   可以遍历数组对象字符串
会出现的问题:key是vue内部在用的,用index作为key会错乱,用p.id唯一标识不会乱,这是虚拟dom在对比时数组顺序打乱,就出现顺序错乱的问题,没有key会自动用index作为key则顺序可能会乱并开销很大

虚拟DOM:提高性能:通过在DOM树中创建、更新和删除虚拟节点,而不是直接操作真实DOM,可以减少浏览器进行重绘和重排的次数,从而提高应用程序的性能和响应速度。方便组件化开发;创建真实DOM成本较高,如果用js对象来描述一个DOM节点,创建的成本相对较低。

diff算法

数据改变=》setter=》Dep.notify=》watcher调用patch(old,new)=》更新

Dep维护了一个观察者数组,数据改变了就调用notify方法通知所有watcher更新,watcher就调用patch函数

patch操作:两个参数old,new

没有新节点,则直接调用 destory 销毁节点;

没有旧节点,代表新建,直接调用 createElem 创建节点;

旧节点和新节点自身一致,进行 sameVnode 判断,一样时调用 patchVnode;

旧节点和新节点自身不一致,创建新节点,删除旧节点;

key的原理:初始数据生成虚拟dom,虚拟dom转为真实dom,用户在真实dom中输入,数据进行更新后,根据新数据生成新虚拟dom,新虚拟dom和之前的真实dom进行对比,一一对比key相同,值不相同则更新,值相同则不更新,对比过后转成新真实dom

vue2响应式问题
this.arr[0]="***"这种修改方式vue监测不到
监测数据的原理:vue在做数据代理之前,加工了data,加了个observer对象,对数据进行getter,setter,深度监视,vue中有set方法,就是方便开发者有新的数据需求的时候也能响应也有gettersetter vm.$set(),Vue.set();vue监测数组时,数组里的数据是没有gettersetter的所以改数组的数据时可能会监测不到,vue中有7个数组方法可以被监视到pop push shift unshift splice sort reverse,vue底层将这些方法包装了一下不是Array原型对象上的方法

判断对象属性是否是响应式

Vue.util.hasREactiveProp(),两个参数,第一个参数对象,第二个参数属性。返回布尔值

生命周期
beforeCreate    在组件实例被创建之初、组件的属性⽣效之前被调用
created           在组件实例已创建完毕。此时属性也已绑定,但真实 DOM 还未⽣成,$el 还不可⽤
beforeMount    在组件挂载开始之前被调⽤。相关的 render 函数⾸次被调⽤
mounted        在 el 被新建的 vm.$el 替换并挂载到实例上之后被调用
beforeUpdate    在组件数据更新之前调⽤。发⽣在虚拟 DOM 打补丁之前
updated                    在组件数据更新之后被调用
beforeDestory    在组件销毁前调⽤
destoryed    在组件销毁后调⽤,组件自定义事件不生效了,原生dom事件还是生效但无响应式

activited    在组件被激活时调⽤(使用了 <keep-alive> 的情况下)
deactivated    在组件失活时调⽤(使用了 <keep-alive> 的情况下)

uniapp生命周期

页面初次渲染完成onReady、页面显示onShow、页面隐藏onHide、页面被卸载onUnload、页面滚动onPageScroll、标题栏按钮点击onNavigationBarButtonTap、分享onShareAppMessage、收藏onAddToFavorites等

父子生命周期顺序

Vue收集依赖的时间点通常是在组件初始化的时候。在Vue的生命周期钩子函数beforeCreate和created中,会进行依赖收集。此时,所有的响应式数据都还没有被初始化,因此在这个阶段进行依赖收集可以确保在数据被初始化之前已经收集到依赖关系。

在Vue的构造函数中,执行了_init()方法,首先初始化绑定事件和生命周期钩子,然后调用beforeCreate这个钩子函数。在这个钩子函数中,还没有初始化数据,所以在这个钩子函数中一般不进行操作。紧接着进行props、data、methods、computed、watch等的初始化,这个过程中已经将数据转换为了响应式数据。

VueComponent.prototype.__proto__===Vue.prototype

残缺版vue不包含模板解析器,所以用render来完成

nextTick

在下一次dom更新结束后执行回调,例如可以获取input焦点this.$nextTick(function(){})

先看微任务不行再看宏任务

组件通信

父组件给子组件传方法,形参为子值,子组件props接收这个函数把值给父组件

父组件给子组件v-on绑定自定义事件,子组件中用$emit触法这个自定义事件并传值给父组件

父组件ref获取子组件用this.$refs.son.$on()绑定自定义事件然后做上一步,$off解绑事件

new Vue的beforcreate中定义全局事件总线Vue.prototype.$bus=this,this.$bus.$on()。。。

消息订阅与发布pubsub.js用库实现,类似全局事件总线的方式写

webstorage,window,vuex

.sync v-model

$parent / $children 通过$parent和$children就可以访问组件的实例

slot
默认插槽:在组件中放一个h5标签时例如img,放一个<slot></slot>;                                           具名插槽:当有多个插槽时,slot属性name="***",img属性slot="***";                                        作用域插槽:子传父,父中写<template scope="data">h5标签</template>

Vuex
Vue的状态管理工具,Vuex就是把组件共享状态抽取出来以一个全局单例模式管理,把共享的数据函数放进Vuex中,任何组件都可以进行使用,通过Vuex可以在多个组件之间共享和持久化状态。
state(数据)    getters(类似compute)
commit==》actions(异步方法,最终通过mutation间接修改)      dispatch==》mutations(操作state的方法)   
mapstate mapgetters mapmutations mapactions 映射数据和方法

action处理异步函数可以在devtools中追踪数据变化,而mutation只可以处理同步函数,当处理异步函数的时候检测不到数据变化

vue-router
前端路由的核心,就在于改变视图的同时不会向后端发出请求;而是加载路由对应的组件。vue-router是vue的插件,专门实现SPA应用,它组件映射到路由, 然后渲染出来的。

路由懒加载

// 相当于组件动态导入,将
// import UserDetails from './views/UserDetails.vue'
// 替换成
const UserDetails = () => import('./views/UserDetails.vue')
const router = createRouter({
  // ...
  routes: [{ path: '/users/:id', component: UserDetails }],
})

鉴权

常用的鉴权有两种:一种是路由拦截,一种是动态路由。


const router = new Router({
  routes: [
    {
      path: '/dashboard',
      name: 'dashboard',
      component: Dashboard,
      meta: { requiresAuth: true } // 添加 meta 字段,用于鉴权判断
    },
  ]

拦截

// 守卫
router.beforeEach((to, from, next) => {
  // 1. 判断是不是登录页面
  // 是登录页面
  if(to.path === '/login') {
    next()
  } else {
    // 不是登录页面
    // 2. 判断 是否登录过
    let token = localStorage.getItem('token')
    token ? next() : next('/login')
  }
})

登录流程

点击登录,账号和密码调用后端的登录接口,后端比对,比对错误,返回错误信息给前端

比对成功,给前端返回一个token值,前端拿到token存储在localstorage和vuex中并跳转页面登录成功,放在vux中是因为vuex数据存在内存中读取更快且响应式,放在localstorage时是因为防止vux数据丢失可以从这里获取数据,两者结合

前端每次跳转需具备登录状态的页面时,都需要判断token是否存在,如果不存在就跳转到登录页面,存在就正常跳转,利用路由来鉴权

扫码登陆流程

展示二维码,同时不断的向服务端发送请求询问该二维码的状态setInterval;
扫描二维码,读取二维码成功后,跳转至确认登录页,若用户确认登录,则服务器修改二维码状态,并返回用户登录信息;
网页端收到服务器端二维码状态改变,则跳转登录后页面;
若超过一定时间用户未操作,网页端二维码失效,需要重新刷新生成新的二维码。

keep-alive是Vue中内置的一个抽象组件。它自身不会渲染一个 DOM 元素,也不会出现在父组件链中。当它包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。

路由模式:Hash模式、History模式。默认Hash模式
hash模式 : #,前端访问,不经过服务器 window.location.hash 浏览器通过监听hashchange事件来响应路由的变化。
history模式:/,后端访问,History 模式的原理是使用 HTML5 History API 中的 pushState 和 replaceState 方法,它们可以在不刷新页面的情况下修改浏览器的历史记录和 URL

// hash原理
window.addEventListener('hashchange', myFunction);
function myFunction(e) {
  console.log(e.oldURL);
  console.log(e.newURL);
}
window.location.hash = 'part2';
// history原理
window.history.pushState(null,null,'2.html')