2025年前端Vue2高频面试题汇总

2,615 阅读11分钟

Vue面试题汇总

Vue生命周期

1. Vue生命周期有哪些?

  • beforeCreate
  • created
  • beforeMount
  • mounted
  • beforeUpdate
  • updated
  • beforeDestroy
  • destroyed

2. 首次加载组件会加载哪些生命周期函数

  • beforeCreate:没有数据,没有DOM。
  • created:没有DOM,但有数据(data)。
  • beforeMount:没有DOM,但有数据(data)。
  • mounted:有DOM,有数据(data)。

3. 如果父组件引入了子组件,那么生命周期执行的顺序是什么?

  • 父组件:beforeCreate、created、beforeMount
  • 子组件:beforeCreate、created、beforeMount、mounted
  • 父组件:mounted

4. 在created中怎么获取DOM元素?

created中定义异步函数(如setTimeoutaxios请求、Promise或Vue系统内置的nextTick等),在异步函数中可以获取到页面DOM元素。

5. 为什么发送请求不放在beforeCreate里面?

1)数据尚未响应式

beforeCreate 阶段,dataprops 的数据尚未被 Vue 的响应式系统处理。如果你在这个阶段发送请求并更新数据,这些数据可能不会被 Vue 的响应式系统追踪,导致视图无法正确更新。

(2)模板和 DOM 尚未渲染

由于模板和 DOM 在 beforeCreate 阶段尚未渲染,即使你发送请求并获取了数据,也无法立即反映到视图上。这可能导致用户体验不佳,或者需要额外的逻辑来处理视图的更新。

(3)组件上下文未完全初始化

beforeCreate 阶段的组件上下文(this)尚未完全可用,可能会导致一些操作失败。例如,你可能无法访问到 this.$emitthis.$router 等实例方法。 还有如果请求的方法在methods里封装好了,在beforeCreate调用时,beforeCreate拿不到methods里面的方法,会报错。

6. 发送请求要放在mounted里还是created里?

在 Vue 中,发送请求放在 created 还是 mounted 钩子中,取决于你的具体需求和场景。两者都可以发送请求,但它们的适用场景有所不同。以下是两者的对比和建议:


1. created 钩子

created 钩子在组件实例创建完成后被调用。此时:

  • 数据已经响应式dataprops 的数据已经被 Vue 的响应式系统处理,你可以安全地访问和修改它们。
  • 组件上下文可用this 已经完全可用,你可以访问到 Vue 实例的所有方法和属性。
  • 模板尚未渲染:DOM 还没有被挂载,因此你无法操作 DOM。
优点:
  1. 快速初始化数据:如果你的请求结果直接影响组件的初始渲染内容,将请求放在 created 中可以更早地获取数据,减少用户等待时间。
  2. 避免不必要的 DOM 操作:在 created 中发送请求时,你不需要等待 DOM 挂载,因此可以更快地处理数据。

2. mounted 钩子

mounted 钩子在组件挂载完成后被调用。此时:

  • DOM 已经挂载:你可以访问和操作 DOM 元素。
  • 数据已经响应式:你可以安全地更新数据并触发视图更新。
优点:
  1. 操作 DOM:如果你需要根据 DOM 的某些状态(如尺寸、位置等)发送请求,mounted 是更好的选择。
  2. 确保视图已渲染:如果你需要在请求完成后立即更新视图或操作 DOM,mounted 可以确保视图已经渲染完成。
  • 如果请求结果直接影响组件的初始渲染内容,并且不需要操作 DOM,建议将请求放在 created 中。这样可以更快地获取数据并初始化组件。
  • 如果请求需要根据 DOM 的状态(如尺寸、位置等)发送,或者需要在请求完成后立即操作 DOM,建议将请求放在 mounted 中。

7. 加入keep-alive后会执行哪些生命周期函数?

  • activated:组件被重新激活时触发。

  • deactivated:组件被停用时触发。

    keep-alive是Vue.js内置的组件,用于缓存不活动的组件实例,而不是销毁它们。这意味着当组件在keep-alive内切换时,它的状态和DOM元素都会被保留,而不是销毁和重新创建。

    keep-alive的主要用途:
    1. 保持组件状态,避免重新渲染,提高性能。
    2. 缓存组件实例,避免重复创建和销毁组件,节省资源。
    keep-alive的属性:
    • include:字符串或正则表达式,指定要缓存的组件名称。
    • exclude:字符串或正则表达式,指定要排除的组件名称。
    • max:数字,指定缓存组件的最大数量。

关于组件

1. 组件的通信方式有哪些?

  1. 父组件向子组件传值
    • 通过props属性,在子组件中定义props属性,父组件通过v-bind指令将值传递给子组件,子组件通过props接收。
  2. 子组件向父组件传值
    • 通过自定义事件,在子组件中通过$emit方法触发自定义事件,父组件通过v-on指令监听事件并获取值。
  3. 兄弟组件通信
    • 通过事件总线,创建事件总线对象,在兄弟组件中触发和监听事件。
  4. 父子组件通信
    • 通过$refs属性,父组件通过$refs获取子组件实例,调用子组件方法或访问属性。
  5. 父子组件通信
    • 通过provide/inject,父组件通过provide提供数据,子组件通过inject注入数据。
  6. 父子组件通信
    • 通过$parent/$children属性,父组件通过$children获取子组件实例,子组件通过$parent获取父组件实例。
  7. 通过Vuex进行状态管理,实现组件之间的通信。

2. slot插槽

vue 的插槽(Slot)是 Vue.js 提供的一种强大的功能,用于在组件中插入自定义内容。它允许父组件向子组件的特定位置插入内容,从而实现组件的可扩展性和灵活性。插槽是 Vue 组件化开发中非常重要的一个特性,尤其在构建可复用组件时非常有用。

1. 插槽的基本概念

插槽允许父组件在子组件的模板中插入自定义内容。它类似于 HTML 的 content 插槽,但更强大。通过插槽,你可以:

  • 在子组件中定义一个或多个“插槽”位置。
  • 在父组件中向这些插槽位置插入内容。

2. 插槽的类型

Vue 提供了三种类型的插槽:

  1. 默认插槽(Default Slot)
  2. 具名插槽(Named Slot)
  3. 作用域插槽(Scoped Slot)
(1)默认插槽(Default Slot)

默认插槽是最简单的插槽类型。它允许父组件向子组件的默认位置插入内容。

(2)具名插槽(Named Slot)

具名插槽允许你在子组件中定义多个插槽,并通过名字区分它们。父组件可以通过名字将内容插入到指定的插槽中。

(3)作用域插槽(Scoped Slot)

作用域插槽允许子组件向父组件传递数据,父组件可以使用这些数据来渲染插槽内容。它通过 slot-scopev-slot 来实现。

3. 插槽的使用场景

插槽非常适合以下场景:

  1. 可复用组件:构建通用的组件,如卡片、弹窗、列表等,父组件可以根据需要插入不同的内容。
  2. 动态内容:根据父组件的状态动态插入内容。
  3. 自定义布局:父组件可以根据自己的需求定制子组件的布局。

4. 最佳实践

  1. 使用默认内容:在子组件中为插槽提供默认内容,这样即使父组件没有提供内容,子组件也能正常显示。
  2. 合理使用具名插槽:如果需要在子组件中插入多个内容,使用具名插槽可以更清晰地组织代码。
  3. 使用作用域插槽传递数据:当需要将子组件的数据传递给父组件时,使用作用域插槽可以避免不必要的 props 传递。
  4. 避免滥用插槽:虽然插槽非常强大,但过度使用可能会导致组件的逻辑变得复杂。尽量保持组件的职责清晰。

5. 总结

Vue 的插槽是一个非常强大的功能,它允许父组件向子组件插入自定义内容,从而实现组件的高度可扩展性和灵活性。通过默认插槽、具名插槽和作用域插槽,你可以满足不同的开发需求。合理使用插槽可以让你的组件更加通用和灵活,同时也能提高代码的可维护性。

vue状态管理工具

1. vuex的五个核心概念

  1. State:Vuex中的状态,用于存储全局状态。
  2. Getters:用于从State中派生出一些状态,类似于计算属性。
  3. Mutations:用于同步修改State中的状态,是唯一可以修改State的方法。
  4. Actions:用于异步操作,可以包含多个Mutation,并在操作完成后调用Mutation。
  5. Modules:用于将Vuex的状态分割成模块,每个模块拥有自己的State、Mutations、Actions、Getters。

vuex的持久化存储

Vuex的状态存储在内存中,当页面刷新时,状态会丢失。为了实现状态的持久化存储,可以使用以下方法:

  1. localStorage:将状态存储在浏览器的localStorage中,可以实现状态的持久化存储。
  2. sessionStorage:将状态存储在浏览器的sessionStorage中,可以实现状态的临时存储。
  3. cookie:将状态存储在浏览器的cookie中,可以实现状态的持久化存储。
  4. cookie、localStorage、sessionStorage的区别: cookie:存储在客户端,可以设置过期时间,可以设置路径,可以设置域,可以设置是否只读。大小限制为4KB。 localStorage:存储在客户端,没有过期时间,可以设置路径,可以设置域,可以设置是否只读。大小限制为5MB。 sessionStorage:存储在客户端,当页面关闭时,数据会丢失,可以设置路径,可以设置域,可以设置是否只读。大小限制为5MB。

vue-router路由管理

vue-router的路由模式

  1. hash模式:使用URL的hash部分来模拟一个完整的URL,当URL的hash部分发生变化时,会触发路由的更新。
  2. history模式:使用HTML5的history API来实现URL的跳转,当URL发生变化时,会触发路由的更新。

vue-router中两种模式的区别

  1. URL的表现形式:hash模式使用URL的hash部分来模拟一个完整的URL,而history模式使用HTML5的history API来实现URL的跳转。
  2. 刷新页面时的表现:hash模式刷新页面时,不会重新加载页面,而history模式刷新页面时,会重新加载页面。
  3. 浏览器兼容性:hash模式兼容性好,而history模式需要服务器端的支持,否则会出现404错误。
  4. SEO优化:hash模式对SEO优化不友好,而history模式对SEO优化友好。
  5. 性能:hash模式性能较好,而history模式性能较差。
  6. 安全性:hash模式安全性较高,而history模式安全性较低。

vue-router的导航守卫

vue-router的导航守卫是指在路由跳转过程中,可以执行一些额外的操作,比如权限验证、数据预加载等。 导航守卫可以通过beforeEachbeforeResolveafterEach方法实现

vue-router提供了导航守卫,可以在路由发生变化时执行一些操作。导航守卫分为全局守卫、路由独享守卫、组件内守卫。

  1. 全局守卫:全局守卫可以在路由发生变化时执行一些操作,包括全局前置守卫(beforeEach)、全局解析守卫(beforeResolve)、全局后置钩子(afterEach)。
  2. 路由独享守卫:路由独享守卫可以在路由发生变化时执行一些操作,包括路由独享的beforeEnter守卫。
  3. 组件内守卫:组件内守卫可以在路由发生变化时执行一些操作,包括组件内的beforeRouteEnter守卫、beforeRouteUpdate守卫、beforeRouteLeave守卫。
  4. 导航守卫的执行顺序:全局前置守卫 -> 路由独享守卫 -> 组件内守卫 -> 全局解析守卫 -> 全局后置钩子。
  5. 导航守卫的参数:全局前置守卫和路由独享守卫的参数为to、from、next;组件内守卫的参数为to、from、next;全局解析守卫和全局后置钩子的参数为to、from。
  6. 导航守卫的返回值:全局前置守卫和路由独享守卫的返回值为false时,表示中断导航;组件内守卫的返回值为false时,表示中断导航;全局解析守卫和全局后置钩子的返回值没有意义。
  7. 导航守卫的next参数:next参数可以用来控制导航的走向,包括next()、next(false)、next('/path')、next({path: '/path'})、next(error)。
  8. 导航守卫的用法:导航守卫可以在路由发生变化时执行一些操作,比如权限验证、数据获取、页面加载等。

vue-router的懒加载

vue-router的懒加载是指在路由被访问时,才加载对应的组件。这样可以减少应用的初始加载时间,提高应用的性能。 vue-router的懒加载可以通过动态import()语法实现,例如:

const Home = () => import('./Home.vue')
const About = () => import('./About.vue')

const routes = [
{ path: '/', component: Home },
{ path: '/about', component: About }
]

在上面的例子中,Home和About组件只有在被访问时,才会被加载。

vue-router的嵌套路由

vue-router的嵌套路由是指在路由中嵌套其他路由。这样可以实现多级路由,提高应用的模块化程度。 嵌套路由可以通过children属性实现,例如:

const routes = [
{
   path: '/',
   component: Home,
   children: [
      {
      path: 'about',
      component: About
      }
   ]
}
]

在上面的例子中,About组件是Home组件的子路由,只有当访问/about时,才会加载About组件。

vue-router的编程式导航

vue-router的编程式导航是指在代码中通过编程的方式实现路由跳转。这样可以实现更灵活的路由跳转,比如根据条件跳转到不同的路由。

编程式导航可以通过this.$router.push()方法实现,例如:

this.$router.push('/about')

在上面的例子中,会跳转到/about路由。

vue-router的命名视图

vue-router的命名视图是指在同一个路由下,可以同时渲染多个视图。这样可以实现更复杂的布局。

命名视图可以通过<router-view>组件的name属性实现,例如:

<router-view name="header"></router-view>
<router-view></router-view>
<router-view name="footer"></router-view>

在上面的例子中,会渲染三个视图,分别对应header、默认和footer。

vue-router的参数传递

通过 URL 路径传递参数:在路由路径中使用冒号(:)来定义参数,然后在组件中通过 this.$route.params 来获取参数。 例如:

const routes = [
   {
      path: '/user/:id',
      component: User
   }
]

在上面的例子中,可以通过this.$route.params.id获取到用户id。

通过查询参数传递参数:在路由路径中使用 ? 来定义查询参数,然后在组件中通过 this.$route.query 来获取参数。例如:

const routes = [
   {
      path: '/user?id=123',
      component: User
   }
]

在上面的例子中,可以通过this.$route.query.id获取到用户id。

通过路由对象传递参数:在路由跳转时,可以通过 params 或 query 属性来传递参数。例如:

this.$router.push({ path: '/user', params: { id: 123 } })
this.$router.push({ path: '/user', query: { id: 123 } })

在上面的例子中,可以通过this.$route.params.idthis.$route.query.id获取到用户id。 需要注意的是,通过 URL 路径传递的参数是动态的,而通过查询参数传递的参数是静态的。另外,通过 params 传递的参数在刷新页面后会被丢失,而通过 query 传递的参数不会丢失。

vue指令

v-if和v-show的区别

v-if和v-show都是Vue.js中的条件渲染指令,它们都可以根据表达式的值来决定是否渲染元素。但是它们在实现方式和行为上有一些区别:

  1. 实现方式:v-if是真正地条件渲染,它会根据表达式的值来决定是否创建和销毁DOM元素。而v-show只是简单地切换元素的display属性,无论表达式的值是什么,DOM元素都会被创建,只是通过display属性来控制元素的显示和隐藏。因此,v-if在初始渲染时会比v-show更慢,但是切换状态时v-show会更快。
  2. 行为:v-if在切换状态时会触发组件的生命周期钩子函数,而v-show则不会。因此,如果需要在切换状态时执行一些额外的逻辑,应该使用v-if。
  3. 使用场景:v-show适用于需要频繁切换状态的场景,而v-if适用于只需要切换一次状态的场景。

v-for和v-if的优先级

  • 在 Vue 2 中,v-for 的优先级高于 v-if。这意味着当它们同时出现在同一个元素上时,Vue 会先处理 v-for,生成循环的虚拟 DOM 节点,然后再根据 v-if 的条件判断是否渲染每个节点。
  • 在 Vue 3 中,v-if 的优先级高于 v-for。这意味着 v-if 的条件会先被评估,只有当条件为 true 时,v-for 才会执行。例如:
<li v-for="item in items" v-if="item.isActive" :key="item.id">
{{ item.name }}
</li>
  • 在vue2中会先遍历 items,然后对每个 item 执行 v-if 的条件判断。这可能会导致性能问题,尤其是当 items 数量较大且大部分条件为 false 时。
  • 在vue3中会先根据 v-if 的条件判断是否渲染整个列表,而不是逐项判断。如果 v-if 的条件依赖于 v-for 的循环变量(如 item.isActive),这会导致错误,因为 v-if 无法访问到 v-for 的作用域。
使用建议:
  1. 避免在同一元素上同时使用 v-ifv-for:由于优先级不同,这可能导致意外的行为和性能问题。
  2. 使用计算属性替代:将需要过滤的逻辑放在计算属性中,这样可以提高性能并避免优先级问题。
  3. v-if 放在外部:如果需要根据条件渲染整个列表,可以将 v-if 放在 v-for 的外部
  • 最佳实践:尽量避免在同一元素上同时使用 v-ifv-for,推荐使用计算属性来处理数据过滤。

computed和watch的区别

computed 和 watch 都是 Vue.js 中用于处理数据变化的工具,但它们在用途和实现方式上有一些区别:

  1. 用途:computed 用于声明式地计算属性,它基于其他数据的变化自动更新。watch 用于监听数据的变化,并在数据变化时执行特定的操作。
  2. 实现方式:computed 是基于依赖关系自动更新的,当依赖的数据发生变化时,computed 会重新计算并更新其值。watch 是通过监听数据的变化来触发特定的操作,它可以在数据变化时执行异步操作或复杂的逻辑。
  3. 使用场景:computed 适用于需要基于其他数据计算新值的情况,例如计算属性、过滤器等。watch 适用于需要在数据变化时执行特定操作的情况,例如数据验证、异步操作等。
  4. computed 是惰性的,它的值会被缓存,只有当依赖的数据发生变化时才会重新计算。watch 是立即执行的,只要监听的数据发生变化就会执行指定的操作。

Vue中key的作用

在 Vue.js 中,key 是一个特殊的属性,用于标识每个元素或组件的唯一性。它可以帮助 Vue 更高效地更新和渲染 DOM 元素。 当 Vue.js 渲染一个列表时,它会为每个元素分配一个唯一的 key 值。当列表发生变化时,Vue.js 会使用 key 值来确定哪些元素需要被更新、添加或删除。如果 key 值没有发生变化,Vue.js 会保留原有的 DOM 元素,而不是重新创建一个新的元素。这可以避免不必要的 DOM 操作,提高渲染性能。 在使用 v-for 指令渲染列表时,建议为每个元素指定一个唯一的 key 值。例如:

<ul>
   <li v-for="item in items" :key="item.id">{{ item.text }}</li>
</ul>

在这个例子中,我们为每个列表项指定了一个唯一的 key 值(item.id)。这样,当 items 数据发生变化时,Vue.js 可以根据 key 值来确定哪些元素需要被更新、添加或删除。 注意:key 值必须是唯一的,不能重复。如果 key 值重复,Vue.js 可能无法正确地更新和渲染 DOM 元素。

vue中$nextTick作用

Vue是一个响应式框架,当数据发生变化时,Vue 会自动更新 DOM。然而,DOM 的更新并不是立即发生的,而是会被放入一个队列中,等待 Vue 的下一个tick时统一处理。$nextTick 的作用是允许你在 DOM 更新完成后执行代码。 简单来说,$nextTick 的作用是:

  • 等待DOM更新完成: 确保在 DOM 更新完成后执行某些操作。
  • 异步执行:它是一个异步方法,允许你在Vue的下一个tick中执行代码。
  1. 为什么需要 $nextTick 在 Vue 中,数据更新和 DOM 更新是异步的。这意味着当你修改数据后,DOM并不会立即更新。如果你需要在 DOM更新完成后执行某些操作(比如获取 DOM 元素的尺寸、滚动位置等),直接在数据更新后执行可能会导致问题,因为DOM还未完成更新。

使用场景:

  • 1、在数据更新后立即获取 DOM 元素的尺寸或位置。
  • 2、在数据更新后执行某些操作,需要确保 DOM 已经更新完成。
  • 3、在数据更新后执行某些异步操作,需要确保 DOM 已经更新完成。
  • 4、在数据更新后执行某些操作,需要确保 DOM 已经更新完成,并且可以获取到最新的 DOM 元素。

注意事项:

  • 避免滥用:虽然 $nextTick 非常有用,但过度使用可能导致代码难以维护。尽量通过响应式数据或计算属性来解决问题,而不是依赖 $nextTick
  • 性能优化$nextTick 是异步的,如果在循环中频繁调用,可能会导致性能问题。尽量减少不必要的调用。

vue中如何自定义指令

在 Vue.js 中,自定义指令是一种强大的功能,允许你直接操作 DOM 元素。自定义指令可以让你封装 DOM 操作逻辑,使其在模板中复用。 自定义指令分为全局注册自定义指令和局部注册的自定义指令两种。

vue2的自定义指令生命周期钩子函数:

bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
inserted:被绑定元素插入父节点时调用(父节点存在即可调用,不必存在于 document 中)。
update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。
componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。
unbind:只调用一次,指令与元素解绑时调用。

vue3的自定义指令生命周期钩子函数:

const myDirective = {
   // 在绑定元素的 attribute 前
   // 或事件监听器应用前调用
   created(el, binding, vnode) {
      // 下面会介绍各个参数的细节
   },
   // 在元素被插入到 DOM 前调用
   beforeMount(el, binding, vnode) {},
   // 在绑定元素的父组件
   // 及他自己的所有子节点都挂载完成后调用
   mounted(el, binding, vnode) {},
   // 绑定元素的父组件更新前调用
   beforeUpdate(el, binding, vnode, prevVnode) {},
   // 在绑定元素的父组件
   // 及他自己的所有子节点都更新后调用
   updated(el, binding, vnode, prevVnode) {},
   // 绑定元素的父组件卸载前调用
   beforeUnmount(el, binding, vnode) {},
   // 绑定元素的父组件卸载后调用
   unmounted(el, binding, vnode) {}
}
app.directive('my-directive', myDirective);

全局指令: 全局注册自定义指令可以在任何组件中使用,通过 Vue.directive() 方法注册。例如,创建一个名为 v-focus 的全局指令,用于自动聚焦输入框:

Vue.directive('focus', {
  inserted: function (el) {
    el.focus();
  }
});

局部指令: 局部注册自定义指令可以在组件的 directives 选项中定义。例如,创建一个名为 v-focus 的局部指令,用于自动聚焦输入框:

directives: {
  focus: {
    inserted: function (el) {
      el.focus();
    }
  }
}

使用指令: 在模板中使用自定义指令时,需要在指令名称前加上 v- 前缀。例如,使用 v-focus 指令自动聚焦输入框:

<input v-focus>

使用场景: 自定义指令适用于以下场景:

  • 操作 DOM:如聚焦、滚动、调整样式等。
  • 封装复杂逻辑:如实现自定义的拖拽功能、提示框等。
  • 与第三方库结合:如集成 jQuery 插件或其他 DOM 操作库。

注意事项

  • 避免滥用:自定义指令直接操作 DOM,可能会导致代码难以维护。尽量通过 Vue 的响应式系统解决问题。
  • 性能优化:指令的生命周期钩子可能会频繁触发,注意优化性能。
  • 解绑逻辑:如果指令绑定了事件监听器或进行了其他操作,记得在 unmounted 钩子中清理。 总结 自定义指令是 Vue 中操作 DOM 的强大工具,通过生命周期钩子、参数和修饰符,可以实现复杂的 DOM 操作逻辑。合理使用自定义指令可以让你的代码更加简洁和复用性强,但也要注意避免滥用,保持代码的可维护性。

scoped的原理

在 Vue 中,scoped 样式的作用范围仅限于当前组件,避免了样式冲突。scoped 样式的实现原理是通过在 DOM 元素上添加唯一的属性(如 data-v-xxxxx),然后在样式中使用属性选择器来限制样式的作用范围。例如,对于以下 scoped 样式:

<style scoped>
.example {
  color: red;
}
</style>

Vue 会将其转换为以下形式:

<style>
.example[data-v-xxxxx] {
  color: red;
}
</style>

其中,data-v-xxxxx 是 Vue 自动生成的唯一属性。这样,样式只会应用于带有该属性的元素,从而实现 scoped 样式的效果。

v-model原理

Vue 的 v-model 指令用于在表单元素上创建双向数据绑定。它的实现原理如下: 语法糖:v-model 是一个语法糖,它实际上是 v-bind 和 v-on 的组合。在表单元素上使用 v-model 时,Vue 会自动为该元素绑定一个 value 属性,并监听 input或change 事件,从而实现数据的双向绑定。

<input v-model="message">

等价于:

<input :value="message" @input="message = $event.target.value">

组件支持:在自定义组件中,v-model 的行为可以通过 props 和 emit来实现。默认情况下,vmodel会将父组件传递的值作为子组件的props,并在子组件内部通过emit 来实现。默认情况下,v-model 会将父组件传递的值作为子组件的 props,并在子组件内部通过 emit 更新父组件的值。

<custom-input v-model="message"></custom-input>

在子组件中:

<template>
   <input :value="value" @input="$emit('update:value', $event.target.value)" />
</template>

<script>
   export default {
      props: ['value']
   };
</script>

总结: v-model 的原理基于 Vue 的指令系统和响应式系统。它通过将数据模型与表单元素的值绑定,并监听输入事件来实现双向数据同步。这种机制使得开发者可以非常方便地在表单输入和应用状态之间进行数据交互,而无需手动管理事件监听和数据更新。

v-model和data绑定的区别

  1. 功能和用途 (1)v-model:双向数据绑定 功能:v-model 是用于表单元素(如 、、 等)的双向数据绑定指令。它同时实现了从数据到视图(data -> view)和从视图到数据(view -> data)的同步。

    原理:它通过 v-bind 将数据绑定到表单元素的 value 属性,并通过 v-on 监听表单的输入事件(如 input 或 change),从而实现数据的双向同步。

    使用场景:主要用于表单输入场景,方便用户输入内容时自动更新数据模型,同时将数据模型的值动态显示在表单中。 (2)v-bind(或 :):单向数据绑定 功能:v-bind 是用于将数据动态绑定到 DOM 属性的单向绑定指令。它只从数据流向视图(data -> view),不会将视图的变化自动更新到数据模型中。

    原理:通过将 Vue 实例中的数据绑定到 DOM 元素的属性上,当数据变化时,绑定的属性值也会自动更新。

    使用场景:适用于需要动态绑定数据到 DOM 属性的场景,如绑定类名、样式、属性值等。

Vue的双向绑定原理

Vue 的双向绑定是通过数据劫持和发布订阅模式实现的。具体来说,Vue 在初始化时,会使用 Object.defineProperty() 方法对数据对象进行劫持,为每个属性添加 getter 和 setter。当数据发生变化时,setter 会通知依赖该数据的视图进行更新。同时,Vue 还会使用发布订阅模式,将视图中的数据绑定到数据模型上,当视图发生变化时,会通知数据模型进行更新。

具体步骤如下:

  1. 数据劫持:Vue 在初始化时,会使用 Object.defineProperty() 方法对数据对象进行劫持,为每个属性添加 getter 和 setter。当数据发生变化时,setter 会通知依赖该数据的视图进行更新。
  2. 发布订阅模式:Vue 还会使用发布订阅模式,将视图中的数据绑定到数据模型上,当视图发生变化时,会通知数据模型进行更新。
  3. 数据更新:当数据发生变化时,setter 会通知依赖该数据的视图进行更新。同时,视图中的数据绑定到数据模型上,当视图发生变化时,会通知数据模型进行更新。