在Vue3中,侦听器的使用方式与Vue2相同,可以使用watch
选项或$watch
方法来创建侦听器。不同之处在于,Vue3中取消了immediate
选项,同时提供了新的选项和API。
创建侦听器
可以使用watch
选项或$watch
方法来创建侦听器,语法与Vue2相同。示例如下:
<template> <div>{{ message }}</div> </template> <script> export default { data() { return { message: 'Hello, Vue3!' } }, watch: { message(newValue, oldValue) { console.log(`New value: ${newValue}, old value: ${oldValue}`) } } } </script>
上面的代码中,使用watch
选项来创建侦听器,当message
的值发生变化时,会触发侦听器函数。
侦听多个属性
在Vue3中,可以使用数组的方式侦听多个属性。示例如下:
<template> <div>{{ fullName }}</div> </template> <script> export default { data() { return { firstName: 'John', lastName: 'Doe' } }, watch: { ['firstName', 'lastName'](newValues, oldValues) { console.log(`New values: ${newValues}, old values: ${oldValues}`) } }, computed: { fullName() { return `${this.firstName} ${this.lastName}` } } } </script>
上面的代码中,使用数组的方式侦听firstName
和lastName
两个属性,当它们的值发生变化时,会触发侦听器函数。
深度侦听
在Vue3中,可以使用deep
选项来实现深度侦听。示例如下:
<template> <div>{{ user.name }}</div> </template> <script> export default { data() { return { user: { name: 'John Doe', age: 30 } } }, watch: { user: { handler(newValue, oldValue) { console.log(`New value: ${JSON.stringify(newValue)}, old value: ${JSON.stringify(oldValue)}`) }, deep: true } } } </script>
上面的代码中,使用deep
选项来实现深度侦听user
对象的所有属性,当user
对象的任何属性发生变化时,都会触发侦听器函数。
取消侦听器
在Vue3中,可以使用watch
选项返回的取消函数来取消侦听器。示例如下:
<template> <div>{{ message }}</div> </template> <script> export default { data() { return { message: 'Hello, Vue3!' } }, mounted() { const unwatch = this.$watch('message', (newValue, oldValue) => { console.log(`New value: ${newValue}, old value: ${oldValue}`) }) setTimeout(() => { unwatch() }, 5000) } } </script>
上面的代码中,使用$watch
方法创建侦听器,并将返回的取消函数存储在unwatch
变量中,在5秒后调用取消函数,取消侦听器。
在Vue3中,表单输入绑定的方式与Vue2相同,可以使用v-model
指令来实现。不同之处在于,Vue3中取消了.sync
修饰符,同时提供了新的修饰符和API。
基本用法
使用v-model
指令可以将表单元素的值与组件的数据进行双向绑定。示例如下:
<template> <div> <input type="text" v-model="message"> <p>{{ message }}</p> </div> </template> <script> export default { data() { return { message: '' } } } </script>
上面的代码中,将input
元素的值与message
数据进行双向绑定,当input
元素的值发生变化时,message
数据也会跟着变化,同时p
元素中展示message
数据的值。
修饰符
在Vue3中,提供了新的修饰符来实现更灵活的表单输入绑定。
.lazy
修饰符:在输入框失去焦点或按下回车键后才更新数据。示例如下:
<template> <div> <input type="text" v-model.lazy="message"> <p>{{ message }}</p> </div> </template> <script> export default { data() { return { message: '' } } } </script>
上面的代码中,使用.lazy
修饰符将输入框的值在失去焦点或按下回车键后才更新message
数据。
.trim
修饰符:去除输入框的首尾空格。示例如下:
<template> <div> <input type="text" v-model.trim="message"> <p>{{ message }}</p> </div> </template> <script> export default { data() { return { message: '' } } } </script>
上面的代码中,使用.trim
修饰符去除输入框的首尾空格,并将处理后的值绑定到message
数据上。
.number
修饰符:将输入框的值转换为数字类型。示例如下:
<template> <div> <input type="text" v-model.number="age"> <p>{{ age }}</p> </div> </template> <script> export default { data() { return { age: 0 } } } </script>
上面的代码中,使用.number
修饰符将输入框的值转换为数字类型,并将转换后的值绑定到age
数据上。
自定义组件
在自定义组件中,可以使用v-model
指令来实现自定义组件的双向绑定。示例如下:
<template> <div> <my-input v-model="message"></my-input> <p>{{ message }}</p> </div> </template> <script> import MyInput from './MyInput.vue' export default { components: { MyInput }, data() { return { message: '' } } } </script>
上面的代码中,使用v-model
指令将my-input
组件的值与message
数据进行双向绑定,当my-input
组件的值发生变化时,message
数据也会跟着变化,同时p
元素中展示message
数据的值。需要注意的是,my-input
组件内部需要使用$emit
方法触发input
事件来实现数据的更新。
在Vue3中,模板引用使用ref
来实现。ref
可以用来获取组件实例或DOM元素的引用,并将其绑定到组件实例的数据上。
组件引用
在Vue3中,使用ref
可以获取到组件实例的引用。示例如下:
<template> <div> <my-component ref="myComponent"></my-component> </div> </template> <script> import MyComponent from './MyComponent.vue' export default { components: { MyComponent }, mounted() { console.log(this.$refs.myComponent) // 输出组件实例 } } </script>
上面的代码中,使用ref
获取到my-component
组件的实例,并将其绑定到myComponent
数据上。在mounted
钩子函数中,可以通过this.$refs.myComponent
获取到组件实例,并进行操作。
DOM元素引用
在Vue3中,使用ref
可以获取到DOM元素的引用。示例如下:
<template> <div> <input type="text" ref="myInput"> </div> </template> <script> export default { mounted() { console.log(this.$refs.myInput) // 输出DOM元素 } } </script>
上面的代码中,使用ref
获取到input
元素的引用,并将其绑定到myInput
数据上。在mounted
钩子函数中,可以通过this.$refs.myInput
获取到DOM元素,并进行操作。
需要注意的是,在Vue3中,ref
只能绑定到组件实例或DOM元素上,不能绑定到普通数据上。
在Vue3中,组件由三部分组成:模板、逻辑和样式。其中,模板和逻辑与Vue2中的组件相同,而样式方面,Vue3推荐使用CSS Modules和CSS Variables来实现。
模板
组件的模板与Vue2中的模板相同,使用template
标签来定义。示例如下:
<template> <div> <h1>{{ title }}</h1> <p>{{ content }}</p> </div> </template> <script> export default { data() { return { title: 'Hello, Vue3!', content: 'Vue3 is awesome!' } } } </script>
上面的代码中,定义了一个简单的组件模板,包含一个标题和一段文本内容,使用双花括号绑定数据。
逻辑
组件的逻辑与Vue2中的逻辑相同,使用script
标签来定义。示例如下:
<template> <div> <h1>{{ title }}</h1> <p>{{ content }}</p> </div> </template> <script> export default { data() { return { title: 'Hello, Vue3!', content: 'Vue3 is awesome!' } }, methods: { handleClick() { console.log('clicked') } } } </script>
上面的代码中,定义了一个简单的组件逻辑,包含一个data
数据对象和一个handleClick
方法。
样式
在Vue3中,推荐使用CSS Modules和CSS Variables来实现组件样式。CSS Modules可以避免全局样式的污染,而CSS Variables可以实现更灵活的样式控制。
使用CSS Modules时,可以在style
标签中设置module
属性来启用CSS Modules。示例如下:
<template> <div class="wrapper"> <h1 class="title">{{ title }}</h1> <p class="content">{{ content }}</p> </div> </template> <script> export default { data() { return { title: 'Hello, Vue3!', content: 'Vue3 is awesome!' } } } </script> <style module> .wrapper { padding: 20px; background-color: #f5f5f5; } .title { font-size: 24px; color: var(--primary-color); } .content { font-size: 16px; color: #333; } </style>
上面的代码中,使用CSS Modules设置了.wrapper
、.title
和.content
三个类的样式,并使用CSS Variables设置了--primary-color
变量的值。
需要注意的是,使用CSS Modules时,类名会被自动转换为唯一的类名,可以通过$style
来引用。示例如下:
<template> <div :class="$style.wrapper"> <h1 :class="$style.title">{{ title }}</h1> <p :class="$style.content">{{ content }}</p> </div> </template> <script> export default { data() { return { title: 'Hello, Vue3!', content: 'Vue3 is awesome!' } } } </script> <style module> .wrapper { padding: 20px; background-color: #f5f5f5; } .title { font-size: 24px; color: var(--primary-color); } .content { font-size: 16px; color: #333; } </style>
上面的代码中,使用$style
引用了.wrapper
、.title
和.content
三个类的样式。
在Vue3中,组件嵌套关系与Vue2中的组件嵌套关系相同,通过在模板中嵌套组件来实现。
例如,有两个组件Parent
和Child
,其中Parent
组件中嵌套了Child
组件。示例如下:
<template> <div> <h1>{{ title }}</h1> <child :content="content"></child> </div> </template> <script> import Child from './Child.vue' export default { components: { Child }, data() { return { title: 'Parent Component', content: 'This is the content of Parent Component.' } } } </script>
上面的代码中,Parent
组件中通过<child>
标签嵌套了Child
组件,并将content
数据传递给Child
组件。
Child
组件的代码如下:
<template> <div> <h2>{{ title }}</h2> <p>{{ content }}</p> </div> </template> <script> export default { props: { content: { type: String, default: '' } }, data() { return { title: 'Child Component' } } } </script>
上面的代码中,Child
组件接收了Parent
组件传递的content
数据,并在模板中展示出来。
需要注意的是,当组件嵌套层级较深时,可以使用provide
和inject
来实现跨层级传递数据,避免层层传递数据的麻烦。
在Vue3中,组件注册方式与Vue2中的组件注册方式有所不同,Vue3提供了defineComponent
函数来定义组件。具体步骤如下:
创建组件
使用defineComponent
函数创建组件,示例如下:
import { defineComponent } from 'vue' export default defineComponent({ name: 'MyComponent', props: { content: { type: String, default: '' } }, setup(props) { return { title: 'My Component', handleClick() { console.log('clicked') } } }, template: ` <div> <h1>{{ title }}</h1> <p>{{ content }}</p> <button @click="handleClick">Click Me</button> </div> ` })
上面的代码中,使用defineComponent
函数定义了一个名为MyComponent
的组件,包含props
、setup
和template
三个部分。其中,props
定义了组件的属性,setup
定义了组件的逻辑,template
定义了组件的模板。
注册组件
使用createApp
函数创建Vue实例,并使用component
方法注册组件,示例如下:
import { createApp } from 'vue' import MyComponent from './MyComponent.vue' const app = createApp() app.component('my-component', MyComponent) app.mount('#app')
上面的代码中,使用component
方法将MyComponent
组件注册为my-component
组件,并使用mount
方法将Vue实例挂载到DOM节点上。
需要注意的是,使用defineComponent
函数创建的组件可以直接在component
方法中注册,无需再进行额外的处理。另外,也可以使用defineAsyncComponent
函数定义异步组件,以优化应用的加载性能。
在Vue3中,组件传递数据的方式与Vue2中基本相同,都是通过props
属性进行传递。但是Vue3中对props
进行了一些优化,使得组件传递数据更加方便和灵活。
下面是一个简单的示例,演示了如何在Vue3中使用props
传递数据:
// ChildComponent.vue <template> <div> <h2>{{ title }}</h2> <p>{{ content }}</p> </div> </template> <script> import { defineComponent } from 'vue' export default defineComponent({ name: 'ChildComponent', props: { title: { type: String, required: true }, content: { type: String, default: '' } } }) </script>
// ParentComponent.vue <template> <div> <h1>{{ pageTitle }}</h1> <child-component :title="childTitle" :content="childContent" /> </div> </template> <script> import { defineComponent } from 'vue' import ChildComponent from './ChildComponent.vue' export default defineComponent({ name: 'ParentComponent', components: { ChildComponent }, data() { return { pageTitle: 'Parent Component', childTitle: 'Child Component', childContent: 'Lorem ipsum dolor sit amet' } } }) </script>
在上面的示例中,ChildComponent
组件定义了两个props
:title
和content
。title
属性是必需的,类型为字符串;content
属性是可选的,类型为字符串,如果没有传递则默认为空字符串。
在ParentComponent
组件中,使用child-component
标签引入了ChildComponent
组件,并通过:title
和:content
指令将数据传递给子组件。在data
中定义了pageTitle
、childTitle
和childContent
三个属性,分别用于在父组件和子组件中显示标题和内容。
需要注意的是,在Vue3中,使用props
传递数据时,可以通过.sync
修饰符实现双向绑定,也可以使用v-model
指令简化双向绑定的写法。此外,还可以使用emit
方法向父组件发送事件,实现组件之间的通信。
在Vue3中,组件传递多种数据类型的方式与Vue2中基本相同,都是通过props
属性进行传递。下面是一个示例,演示了如何在Vue3中使用props
传递多种数据类型:
// ChildComponent.vue <template> <div> <h2>{{ title }}</h2> <p>{{ content }}</p> <ul> <li v-for="item in list" :key="item.id">{{ item.name }}</li> </ul> </div> </template> <script> import { defineComponent } from 'vue' export default defineComponent({ name: 'ChildComponent', props: { title: { type: String, required: true }, content: { type: String, default: '' }, list: { type: Array, default: () => [] } } }) </script>
在上面的示例中,ChildComponent
组件定义了三个props
:title
、content
和list
。title
属性是必需的,类型为字符串;content
属性是可选的,类型为字符串,如果没有传递则默认为空字符串;list
属性是可选的,类型为数组,如果没有传递则默认为空数组。
在父组件中,可以通过:title
、:content
和:list
指令将数据传递给子组件。需要注意的是,如果要传递数组类型的数据,可以使用v-bind
指令或简写的:
语法,例如:list="[ { id: 1, name: 'item 1' }, { id: 2, name: 'item 2' } ]"
。
需要注意的是,在Vue3中,使用props
传递数据时,可以通过.sync
修饰符实现双向绑定,也可以使用v-model
指令简化双向绑定的写法。此外,还可以使用emit
方法向父组件发送事件,实现组件之间的通信。
在Vue3中,组件传递props
时,可以使用Props
选项进行校验。Props
选项是一个对象,用于指定组件接受的props
以及其类型、默认值和校验规则等。
下面是一个示例,演示了如何在Vue3中使用Props
选项进行校验:
// ChildComponent.vue <template> <div> <h2>{{ title }}</h2> <p>{{ content }}</p> <ul> <li v-for="item in list" :key="item.id">{{ item.name }}</li> </ul> </div> </template> <script> import { defineComponent } from 'vue' export default defineComponent({ name: 'ChildComponent', props: { // 校验title属性,类型为字符串,必须传递 title: { type: String, required: true }, // 校验content属性,类型为字符串,如果没有传递则默认为空字符串 content: { type: String, default: '' }, // 校验list属性,类型为数组,如果没有传递则默认为空数组 list: { type: Array, default: () => [] }, // 校验count属性,类型为数字,必须大于0 count: { type: Number, validator: (value) => value > 0 } } }) </script>
在上面的示例中,ChildComponent
组件定义了四个props
:title
、content
、list
和count
。其中,title
和count
属性是必需的,类型分别为字符串和数字;content
和list
属性是可选的,类型分别为字符串和数组,如果没有传递则分别默认为空字符串和空数组。此外,count
属性还定义了一个校验规则,即必须大于0。
需要注意的是,在Vue3中,如果一个props
属性没有指定类型,那么它可以接受任何类型的数据。如果需要限制props
属性接受的数据类型,可以使用type
选项指定。如果需要指定多个类型,可以使用数组形式,例如type: [String, Number]
。
此外,如果需要对props
属性进行更复杂的校验,可以使用validator
选项。validator
是一个函数,用于校验props
属性的值是否符合指定的规则。如果校验失败,可以返回false
或抛出异常,Vue会在控制台输出警告信息。
在Vue3中,组件事件可以使用emits
选项进行定义。emits
选项是一个数组,用于指定组件可以触发的事件名称。定义组件事件后,可以使用$emit
方法在组件内部触发事件,并可以在父组件中使用v-on
指令监听事件。
下面是一个示例,演示了如何在Vue3中定义组件事件:
// ChildComponent.vue <template> <button @click="handleClick">{{ buttonText }}</button> </template> <script> import { defineComponent } from 'vue' export default defineComponent({ name: 'ChildComponent', emits: ['click'], props: { buttonText: { type: String, required: true } }, methods: { handleClick() { this.$emit('click') } } }) </script>
在上面的示例中,ChildComponent
组件定义了一个emits
选项,指定了可以触发的click
事件。在组件内部,使用$emit
方法触发click
事件,并在父组件中使用v-on
指令监听该事件。
下面是父组件如何监听ChildComponent
组件触发的click
事件:
// ParentComponent.vue <template> <div> <ChildComponent :buttonText="buttonText" @click="handleClick" /> </div> </template> <script> import { defineComponent } from 'vue' import ChildComponent from './ChildComponent.vue' export default defineComponent({ name: 'ParentComponent', components: { ChildComponent }, data() { return { buttonText: 'Click me' } }, methods: { handleClick() { console.log('ChildComponent clicked') } } }) </script>
在上面的示例中,ParentComponent
组件使用v-on
指令监听ChildComponent
组件触发的click
事件,并在handleClick
方法中输出一条日志。
需要注意的是,在Vue3中,如果一个组件触发了未定义的事件,Vue会在控制台输出警告信息。如果需要禁用这个警告,可以在createApp
方法中传递一个config
选项,设置warnHandler
属性为null
。例如:
import { createApp } from 'vue' const app = createApp({ // ... }) app.config.warnHandler = null app.mount('#app')
在Vue3中,组件事件可以配合v-model
指令使用,用于实现双向数据绑定。要实现v-model
指令,需要在组件中定义一个名为modelValue
的prop,并在emits
选项中指定update:modelValue
事件。
以下是一个示例,演示了如何在Vue3中使用v-model
指令:
// ChildComponent.vue <template> <input :value="modelValue" @input="handleInput" /> </template> <script> import { defineComponent } from 'vue' export default defineComponent({ name: 'ChildComponent', emits: ['update:modelValue'], props: { modelValue: { type: String, required: true } }, methods: { handleInput(event) { this.$emit('update:modelValue', event.target.value) } } }) </script>
在上面的示例中,ChildComponent
组件定义了一个名为modelValue
的prop,并在emits
选项中指定了update:modelValue
事件。在组件内部,使用$emit
方法触发update:modelValue
事件,并传递输入框的值。
下面是父组件如何使用v-model
指令绑定ChildComponent
组件的modelValue
:
// ParentComponent.vue <template> <div> <ChildComponent v-model="inputValue" /> </div> </template> <script> import { defineComponent, ref } from 'vue' import ChildComponent from './ChildComponent.vue' export default defineComponent({ name: 'ParentComponent', components: { ChildComponent }, setup() { const inputValue = ref('') return { inputValue } } }) </script>
在上面的示例中,ParentComponent
组件使用v-model
指令绑定ChildComponent
组件的modelValue
,并将其赋值给inputValue
变量。此时,ChildComponent
组件的输入框和inputValue
变量会实现双向数据绑定。
需要注意的是,v-model
指令实际上是语法糖,相当于同时绑定了一个value
prop和一个update:value
事件。因此,如果需要在组件内部使用v-model
指令,也需要定义一个名为value
的prop,并在emits
选项中指定update:value
事件。
在 Vue3 中,组件数据传递可以通过 props 和 emit 实现。
Props
在 Vue3 中,通过 props
定义组件的属性,可以将数据从父组件传递到子组件。父组件中使用子组件时,可以通过 v-bind
或简写的 :
来绑定属性值。
例如,下面的代码演示了如何使用 props
在父组件中向子组件传递数据:
// ChildComponent.vue <template> <div>{{ message }}</div> </template> <script> import { defineComponent, PropType } from 'vue' export default defineComponent({ name: 'ChildComponent', props: { message: { type: String, required: true } } }) </script> // ParentComponent.vue <template> <div> <ChildComponent :message="parentMessage" /> </div> </template> <script> import { defineComponent } from 'vue' import ChildComponent from './ChildComponent.vue' export default defineComponent({ name: 'ParentComponent', components: { ChildComponent }, data() { return { parentMessage: 'Hello from parent component' } } }) </script>
在上面的代码中,ChildComponent
组件定义了一个名为 message
的 props
,并在模板中使用它来显示数据。在 ParentComponent
组件中,使用 v-bind
或简写的 :
来将父组件的 parentMessage
数据传递给子组件的 message
属性。
Emit
在 Vue3 中,通过 emit
发送自定义事件,可以将数据从子组件传递到父组件。子组件使用 $emit
方法触发事件,并传递数据。父组件中通过 v-on
或简写的 @
来监听事件,并在事件处理函数中获取数据。
例如,下面的代码演示了如何使用 emit
在子组件中向父组件传递数据:
// ChildComponent.vue <template> <button @click="sendMessage">Send message to parent</button> </template> <script> import { defineComponent } from 'vue' export default defineComponent({ name: 'ChildComponent', methods: { sendMessage() { this.$emit('message-sent', 'Hello from child component') } } }) </script> // ParentComponent.vue <template> <div> <ChildComponent @message-sent="handleMessage" /> <div>{{ message }}</div> </div> </template> <script> import { defineComponent, ref } from 'vue' import ChildComponent from './ChildComponent.vue' export default defineComponent({ name: 'ParentComponent', components: { ChildComponent }, setup() { const message = ref('') const handleMessage = (data) => { message.value = data } return { message, handleMessage } } }) </script>
在上面的代码中,ChildComponent
组件定义了一个 sendMessage
方法,在方法中使用 $emit
方法触发 message-sent
事件,并将数据传递给父组件。在 ParentComponent
组件中,使用 v-on
或简写的 @
来监听 message-sent
事件,并在事件处理函数中获取数据。
在 Vue3 中,可以使用 v-bind="$attrs"
透传父组件的 attributes 到子组件,子组件可以通过 inheritAttrs: false
禁用继承父组件的 attributes,然后使用 $attrs
获取透传的 attributes。
例如,下面的代码演示了如何使用 $attrs
透传父组件的 attributes 到子组件:
// ChildComponent.vue <template> <div :class="computedClass" v-bind="$attrs">{{ message }}</div> </template> <script> import { defineComponent } from 'vue' export default defineComponent({ name: 'ChildComponent', inheritAttrs: false, props: { message: { type: String, required: true } }, computed: { computedClass() { return { 'text-red': this.$attrs.color === 'red' } } } }) </script> // ParentComponent.vue <template> <div> <ChildComponent message="Hello from parent component" color="red" /> </div> </template> <script> import { defineComponent } from 'vue' import ChildComponent from './ChildComponent.vue' export default defineComponent({ name: 'ParentComponent', components: { ChildComponent } }) </script>
在上面的代码中,ChildComponent
组件使用 v-bind="$attrs"
透传父组件的 attributes,并在 computedClass
计算属性中根据 color
属性的值来动态设置样式。在 ParentComponent
组件中,使用 color