1、介绍一下MVC MVVM
MVVM
Model(模型)
模型是指代表真实状态内容的领域模型(面向对象),或指代表内容的数据访问层(以数据为中
心)。
View(视图)
就像在MVC和MVP模式中一样,视图是用户在屏幕上看到的结构、布局和外观(UI)。
ViewModel(视图模型)
视图模型是暴露公共属性和命令的视图的抽象。MVVM没有MVC模式的控制器,也没有MVP模式的
presenter,有的是一个绑定器。在视图模型中,绑定器在视图和数据绑定器之间进行通信。
优点:
低耦合 :View可以独立于Model变化和修改,一个ViewModel可以绑定到不同的View上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变。
可重用性 : 可以把一些视图逻辑放在一个ViewModel里面,让很多View重用这段视图逻辑。
独立开发 : 开发人员可以专注于业务逻辑和数据的开发,设计人员可以专注于页面的设计。MVC
MVC分为:Model(模型),View(视图),Controller(控制器)。 这主要是基于分层的目的,让彼此的职责分开.View一般用过Controller来和Model进行联系。Controller是Model和View的协调者,View和Model不直接联系。基本都是单向联系。M和V指的意思和MVVM中的M和V意思一样。C即Controller指的是页面业务逻辑。MVC是单向通信。也就是View跟Model,必须通过Controller来承上启下。
Model(模型)表示应用程序核心(如数据库)。
View(视图)显示效果(HTML页面)。
Controller(控制器)处理输入(业务逻辑)。
MVC 模式同时提供了对 HTML、CSS 和 JavaScript 的完全控制。
Model(模型)是应用程序中用于处理应用程序数据逻辑的部分。 通常模型对象负责在数据库中存取数据。
View(视图)是应用程序中处理数据显示的部分。 通常视图是依据模型数据创建的。
Controller(控制器)是应用程序中处理用户交互的部分。 通常控制器负责从视图读取数据,控制用户输入,并向模型发送数据。
优点:
低耦合
重用性高
生命周期成本低
部署快
可维护性高
有利软件工程化管理
2.Vue组件通讯有哪些方式?
1、props 和 $emit。父组件向子组件传递数据是通过props传递的,子组件传递给父组件是通过$emit触发事件来做到的。
2、$parent 和 $children 获取单签组件的父组件和当前组件的子组件。
3、$attrs 和 $listeners A -> B -> C。Vue2.4开始提供了$attrs和$listeners来解决这个问题。
4、父组件中通过 provide 来提供变量,然后在子组件中通过 inject 来注入变量。(官方不推荐在实际业务中适用,但是写组件库时很常用。)
5、$refs 获取组件实例。
6、envetBus 兄弟组件数据传递,这种情况下可以使用事件总线的方式。
7、vuex 状态管理
3.v-if 和 v-show 的区别
共同点:v-if 和 v-show 都能实现元素的显示隐藏
区别:
1. v-show 只是简单的控制元素的 display 属性,而 v-if 才是条件渲染(条件为真,元素会 被渲染,条件 为假,元素会被销毁);
2. v-show 有更高的首次渲染开销,而 v-if 的首次渲染开销要小的多;
3. v-if 有更高的切换开销,v-show 切换开销小;
4. v-if 有配套的 v-else-if 和 v-else,而 v-show 没有
5. v-if 可以搭配 template 使用,而 v-show 不行
v-show 和 v-if 的使用场景区别:
v-if
与v-show
都能控制dom
元素在页面的显示
v-if
相比v-show
开销更大的(直接操作dom
节点增加与删除)如果需要非常频繁地切换,则使用 v-show 较好
如果在运行时条件很少改变,则使用 v-if 较好
4.Vue中的常见指令
v-bind:用于绑定数据到DOM元素的属性上。例如:
<div v-bind:class="className"></div>
将会动态地将className
的值绑定到class
属性上。v-model:用于实现双向数据绑定,将表单元素的值与Vue实例的数据属性进行关联。例如:
<input v-model="message">
可以实现将输入框的值与message
属性进行双向绑定。v-if / v-else-if / v-else:用于根据条件来渲染元素。例如:
<div v-if="isShown">显示内容</div>
将会根据isShown
的值来判断是否渲染该元素。v-for:用于循环渲染列表。例如:
<li v-for="item in items">{{ item }}</li>
可以根据items
数组的内容循环渲染li
元素。v-on:用于绑定事件监听器。例如:
<button v-on:click="handleClick">点击我</button>
将会在点击按钮时触发handleClick
方法。v-show:类似于v-if,也是用于根据条件来显示或隐藏元素,但是使用v-show时,元素始终会被渲染到DOM中,只是通过CSS的display属性来控制是否显示。
v-cloak:用于防止在Vue实例加载之前显示未编译的Mustache语法。通常与CSS配合使用,例如:
[v-cloak] { display: none; }
。
5.Vue的生命周期
beforeCreate:在实例被创建之前调用,此时数据观测和事件配置尚未开始。
created:在实例创建完成后调用,此时实例已经完成数据观测,可以访问到data、computed和methods等选项,并可以进行初始化操作。
beforeMount:在挂载开始之前调用,此时模板编译已经完成,但是尚未将模板渲染到DOM中。
mounted:在挂载完成后调用,此时组件已经被挂载到DOM中,可以进行DOM操作和异步请求。
beforeUpdate:在更新之前调用,即响应式数据发生改变时,但是DOM尚未重新渲染。
updated:在更新完成后调用,此时DOM已经重新渲染,可以执行依赖于DOM的操作。
beforeDestroy:在实例销毁之前调用,此时实例仍然完全可用。
destroyed:在实例销毁之后调用,此时实例中的所有指令、事件监听器等都已经被移除,可以进行一些清理操作。
除了以上钩子函数外,Vue还提供了一些更特定的生命周期钩子,用于处理路由切换时的组件重用、动画效果等:
- activated:在使用
<keep-alive>
包裹的组件激活时调用。- deactivated:在使用
<keep-alive>
包裹的组件停用时调用。- beforeRouteEnter:在路由进入组件之前调用,可以获取到路由参数,但无法直接访问组件实例。
- beforeRouteLeave:在路由离开组件之前调用,可以进行导航守卫逻辑。
需要注意的是,Vue 3中的生命周期钩子函数与Vue 2中有所不同,具体的钩子函数名称和用法可能会有一些差异。在实际使用时,建议查阅Vue文档以获得最新的生命周期相关信息。
6.Vue数据双向绑定的原理
Vue的数据双向绑定是通过Vue的响应式系统来实现的。Vue使用了一种称为"数据劫持"的技术,通过使用Object.defineProperty()方法来拦截对数据属性的访问和修改,从而实现数据的观测和响应。
当Vue实例创建时,Vue会递归遍历data对象的所有属性,将它们转换为getter和setter,并且在内部维护一个依赖追踪的系统。当访问一个被观测的属性时,Vue会收集当前正在使用该属性的依赖,将其添加到依赖追踪的系统中。当该属性发生改变时,Vue会通知依赖追踪的系统,进而触发相应的更新操作,更新相关的DOM内容。
在数据的双向绑定中,v-model指令起到了关键作用。当使用v-model指令绑定一个表单元素时,Vue会自动为该元素添加一个事件监听器,监听输入框的输入事件。当输入框的值发生变化时,该事件监听器会将新的值更新到Vue实例的对应数据属性上,从而实现了数据的双向绑定。
总结起来,Vue的数据双向绑定通过数据劫持和依赖追踪来实现。通过对数据属性的拦截和监听,Vue能够自动追踪数据的变化,并在需要更新相关内容时进行响应。这样,当数据改变时,相关的视图会自动更新;而当用户操作视图时,数据也会自动更新,实现了数据和视图的同步更新。
7.computed和watch的区别与运用场景
computed
和watch
都是Vue中用于观察数据变化的选项,但它们的使用场景和功能略有不同。
computed
是一个计算属性,它基于已有的响应式数据进行计算,并返回计算结果。计算属性的值会被缓存,只有在依赖的响应式数据发生变化时才会重新计算。计算属性适用于那些依赖其他数据计算得出的值的场景,可以将复杂的计算逻辑封装在计算属性中,从而使代码更简洁可读。下面是一个使用
computed
的例子:data() { return { length: 5, }; }, computed: { doubleLength() { return this.length * 2; }, },
在上面的例子中,
doubleLength
是一个计算属性,它依赖于length
的值并返回其两倍的结果。
watch
用于观察特定的数据变化,并在数据变化时执行相应的操作。相比于计算属性,watch
提供了更通用的方式来响应数据的变化,可以执行任意的自定义操作,例如发起异步请求、执行复杂的逻辑等。watch
可以监听一个或多个数据的变化,并指定回调函数来处理变化。下面是一个使用
watch
的例子:data() { return { length: 5, }; }, computed: { doubleLength() { return this.length * 2; }, },
在上面的例子中,
watch
监听了length
的变化,当length
的值发生变化时,会触发回调函数打印新值和旧值。总结起来,
computed
适用于基于已有的响应式数据进行计算的场景,它会自动缓存计算结果;而watch
适用于需要监听特定数据变化并执行自定义操作的场景,它可以响应任意数据的变化。根据具体的需求,选择合适的选项来实现数据的观察和相应操作。
8.v-model双向绑定的原理是什么?
v-model
指令是Vue中用于实现表单元素的双向数据绑定的语法糖。它的原理是结合了属性绑定(v-bind
)和事件监听(v-on
)。
当使用v-model
指令绑定一个表单元素时,例如:
<input v-model="message">
Vue会自动为该元素添加两个操作:
将
message
属性的值绑定到该表单元素的value
属性上,即相当于使用v-bind
指令将value
属性与message
属性进行绑定。监听该表单元素的
input
事件,并在事件触发时将元素的当前值赋值给message
属性,即相当于使用v-on
指令监听input
事件,并将事件的值赋给message
属性。通过这两个操作,就实现了数据的双向绑定。当用户在输入框中输入内容时,
input
事件被触发,新的值被赋给message
属性,从而实现了从视图到数据的更新。反过来,当message
属性的值发生变化时,由于已经绑定了value
属性,输入框的值也会自动更新,实现了从数据到视图的更新。总结起来,
v-model
指令通过将数据属性和表单元素的value
属性进行双向绑定,并监听输入框的input
事件,实现了数据的双向同步。这样,在视图和数据之间的变化可以实时地进行更新和保持同步。
9.谈谈你对Vuex的理解
以下是对Vuex的一些理解:
集中式存储:Vuex采用了集中式存储的方式,将应用程序的所有组件的共享状态存储在一个单一的、可预测的状态树中。这样,不同组件之间可以直接访问和修改这些状态,避免了传递大量的属性和事件。
状态的响应式更新:Vuex使用了Vue的响应式系统,当状态发生变化时,相关的组件会自动更新视图。这样,我们无需手动追踪状态的变化和更新组件,让状态和视图保持同步。
明确的状态流动:通过使用Vuex,状态的变化变得可追踪和可预测。所有对状态的修改都需要通过提交(mutations)来进行,从而形成一个明确的状态变化流程,使得应用程序的状态变化变得可控。
模块化组织:Vuex支持将应用程序的状态树划分为多个模块,每个模块拥有自己的状态、操作和订阅。这种模块化的组织结构使得状态的管理更加灵活和可扩展,可以根据应用程序的复杂程度进行合理的划分和组织。
插件扩展:Vuex提供了插件机制,允许开发者扩展Vuex的功能。可以使用插件来实现日志记录、持久化存储、调试工具等,以满足应用程序特定的需求。
Vuex适用于中大型复杂的Vue应用程序,尤其是在涉及多个组件之间需要共享状态、进行状态管理和跨组件通信的情况下。通过Vuex,我们可以更好地组织和管理应用程序的状态,提高代码的可维护性和可扩展性,同时也能更方便地进行调试和监控。
10.nextTick的作用的作用是什么?它的实现原理是什么?
nextTick
是Vue中提供的一个异步方法,用于在DOM更新循环结束之后执行回调函数。它的作用是在下次DOM更新周期之前执行一些操作,以确保在修改数据后对DOM进行操作或访问时能够获取到最新的DOM状态。
nextTick
的实现原理如下:
在浏览器环境中,
nextTick
使用MutationObserver
来监听DOM变化。当数据发生变化时,Vue会将回调函数添加到一个异步队列中,并通过MutationObserver
监听DOM的变化。当DOM变化结束后,MutationObserver
会触发回调函数的执行。在非浏览器环境中,
nextTick
使用setTimeout
来模拟异步操作。当数据发生变化时,Vue会将回调函数添加到一个异步队列中,并通过setTimeout
设置一个延迟为0的定时器。在下一个事件循环中,setTimeout
的回调函数会被执行。通过以上的实现方式,Vue能够保证在下一次DOM更新之前,所有数据变化相关的回调函数都能够被执行。这样,在回调函数中可以获取到最新的DOM状态,以便进行相关操作。
nextTick
的常见用途包括:
在修改数据后,立即执行一些DOM相关的操作,例如获取更新后的DOM尺寸、滚动到指定位置等。
在Vue实例中使用
$nextTick
方法,以确保在调用该方法之后进行的操作能够在DOM更新后生效,例如在更新后访问组件中的DOM元素。总结起来,
nextTick
方法提供了一种在下次DOM更新周期之前执行回调函数的机制,确保在修改数据后能够获取到最新的DOM状态。它的实现原理利用浏览器环境下的MutationObserver
或setTimeout
来异步执行回调函数。
11.keep-alive的使用场景和原理。
keep-alive
是Vue提供的一个内置组件,用于缓存组件实例,以避免因组件频繁销毁和重建导致的性能问题。其使用场景通常是对一些比较耗费性能的组件进行缓存,以便在需要时可以直接复用之前的组件实例,而不是重新创建新的组件实例。
keep-alive
的使用方法很简单,只需要将需要缓存的组件包裹在<keep-alive>
标签中即可。例如:<template> <div> <keep-alive> <component :is="currentComponent"></component> </keep-alive> <button @click="toggleComponent">Toggle Component</button> </div> </template> <script> export default { data() { return { currentComponent: 'ComponentA' } }, methods: { toggleComponent() { this.currentComponent = this.currentComponent === 'ComponentA' ? 'ComponentB' : 'ComponentA' } } } </script>
上面的代码中,
<component :is="currentComponent"></component>
表示动态渲染当前选中的组件,<keep-alive>
则将选中的组件进行缓存。
keep-alive
的原理是将组件实例缓存到内存中,当组件被销毁时并不会真正销毁,而是将组件的状态保存下来。当组件再次被激活时,keep-alive
会重新渲染该组件,并将之前保存的状态恢复到组件中,从而避免了组件销毁和重建带来的性能损耗。在
keep-alive
中,组件实例的生命周期钩子函数也会被调用。当组件被缓存时,会依次调用activated
和deactivated
生命周期钩子函数,表示组件被激活和失活;当组件被销毁时,会依次调用beforeDestroy
和destroyed
生命周期钩子函数,表示组件被销毁。需要注意的是,
keep-alive
并不是对所有组件都适用的,通常只用于一些比较复杂或耗费性能的组件上,例如表格、图表等。此外,由于组件被缓存后可能存在一些状态上的问题,需要在组件中进行一些特殊处理,例如在activated
钩子函数中重新初始化一些状态,避免出现问题。
12.谈谈你对Vue响应式的理解。
Vue的响应式系统是Vue框架的核心特性之一,它使得数据的变化能够自动地驱动视图的更新。在Vue中,我们可以通过定义响应式的数据来构建动态的应用程序。
以下是对Vue响应式的一些理解:
数据驱动视图:Vue使用了基于依赖追踪的观察者模式,通过追踪数据与视图之间的依赖关系,当数据发生变化时,自动更新相关的视图。这种数据驱动视图的方式让开发者无需手动操作DOM,只需关注数据的变化,使得开发更加简单和高效。
声明式的数据绑定:在Vue中,可以使用指令(如
v-model
、v-bind
、v-on
等)来实现数据与视图之间的双向绑定和事件处理。通过将数据与视图进行绑定,当数据发生变化时,视图会自动更新;同时,用户在视图上的操作也会同步更新到数据上。响应式对象:Vue通过
Object.defineProperty
或Proxy
来劫持对象的属性访问,使得对属性的获取和修改都能被Vue追踪到,并触发相应的更新操作。这样,在对响应式对象的属性进行修改时,Vue能够自动检测到变化并通知相关的视图进行更新。异步更新队列:为了提高性能和避免频繁的DOM操作,Vue将数据变化的通知进行了批处理,并使用异步更新队列来延迟更新视图。在一次事件循环中,Vue将所有的数据变化进行合并,然后统一更新视图,这样可以减少重复计算和DOM操作,提高性能。
深度观测和浅观测:Vue对响应式数据进行了深度观测,即会递归地劫持对象的所有属性。但对于大型对象或嵌套层次很深的对象,这可能会带来性能问题。因此,Vue提供了
Vue.set
或this.$set
方法来实现对嵌套对象属性的响应式绑定。总结起来,Vue的响应式系统通过数据的变化驱动视图的更新,实现了声明式的数据绑定和自动更新视图的功能。它通过劫持对象属性的访问、使用异步更新队列等机制,实现了高效的响应式数据绑定。这使得开发者能够更专注于数据的变化和业务逻辑的编写,而不用过多关注DOM操作和视图的手动更新。
13.Vue中key的作用
在Vue中,
key
是用于给v-for
指令渲染的元素或组件设置唯一标识的特殊属性。它的作用主要有以下几点:
优化列表渲染:当使用
v-for
渲染列表时,Vue使用key
属性来识别每个节点的唯一性。通过设置合适的key
,Vue能够高效地追踪列表中的每个节点的状态,并在列表变化时实现最小化的DOM操作。在列表中插入、移动或删除元素时,有相同key
的元素会被视为同一个元素,从而实现局部的更新而不是重新渲染整个列表。实现组件的复用:在使用动态组件或条件渲染时,
key
属性可以用来确保组件的正确复用。Vue会基于key
的变化判断两个组件是否相同。如果两个组件的key
相同,Vue会尝试复用之前的组件实例,仅更新组件的数据和状态,而不是销毁和重新创建组件。这样可以提高组件的性能和响应速度。强制更新元素状态:当需要强制重新渲染某个元素时,可以通过修改该元素的
key
属性来触发Vue对该元素的重新渲染。Vue会将具有相同标签和key
的元素视为同一个元素,并在key
变化时重新渲染元素。这在某些特定场景下非常有用,例如当需要重置表单输入框的值时,可以通过修改key
来触发重新渲染而不用手动操作输入框的值。需要注意的是,
key
属性的值应该是唯一且稳定的,不建议使用随机数或索引作为key
。推荐使用具有唯一标识的属性值,如id
或唯一的标识符。同时,应避免在同一父元素下的兄弟元素之间重复使用相同的key
。总结来说,
key
属性在Vue中的作用主要是优化列表渲染、实现组件复用和强制更新元素状态。通过设置合适的key
,可以提高性能、实现局部更新,并确保正确的组件复用和状态管理。
14.vue中的路由模式有哪些
哈希模式(hash mode):默认情况下,Vue使用哈希模式来管理路由。在哈希模式下,URL中的
#
后面的部分被称为哈希值,不会被包含在HTTP请求中,因此可以避免页面的刷新。Vue使用window.location.hash
来实现哈希模式。例如:http://example.com/#/user/profile
。历史模式(history mode):在历史模式下,Vue使用HTML5的
history.pushState
和history.replaceState
来管理路由。这种模式下,URL中的路径部分被用来表示资源的真实路径,而不是仅仅作为哈希值的容器。使用历史模式可以使URL更加友好,并支持浏览器的前进后退。例如:http://example.com/user/profile
。
15.Vue 组件通讯有哪几种方式
在Vue中,组件之间可以通过多种方式进行通信,以下是常用的几种方式:
Props/Events:使用Props和Events是最基本和常用的组件通信方式。通过Props,父组件可以向子组件传递数据,子组件通过接收Props来获取数据。通过Events,子组件可以向父组件发送消息或触发事件,父组件通过监听子组件的Events来获取消息或响应事件。
Vuex:Vuex是Vue的官方状态管理库,用于管理应用的状态。Vuex通过创建一个全局的store来存储和管理数据,并提供了一系列的API来访问和修改这些数据。任何组件都可以通过Vuex来共享和同步状态,从而实现组件之间的通信。
$emit/$on:除了使用Props/Events进行父子组件之间的通信,Vue还提供了
$emit
和$on
方法来实现非父子组件之间的通信。组件可以通过$emit
方法触发自定义事件,并通过$on
方法监听和处理这些事件。这种方式可以实现任意组件之间的通信,但需要借助Vue的实例(vm
)来进行事件的派发和监听。Provide/Inject:通过Provide/Inject可以实现祖先组件向后代组件传递数据,而无需通过Props层层传递。祖先组件通过提供(provide)数据,后代组件通过注入(inject)获取提供的数据。这种方式适用于跨级组件之间的通信。
$refs:通过
$refs
可以获取子组件的引用,从而可以直接访问和操作子组件的属性和方法。这种方式适用于父组件需要直接操作子组件的情况,但在使用时需要注意生命周期的顺序和组件渲染的时机。Event Bus:Event Bus是一种简单的事件系统,可以用于任意组件之间的通信。它可以创建一个全局的事件总线,组件通过事件的发布(emit)和订阅(on)来实现通信。Event Bus是一个简单而灵活的方式,但在大型应用中可能会导致事件管理复杂和难以追踪。
这些是常用的Vue组件通信方式,每种方式都有自己的适用场景和特点。在实际开发中,根据具体的需求和组件之间的关系,选择合适的通信方式来进行组件间的数据传递和交互。
16.scoped的原理以及穿透方法
在Vue中,
scoped
是一个用于样式的特殊属性,它可以应用于<style>
标签或单文件组件的<style>
部分。当使用scoped
属性时,样式仅适用于当前组件的元素,不会影响到其他组件。
scoped
的原理是通过给样式选择器添加一个唯一的属性选择器来限定样式的作用范围。在编译过程中,Vue会对带有scoped
属性的样式进行处理,为每个选择器添加一个唯一的属性选择器,通常是一个哈希值。这样,在组件的渲染过程中,只有带有相同属性选择器的元素才会应用样式,其他元素则不会受到影响。例如,对于以下样式:
<style scoped> .container { background-color: red; } </style>
编译后的样式将变为:
<style> .container[data-v-abc123] { background-color: red; } </style>
在组件的模板中,所有使用
.container
类的元素都会自动添加一个data-v-abc123
属性,从而限定样式的作用范围。尽管
scoped
可以有效地限定样式的作用范围,但有时候可能需要从组件内部修改或穿透到组件的样式。为了实现这个目的,Vue提供了一些穿透方法,包括以下两种方式:
使用/deep/或>>>:在组件的样式中,可以使用
/deep/
或>>>
来穿透scoped
样式的限制,影响到子组件的样式。例如,.container /deep/ .child
或.container >>> .child
将会选择.container
组件内的所有.child
类的元素。使用引用:在子组件中,可以使用
$refs
引用父组件的元素,然后直接操作其样式。例如,在父组件中有一个<child>
子组件,并且为其添加了一个ref="child"
属性,那么可以通过this.$refs.child.$el
来获取子组件的根元素,然后直接对其应用样式。需要注意的是,穿透样式应该谨慎使用,因为它们打破了组件的封装性和隔离性。在大型项目中,过度使用样式穿透可能导致样式的冲突和难以维护。因此,在使用穿透样式时,应当慎重考虑,并确保明确了解其影响和使用场景。
最后愿大家面试成功,拿到心仪offer