1.MVVM和MVC模式
- MVVM定义MVVM是Model-View-ViewModel的简写。即模型-视图-视图模型。
- MVC:View通过Controller来和Model联系,Controller是View和Model的协调者,View和Model单向联系。
- MVVM是双向绑定;MVC是单向数据流,极大程度降低了页面与逻辑的耦合。
2.vue2生命周期
beforeCreate
是new Vue()之后触发的第一个钩子,在当前阶段data、methods、computed以及watch上的数据和方法都不能被访问。created
在实例创建完成后发生,当前阶段已经完成了数据观测,也就是可以使用数据,更改数据,在这里更改数据不会触发updated函数。可以做一些初始数据的获取,在当前阶段无法与Dom进行交互,如果非要想,可以通过vm.$nextTick来访问Dom。beforeMount
发生在挂载之前,在这之前template模板已导入渲染函数编译。而当前阶段虚拟Dom已经创建完成,即将开始渲染。在此时也可以对数据进行更改,不会触发updated。mounted
在挂载完成后发生,在当前阶段,真实的Dom挂载完毕,数据完成双向绑定,可以访问到Dom节点,使用$refs属性对Dom进行操作。beforeUpdate
发生在更新之前,也就是响应式数据发生更新,虚拟dom重新渲染之前被触发,你可以在当前阶段进行更改数据,不会造成重渲染。updated
发生在更新完成之后,当前阶段组件Dom已完成更新。要注意的是避免在此期间更改数据,因为这可能会导致无限循环的更新。beforeDestroy
发生在实例销毁之前,在当前阶段实例完全可以被使用,我们可以在这时进行善后收尾工作,比如清除计时器。destroyed
发生在实例销毁之后,这个时候只剩下了dom空壳。组件已被拆解,数据绑定被卸除,监听被移出,子实例也统统被销毁。
3.vue2父子组件生命周期
- 父组件:beforeCreate -> created -> beforeMount -> 子组件beforeCreate -> 子组件created -> 子组件beforeMount -> 子组件mounted -> 父组件mounted
- 当父组件发生更新时,子组件的生命周期执行顺序为:beforeUpdate -> 子组件beforeUpdate -> 子组件updated -> updated
- 当子组件发生更新时,子组件的生命周期执行顺序为:beforeUpdate -> updated
- 当父组件销毁时,子组件的生命周期执行顺序为:beforeDestroy -> 子组件beforeDestroy -> 子组件destroyed -> destroyed
4.vue3生命周期
- setup() : 开始创建组件之前,在 beforeCreate 和 created 之前执行,创建的是 data 和 method
- onBeforeMount() : 组件挂载到节点上之前执行的函数;
- onMounted() : 组件挂载完成后执行的函数;
- onBeforeUpdate(): 组件更新之前执行的函数;
- onUpdated(): 组件更新完成之后执行的函数;
- onBeforeUnmount(): 组件卸载之前执行的函数;
- onUnmounted(): 组件卸载完成后执行的函数;
- onActivated(): 被包含在 <keep-alive> 中的组件,会多出两个生命周期钩子函数,被激活时执行;
- onDeactivated(): 比如从 A 组件,切换到 B 组件,A 组件消失时执行;
- onErrorCaptured(): 当捕获一个来自子孙组件的异常时激活钩子函数。
5.nextTick作用
vue采用了一种异步更新策略机制,当我们在数据发生变化后立即访问或修改 DOM 元素时,可能会遇到数据已经改变但 DOM 尚未更新的情况。这时,我们可以使用 nextTick
方法来延迟执行代码,确保在 DOM 更新后执行。
由于 Vue 的异步更新策略,当我们在数据发生变化后立即访问或修改 DOM 元素时,可能会遇到数据已经改变但 DOM 尚未更新的情况。这时,我们可以使用 nextTick 方法来延迟执行代码,确保在 DOM 更新后执行。
nextTick 实现依赖于 JavaScript 的事件循环和微任务队,它使用了Promise和 MutationObserver。nextTick 方法接受一个回调函数作为参数,这个回调函数会在 DOM 更新完成后被调用。这样,我们可以在回调函数中安全地访问和修改 DOM 元素。
6.vue中的v-if和v-for为什么不能一起使用
vue2中:v-for 的优先级比 v-if 更高,也就是说在v-if中可以访问到v-for作用域内定义的变量别名 ,因此不会跟vue3一样报错,但并不推荐这么做,原因如下:
性能问题:将 v-for 和 v-if 放在同一个元素上会导致性能下降。Vue 必须为每一个在 v-for 中的项目都检查 v-if 的条件,这会增加不必要的计算量。特别是当 todos 数组很大时,这种性能问题会更加明显。详见文章末尾的附录。
逻辑可读性:从逻辑和可读性的角度来看,将过滤逻辑(v-if)和渲染逻辑(v-for)混合在一起可能会导致代码难以理解和维护。最好是先过滤数据,然后再进行渲染。
vue3中:是因为当它们同时存在于一个节点上时,v-if 比 v-for 的优先级更高。这意味着 v-if 的条件将无法访问到 v-for 作用域内定义的变量别名。 v-if里是无法访问到todo的,这将会报错。
7.vue2和vue3的区别
生命周期
创建前:beforeCreate -> 使用setup()
创建后:created -> 使用setup()
挂载前:beforeMount -> onBeforeMount
挂载后:mounted -> onMounted
更新前:beforeUpdate -> onBeforeUpdate
更新后:updated -> onUpdated
销毁前:beforeDestroy -> onBeforeUnmount
销毁后:destroyed -> onUnmounted
异常捕获:errorCaptured -> onErrorCaptured
被激活:onActivated 被包含在<keep-alive>中的组件,会多出两个生命周期钩子函数。被激活时执行。
切换:onDeactivated 比如从 A 组件,切换到 B 组件,A 组件消失时执行
组合式API和选项式API
在vue2中采用选项式API,将数据和函数集中起来处理,将功能点切割了当逻辑复杂的时候不利于代码阅读。
在vue3中采用组合式API,将同一个功能的代码集中起来处理,使得代码更加有序,有利于代码的书写和维护。
v-if和v-for的优先级
vue2中v-for优先级高,vue3zhongv-if优先级高,具体参考第六条。
双向绑定原理不同
vue2通过Object.definedProperty()的get()和set()来做数据劫持、结合和发布订阅者模式来实现。
vue3通过proxy代理的方式实现。
当属性过多的时候利用Object.definedProperty()要通过遍历的方式监听每一个属性。利用proxy则不需要遍历,会自动监听所有属性,有利于性能的提升
Vue3 的 template 模板支持多个根标签
vue2比vue3快,主要体现在:
1.diff算法的优化
vue2中虚拟dom进行的是全量的对比,vue3采用了静态标记(PatchFlag),对比如果两个结点有不同的地方,就用patch对象存储起来,最后用patch记录的消息去局部的更新dom效率更高。
2.静态提升
vue2中无论元素是否参与更新,都会被重新创建然后渲染,vue3不参与更新的元素只会被创建一次,在渲染的时候直接被复用。
在vue3中使用了tree-shaking技术,自动摇掉了无用的代码,从而减小了打包的体积
vue3使用proxy对象代替了vue2的object.defineProperty,减少了getter、setter的开销,从而提高了渲染性能。
8.路由hash和history的区别
- hash带#,history不带
- 跳转的时候hash只能修改#后面的,history只要是同源的就行
- hash是由onhashchange()来监听事件变化,匹配对应的路由规则,修改不会重新加载页面;history是通过h5新增的APIpushState()、replaceState()和一个事件onpopstate()来监听url变化的,APIpushState()、replaceState()可以用来记录浏览器的历史记录栈,通过forword、back、go对浏览器进行修改,不会向后端发起请求,页面不刷新。
- history必须和后端保持一致,路由全覆盖,不然容易报404
9.vue组件中的data为什么必须是函数
一个组件被复用多次的话,也就会创建多个实例。本质上,这些实例用的都是同一个构造函数
。如果data是对象的话,对象属于引用类型,会影响到所有的实例。所以为了保证组件不同的实例之间data不冲突,data必须是一个函数。
10.watch和computed的区别
- 都是观察数据变化的(相同)
- 计算属性将会混入到 vue 的实例中,所以需要监听自定义变量;watch 监听 data 、props 里面数据的变化;
- computed 有缓存,它依赖的值变了才会重新计算,watch 没有;
- watch 支持异步,computed 不支持;
- watch 是一对多(监听某一个值变化,执行对应操作);computed 是多对一(监听属性依赖于其他属性)
- watch 监听函数接收两个参数,第一个是最新值,第二个是输入之前的值;
- computed 属性是函数时,都有 get 和 set 方法,默认走 get 方法,get 必须有返回值(return)
11.vue中key的作用,为什么不能使用index?
key可以作为元素的标识,让虚拟dom的diff算法更高效。
在diff算法中,如果数组的长度发生变化,会导致key的变化,Vue 会复用错误的旧子节点,做很多额外的工作,不能提高性能。一般用每一项数据的唯一值作为key。
12.vue父子组件通信
-
父子组件通信
父->子
props
,子->父$on、$emit
获取父子组件实例
$parent、$children(vue3中)
Ref
获取实例的方式调用组件的属性或者方法Provide、inject
官方不推荐使用,但是写组件库时很常用 -
兄弟组件通信
Event Bus
实现跨组件通信Vue.prototype.$bus = new Vue
Vuex
-
跨级组件通信
Vuex
$attrs、$listeners
Provide、inject
13.路由守卫
全局路由守卫:beforeEach、beforeResolve、afterEach;
独享路由守卫:beforeEnter
组件内路由守卫:beforeRouteEach、beforeRouteUpdate、beforeRouteLeave
14.Vuex中action和mutation的区别
action一般是放异步方法的,mutation是同步方法。
15.Vue的性能优化有哪些
v-if
和v-for
不一起使用v-for
保证key
的唯一性- 使用
keep-alive
缓存组件 v-if
和v-show
酌情使用- 路由懒加载、异步组件
- 图片懒加载
- 节流防抖
- 第三方模块按需引入
- 服务端与渲染
16.vue路由传参两种方式
- query:导航栏会显示参数,刷新不消失,用path引入
- params:导航栏不会显示参数,刷新消失,用name引入,可以在路由中配置动态参数不消失
17.diff算法
diff算法就是进行虚拟节点对比,并返回一个patch对象,用来存储两个节点不同的地方,最后用patch记录的消息去局部更新Dom。