Vue进阶之旅:组件通信与高级用法深度剖析(组件通信进阶用法)
Vue进阶之旅:组件通信与高级用法深度剖析
文章目录
- Vue进阶之旅:组件通信与高级用法深度剖析
- 一、组件的三大组成部分
- 二、组件通信
- (一)父子关系通信
- (二)非父子关系通信
- 三、综合案例:小黑记事本(组件版)
- 1、拆分组件
- 2、渲染待办任务
- `TodoHeader`组件添加任务功能实现
- 3、添加任务
- 功能需求剖析
- 子组件:数据收集与事件触发
- 父组件:接收数据与添加任务
- 4、**删除任务**
- 功能实现的组件架构
- 子组件`TodoMain`:触发删除事件
- 5、**底部合计和清空功能**
- 底部合计功能:父传子实现数据展示
- 清空功能:子传父触发清空操作
- 6、**持久化存储**
- 持久化存储:使用`watch`监视数据变化
- 7、总结
- 四、进阶语法
在Vue开发的学习过程中,组件化开发是提升效率和代码可维护性的关键。今天深入学习了Vue组件的相关知识,涵盖组件的三大组成部分、组件通信以及一些进阶语法,下面将详细分享学习成果。
一、组件的三大组成部分
组件由结构、样式和逻辑三部分组成,各部分都有其独特的规则和注意点。
- 结构:组件的
<template>
部分定义结构,且有且只能有一个根元素。这确保了组件结构的规范性,利于Vue进行模板解析和渲染。例如:
<template>
<div>
<!-- 组件的具体内容 -->
</div>
</template>
- 样式:组件样式分为全局样式和局部样式。全局样式会影响所有组件,而给组件添加
scoped
属性可创建局部样式,仅作用于当前组件。scoped
的原理是为当前组件内标签添加data-v-hash
值的属性,同时给CSS选择器添加[data-v-hash值]
的属性选择器,使得样式仅对当前组件元素生效。示例如下:
<template>
<div data-v-cbe7c9bc>
<p data-v-cbe7c9bc>我是hm-header</p>
</div>
<div>我是普通盒子</div>
</template>
<style scoped>
div[data-v-cbe7c9bc]{
border: 1px solid #000;
margin: 10px 0;
}
</style>
- 逻辑:组件的逻辑部分在
<script>
中定义,其中data
选项必须是一个函数。这样做能保证每个组件实例维护独立的数据对象,每次创建新组件实例时,都会执行data
函数得到新对象,避免组件间数据相互干扰。如:
<script>
export default {
data() {
return {
count: 100
}
}
}
</script>
二、组件通信
组件通信是组件化开发中实现数据交互的重要手段,不同组件关系对应不同通信方案。
(一)父子关系通信
- 父传子(props):父组件通过
props
向子组件传递数据。在父组件中,以添加属性的方式传值;子组件通过props
选项接收并在模板中使用。代码示例如下:
父组件App.vue
:
<template>
<div id="app">
<!-- 给子组件添加属性传值 -->
<Son :title="msg"></Son>
</div>
</template>
<script>
export default {
data() {
return {
msg: '学前端来黑马'
}
}
}
</script>
子组件Son.vue
:
<template>
<div>
我是子Son组件-{{title}}
</div>
</template>
<script>
export default {
// 子组件内部通过props接收
props: ['title']
}
</script>
- **子传父(
e
m
i
t
)
∗
∗
:子组件利用
‘
emit)**:子组件利用`
emit)∗∗:子组件利用‘emit
通知父组件修改更新。子组件触发事件并传递数据,父组件监听事件并提供处理函数。如下所示: 子组件
Son.vue`:
<template>
<div>
我是Son组件{{title}}
<button @click="changeTitle">修改标题</button>
</div>
</template>
<script>
export default {
props: ['title'],
methods: {
changeTitle() {
// 触发事件并传递数据给父组件
this.$emit('changeTitle', "传智教育")
}
}
}
</script>
父组件App.vue
:
<template>
<div id="app">
<!-- 父组件监听事件 -->
<Son :title="msg" @changeTitle="handleChange"></Son>
</div>
</template>
<script>
import Son from './components/Son'
export default {
data() {
return {
msg: '黑马程'
}
},
methods: {
// 处理子组件传递的数据
handleChange(title) {
this.msg = title
}
}
}
</script>
此外,props
还可进行校验,包括类型校验、非空校验、设置默认值和自定义校验等,为组件的prop
指定验证要求,帮助开发者快速发现错误。示例如下:
props: {
// 类型校验
age: Number,
// 非空校验和默认值
username: {
type: String,
required: true,
default: 'unknown'
},
// 自定义校验
hobby: {
type: Array,
validator(value) {
return value.length > 0
}
}
}
需注意,prop
遵循单向数据流,其数据由外部传入,子组件不能直接修改,要通过$emit
通知父组件修改,确保数据流向清晰。
(二)非父子关系通信
- event bus(事件总线):用于非父子组件间的简易消息传递,复杂场景可使用Vuex。首先创建一个能被所有组件访问的事件总线(空Vue实例),如
utils/EventBus.js
:
import Vue from 'vue'
const Bus = new Vue()
export default Bus
接收方组件监听事件:
created () {
Bus.$on('sendMsg', (msg) => {
this.msg = msg
})
}
发送方组件触发事件:
Bus.$emit('sendMsg', '这是一个消息')
- provide & inject:用于跨层级共享数据。父组件通过
provide
提供数据,子/孙组件通过inject
取值使用。父组件示例:
export default {
data() {
return {
color: 'red',
userInfo: { name: 'John' }
}
},
provide () {
return {
color: this.color,
userInfo: this.userInfo
}
}
}
子/孙组件示例:
export default {
created () {
inject: ['color','userInfo'],
console.log(this.color, this.userInfo)
}
}
三、综合案例:小黑记事本(组件版)
通过小黑记事本案例,实践了组件通信及相关知识。该案例包含拆分组件、渲染任务、添加任务、删除任务、底部合计和清空功能以及持久化存储等核心步骤。
1、拆分组件
新建组件并合理存放,导入并注册使用,构建项目基础结构。
2、渲染待办任务
公共父组件提供数据,通过父传子传递list
,子组件使用v-for
指令渲染任务列表。
####项目结构与组件关系概述
我们的小黑记事本应用包含了多个组件,其中父组件起到了数据管理和协调子组件的作用。在父组件App.vue
中,我们看到如下代码:
<template>
<!-- 主体区域 -->
<section id="app">
<TodoHeader></TodoHeader>
<TodoMain :list="list"></TodoMain>
<TodoFooter></TodoFooter>
</section>
</template>
<script>
import TodoFooter from './components/TodoFooter.vue';
import TodoHeader from './components/TodoHeader.vue';
import TodoMain from './components/TodoMain.vue';
export default {
data () {
return {
list: [
{ id: 1, name: '打篮球' },
{ id: 2, name: '看电影' },
{ id: 3, name: '逛街' },
],
}
},
components : {
TodoHeader,
TodoMain,
TodoFooter
}
}
</script>
父组件管理着任务列表list
数据,并将其传递给TodoMain
组件用于展示。而TodoHeader
组件将承担添加任务的功能,接下来我们重点关注TodoHeader
组件如何实现添加任务的逻辑。
TodoHeader
组件添加任务功能实现
在TodoHeader
组件中,我们将通过v-model
指令收集用户输入的数据,然后通过事件监听和父子组件通信,将新任务传递给父组件,最后由父组件将新任务添加到任务列表中。
TodoHeader
组件模板(template):在TodoHeader
组件的模板部分,添加一个输入框和一个按钮,用于用户输入新任务并触发添加操作。
<template>
<div class="todo-header">
<input v-model="newTask" type="text" placeholder="请输入新任务">
<button @click="addTask">添加任务</button>
</div>
</template>
这里,v-model="newTask"
实现了输入框与组件内部数据newTask
的双向绑定。用户在输入框中输入的内容会实时更新newTask
的值,反之亦然。@click="addTask"
为按钮绑定了一个点击事件,当用户点击按钮时,会触发addTask
方法。
2. TodoHeader
组件逻辑(script):在TodoHeader
组件的<script>
部分,定义newTask
数据以及addTask
方法。
<script>
export default {
data() {
return {
newTask: ''
}
},
methods: {
addTask() {
if (this.newTask.trim() !== '') {
this.$emit('addTaskEvent', { id: Date.now(), name: this.newTask });
this.newTask = '';
}
}
}
}
</script>
在data
函数中,我们初始化了newTask
为空字符串,用于存储用户输入的新任务。addTask
方法首先检查newTask
是否为空(去除首尾空格后),若不为空,则通过this.$emit('addTaskEvent', { id: Date.now(), name: this.newTask })
触发一个自定义事件addTaskEvent
,并将一个包含新任务id
和name
的对象作为参数传递给父组件。这里使用Date.now()
生成一个唯一的id
,保证每个任务的id
不同。最后,将newTask
清空,以便用户输入下一个任务。
3. 父组件接收并添加任务:回到父组件,我们需要监听TodoHeader
组件触发的addTaskEvent
事件,并在事件处理函数中添加新任务到list
数组。在父组件的<script>
部分,添加如下代码:
<script>
import TodoFooter from './components/TodoFooter.vue';
import TodoHeader from './components/TodoHeader.vue';
import TodoMain from './components/TodoMain.vue';
export default {
data () {
return {
list: [
{ id: 1, name: '打篮球' },
{ id: 2, name: '看电影' },
{ id: 3, name: '逛街' },
],
}
},
components : {
TodoHeader,
TodoMain,
TodoFooter
},
methods: {
handleAddTask(newTask) {
this.list.unshift(newTask);
}
}
}
</script>
在父组件的methods
中定义了handleAddTask
方法,该方法接收TodoHeader
组件传递过来的新任务对象,并使用unshift
方法将新任务添加到list
数组的开头。同时,在父组件的模板中,需要为TodoHeader
组件添加事件监听:
<template>
<!-- 主体区域 -->
<section id="app">
<TodoHeader @addTaskEvent="handleAddTask"></TodoHeader>
<TodoMain :list="list"></TodoMain>
<TodoFooter></TodoFooter>
</section>
</template>
@addTaskEvent="handleAddTask"
表示当TodoHeader
组件触发addTaskEvent
事件时,会调用父组件的handleAddTask
方法,并将TodoHeader
组件传递的参数(即新任务对象)作为handleAddTask
方法的参数。
通过以上步骤,我们成功实现了小黑记事本的添加任务功能。用户在TodoHeader
组件的输入框中输入任务名称,点击添加按钮后,新任务会被添加到父组件的任务列表list
中,并在TodoMain
组件中展示出来。这个功能的实现,充分展示了Vue中组件通信和数据绑定的强大功能,为构建复杂的应用奠定了基础。
3、添加任务
通过v-model
收集数据,监听事件,子传父传递任务,父组件使用unshift
方法添加任务。
功能需求剖析
小黑记事本的添加任务功能要求用户在输入框输入任务名称后,点击添加按钮或按下回车键,新任务就能添加到任务列表顶部。为实现此功能,我们采用父子组件协同工作的模式。父组件负责管理任务列表数据,子组件TodoHeader
承担用户输入的收集与事件触发。
子组件:数据收集与事件触发
TodoHeader
组件代码如下:
<template>
<!-- 输入框 -->
<header class="header">
<h1>小黑记事本</h1>
<input @keyup.enter="HandleAdd" v-model="todoName" placeholder="请输入任务" class="new-todo" />
<button @click="HandleAdd" class="add">添加任务</button>
</header>
</template>
<script>
export default {
data () {
return {
todoName :''
}
},
methods: {
HandleAdd () {
if(this.todoName.trim() === ''){
alert('任务名称不能为空!')
return
}
this.$emit('add' , this.todoName)
this.todoName = ''
}
},
}
</script>
在模板部分,<input>
标签使用v-model="todoName"
指令,在用户输入时自动将内容同步到组件的todoName
数据属性,实现双向数据绑定。同时监听keyup.enter
事件,当用户按下回车键时触发HandleAdd
方法。添加按钮通过@click="HandleAdd"
绑定点击事件,同样触发HandleAdd
方法。
在script
部分,data
函数初始化todoName
为空字符串。HandleAdd
方法先检查todoName
是否为空,若为空则弹出提示,阻止后续操作。若有输入内容,则通过this.$emit('add', this.todoName)
触发名为add
的自定义事件,并将todoName
的值作为参数传递给父组件,随后清空todoName
,方便用户输入下一个任务。
父组件:接收数据与添加任务
父组件代码如下:
<template>
<!-- 主体区域 -->
<section id="app">
<TodoHeader @add="handleadd" ></TodoHeader>
<TodoMain :list="list"></TodoMain>
<TodoFooter></TodoFooter>
</section>
</template>
<script>
import TodoFooter from './components/TodoFooter.vue';
import TodoHeader from './components/TodoHeader.vue';
import TodoMain from './components/TodoMain.vue';
export default {
data () {
return {
list: [
{ id: 1, name: '打篮球' },
{ id: 2, name: '看电影' },
{ id: 3, name: '逛街' },
],
}
},
components : {
TodoHeader,
TodoMain,
TodoFooter
},
methods: {
handleadd (todoName) {
this.list.unshift({
id : +new Date(),
name : todoName
})
}
},
}
</script>
在模板中,<TodoHeader>
标签通过@add="handleadd"
监听TodoHeader
组件触发的add
事件,事件触发时调用父组件的handleadd
方法,并将TodoHeader
组件传递的任务名称作为参数传入。
在script
部分,handleadd
方法接收子组件传来的todoName
,通过unshift
方法将新任务添加到list
数组开头。新任务对象包含一个由当前时间戳生成的唯一id
和接收到的todoName
。
通过父子组件的紧密协作,小黑记事本的添加任务功能得以顺利实现。用户输入任务名称,点击按钮或按回车键,任务便会快速添加到任务列表顶部。这一功能不仅展示了Vue组件通信与数据处理的便捷性,也为构建复杂交互应用提供了基础思路。在实际开发中,我们可以基于此功能进一步拓展,如添加任务验证规则、优化提示信息,使应用更加完善,为用户带来更好的体验。
4、删除任务
监听删除操作并携带id
,子传父传递id
,父组件利用filter
方法删除对应任务。通过删除任务这个功能,用户可以灵活管理自己的任务列表,删除不再需要的任务。下面我们来详细讲解该功能是如何通过Vue实现的,其核心流程为监听删除操作并携带任务id
,子组件将id
传递给父组件,父组件利用filter
方法删除对应任务。
功能实现的组件架构
小黑记事本应用由多个组件协同工作,其中TodoMain
组件负责展示任务列表,并且提供删除任务的交互操作;TodoMain
组件作为子组件,将用户的删除操作传递给父组件;父组件负责实际的任务数据管理,接收到删除指令后执行删除操作。在父组件中,相关代码如下:
<template>
<!-- 主体区域 -->
<section id="app">
<TodoHeader @add="handleadd"></TodoHeader>
<TodoMain @del="handleDel" :list="list"></TodoMain>
<TodoFooter></TodoFooter>
</section>
</template>
<script>
import TodoFooter from './components/TodoFooter.vue';
import TodoHeader from './components/TodoHeader.vue';
import TodoMain from './components/TodoMain.vue';
export default {
data() {
return {
list: [
{ id: 1, name: '打篮球' },
{ id: 2, name: '看电影' },
{ id: 3, name: '逛街' },
],
};
},
components: {
TodoHeader,
TodoMain,
TodoFooter
},
methods: {
handleadd(todoName) {
this.list.unshift({
id: +new Date(),
name: todoName
});
},
handleDel(id) {
this.list = this.list.filter(item => item.id !== id);
}
},
};
</script>
在上述代码中,父组件通过TodoMain
标签上的@del="handleDel"
监听TodoMain
组件触发的del
事件,并将触发事件时传递的参数(即任务id
)传递给handleDel
方法。当handleDel
方法被调用时,它使用filter
方法创建一个新数组,该数组排除了id
与传入参数id
相等的任务对象,从而实现删除对应任务的功能。
子组件TodoMain
:触发删除事件
TodoMain
组件的模板和脚本代码如下:
<template>
<!-- 列表区域 -->
<section class="main">
<ul class="todo-list">
<li v-for="(item, index) in list" :key="item.id" class="todo">
<div class="view">
<span class="index">{{index + 1}}.</span> <label>{{ item.name }}</label>
<button @click="handleDel(item.id)" class="destroy"></button>
</div>
</li>
</ul>
</section>
</template>
<script>
export default {
props: {
list : Array
},
methods: {
handleDel(id) {
this.$emit('del', id);
}
},
};
</script>
在模板中,每个任务列表项都包含一个删除按钮,通过@click="handleDel(item.id)"
绑定点击事件,当用户点击删除按钮时,会触发handleDel
方法,并将当前任务的id
作为参数传入。在script
部分的handleDel
方法中,通过this.$emit('del', id)
触发del
事件,并将任务id
传递给父组件。这样,父组件就能接收到要删除的任务id
,并执行相应的删除操作。
通过以上父子组件的配合,小黑记事本的删除任务功能得以顺利实现。当用户点击任务列表中的删除按钮时,TodoMain
组件会将对应的任务id
传递给父组件,父组件则利用filter
方法从任务列表中删除该任务。这一功能的实现,充分展示了Vue组件之间高效的数据通信和灵活的交互能力。在实际开发中,我们可以根据需求进一步优化该功能,比如添加删除确认提示框,防止用户误删任务,提升用户体验。
5、底部合计和清空功能
底部合计通过父传子传递list
实现展示;清空功能由子传父通知父组件,父组件执行清空操作。
在小黑记事本的开发过程中,底部合计和清空功能以及数据持久化存储是提升用户体验和数据管理的关键部分。这两个功能的实现,不仅让用户能直观了解任务数量并便捷管理任务列表,还确保数据在页面刷新或关闭后依然存在。下面我们就来详细探讨这两个功能的实现思路与代码逻辑。
底部合计功能:父传子实现数据展示
小黑记事本的底部区域由TodoFooter
组件负责呈现,它需要展示任务的合计数量。而任务数据存储在父组件中,因此需要通过父传子的方式将任务列表list
传递给TodoFooter
组件。
- 父组件传递数据:在父组件中,代码如下:
<template>
<!-- 主体区域 -->
<section id="app">
<TodoHeader @add="handleadd"></TodoHeader>
<TodoMain @del="handleDel" :list="list"></TodoMain>
<TodoFooter @clear="handleClear" :list="list"></TodoFooter>
</section>
</template>
<script>
import TodoFooter from './components/TodoFooter.vue';
import TodoHeader from './components/TodoHeader.vue';
import TodoMain from './components/TodoMain.vue';
export default {
data() {
return {
list: JSON.parse(localStorage.getItem('list')) || [
{ id: 1, name: '打篮球' },
{ id: 2, name: '看电影' },
{ id: 3, name: '逛街' },
],
};
},
components: {
TodoHeader,
TodoMain,
TodoFooter
},
methods: {
handleadd(todoName) {
this.list.unshift({
id: +new Date(),
name: todoName
});
},
handleDel(id) {
this.list = this.list.filter(item => item.id !== id);
},
handleClear() {
this.list = [];
}
},
watch: {
list: {
deep: true,
handler(newValue) {
localStorage.setItem('list', JSON.stringify(newValue));
}
}
},
};
</script>
在父组件的模板中,通过TodoFooter
标签上的:list="list"
,将父组件中的list
数据传递给TodoFooter
组件。这样,TodoFooter
组件就能获取到任务列表,从而进行底部合计数量的计算和展示。
2. 子组件展示数据:TodoFooter
组件代码如下:
<template>
<!-- 统计和清空 -->
<footer class="footer">
<!-- 统计 -->
<span class="todo-count">合 计:<strong> {{list.length}} </strong></span>
<!-- 清空 -->
<button @click="clear" class="clear-completed">
清空任务
</button>
</footer>
</template>
<script>
export default {
props: {
list: Array
},
methods: {
clear() {
this.$emit('clear');
}
},
};
</script>
在TodoFooter
组件中,通过props
接收父组件传递过来的list
。在模板中,使用{{list.length}}
直接展示任务列表的长度,也就是任务的合计数量。每当任务列表发生变化,父组件传递的list
更新,TodoFooter
组件中的合计数量也会实时更新,为用户提供最新的任务统计信息。
清空功能:子传父触发清空操作
清空功能允许用户一键删除所有任务,这需要子组件TodoFooter
触发事件通知父组件执行清空任务列表的操作。
- 子组件触发事件:在
TodoFooter
组件中,当用户点击“清空任务”按钮时,会触发clear
方法。代码如下:
<template>
<!-- 统计和清空 -->
<footer class="footer">
<!-- 统计 -->
<span class="todo-count">合 计:<strong> {{list.length}} </strong></span>
<!-- 清空 -->
<button @click="clear" class="clear-completed">
清空任务
</button>
</footer>
</template>
<script>
export default {
props: {
list: Array
},
methods: {
clear() {
this.$emit('clear');
}
},
};
</script>
clear
方法通过this.$emit('clear')
触发一个名为clear
的自定义事件,该事件会向上传递给父组件。
2. 父组件执行清空:父组件监听TodoFooter
组件触发的clear
事件,并执行相应的清空操作。在父组件中:
<template>
<!-- 主体区域 -->
<section id="app">
<TodoHeader @add="handleadd"></TodoHeader>
<TodoMain @del="handleDel" :list="list"></TodoMain>
<TodoFooter @clear="handleClear" :list="list"></TodoFooter>
</section>
</template>
<script>
import TodoFooter from './components/TodoFooter.vue';
import TodoHeader from './components/TodoHeader.vue';
import TodoMain from './components/TodoMain.vue';
export default {
data() {
return {
list: JSON.parse(localStorage.getItem('list')) || [
{ id: 1, name: '打篮球' },
{ id: 2, name: '看电影' },
{ id: 3, name: '逛街' },
],
};
},
components: {
TodoHeader,
TodoMain,
TodoFooter
},
methods: {
handleadd(todoName) {
this.list.unshift({
id: +new Date(),
name: todoName
});
},
handleDel(id) {
this.list = this.list.filter(item => item.id !== id);
},
handleClear() {
this.list = [];
}
},
watch: {
list: {
deep: true,
handler(newValue) {
localStorage.setItem('list', JSON.stringify(newValue));
}
}
},
};
</script>
在父组件的模板中,TodoFooter
标签上的@clear="handleClear"
监听clear
事件,当事件触发时,会调用父组件的handleClear
方法。在handleClear
方法中,通过this.list = []
将任务列表清空,实现了清空所有任务的功能。
6、持久化存储
持久化存储:使用watch
监视数据变化
为了保证任务数据在页面刷新或关闭后不会丢失,我们使用watch
来监视任务列表list
的变化,并将数据持久化存储到本地。在父组件中:
<script>
import TodoFooter from './components/TodoFooter.vue';
import TodoHeader from './components/TodoHeader.vue';
import TodoMain from './components/TodoMain.vue';
export default {
data() {
return {
list: JSON.parse(localStorage.getItem('list')) || [
{ id: 1, name: '打篮球' },
{ id: 2, name: '看电影' },
{ id: 3, name: '逛街' },
],
};
},
components: {
TodoHeader,
TodoMain,
TodoFooter
},
methods: {
handleadd(todoName) {
this.list.unshift({
id: +new Date(),
name: todoName
});
},
handleDel(id) {
this.list = this.list.filter(item => item.id !== id);
},
handleClear() {
this.list = [];
}
},
watch: {
list: {
deep: true,
handler(newValue) {
localStorage.setItem('list', JSON.stringify(newValue));
}
}
},
};
</script>
通过watch
监听list
的变化,当list
发生改变时,handler
函数会被触发。在handler
函数中,使用localStorage.setItem('list', JSON.stringify(newValue))
将更新后的任务列表存储到本地。JSON.stringify
方法将任务列表转换为JSON字符串进行存储,在页面加载时,通过JSON.parse(localStorage.getItem('list'))
从本地获取数据并转换为数组,赋值给list
,从而实现数据的持久化。
通过底部合计、清空功能以及持久化存储的实现,小黑记事本在功能完整性和用户体验上都得到了极大的提升。这些功能的实现,不仅加深了我们对Vue组件通信和数据管理的理解,也为开发更加复杂的应用奠定了坚实的基础。在实际项目中,我们可以根据需求进一步扩展和优化这些功能,例如添加动画效果、优化存储逻辑等,为用户打造更优质的应用体验。
7、总结
四、进阶语法
- v-model原理及应用:
v-model
本质是语法糖,在输入框上是value
属性和input
事件的合写,实现数据双向绑定。在表单类组件封装中,可通过拆解v-model
实现子组件和父组件数据双向绑定。父组件传递prop
数据,子组件监听输入并传值给父组件修改。简化代码时,子组件通过value
接收props
,触发input
事件;父组件直接用v-model
绑定数据。示例如下:
子组件BaseSelect.vue
:
<template>
<select :value="value" @change="handleChange">
<!-- 选项 -->
</select>
</template>
<script>
export default {
props: {
value: String
},
methods: {
handleChange (e) {
this.$emit('input', e.target.value)
}
}
}
</script>
父组件使用:
<BaseSelect v-model="selectId"></BaseSelect>
- .sync修饰符:可实现子组件与父组件数据双向绑定,且
prop
属性名可自定义。其本质是:属性名
和@update:属性名
的合写,常用于封装弹框类基础组件。例如:
父组件:
<BaseDialog :visible.sync="isShow" />
等同于:
<BaseDialog :visible="isShow" @update:visible="isShow = $event" />
子组件:
props: {
visible: Boolean
},
methods: {
closeDialog() {
this.$emit('update:visible', false)
}
}
- **ref和
r
e
f
s
∗
∗
:用于获取
D
O
M
元素或组件实例,查找范围为当前组件内,相比
‘
d
o
c
u
m
e
n
t
.
q
u
e
r
y
S
e
l
e
c
t
o
r
‘
更精确稳定。获取
D
O
M
元素时,给目标标签添加
‘
r
e
f
‘
属性,在合适时机通过
‘
t
h
i
s
.
refs**:用于获取DOM元素或组件实例,查找范围为当前组件内,相比`document.querySelector`更精确稳定。获取DOM元素时,给目标标签添加`ref`属性,在合适时机通过`this.
refs∗∗:用于获取DOM元素或组件实例,查找范围为当前组件内,相比‘document.querySelector‘更精确稳定。获取DOM元素时,给目标标签添加‘ref‘属性,在合适时机通过‘this.refs.xxx
获取。获取组件实例同理,给目标组件添加
ref`属性后,可调用组件对象的方法。例如:
获取DOM元素:
<template>
<div ref="chartRef">我是渲染图表的容器</div>
</template>
<script>
export default {
mounted () {
console.log(this.$refs.chartRef)
}
}
</script>
获取组件实例:
<template>
<BaseForm ref="baseForm"></BaseForm>
</template>
<script>
export default {
methods: {
handleSubmit() {
this.$refs.baseForm.resetForm()
}
}
}
</script>
- **
n
e
x
t
T
i
c
k
∗
∗
:由于
V
u
e
异步更新
D
O
M
,在数据更新后立即操作
D
O
M
可能无法成功。
‘
nextTick**:由于Vue异步更新DOM,在数据更新后立即操作DOM可能无法成功。`
nextTick∗∗:由于Vue异步更新DOM,在数据更新后立即操作DOM可能无法成功。‘nextTick
可在DOM更新后执行函数体。例如,编辑标题时,希望编辑框在显示后自动聚焦,可使用
$nextTick`:
<template>
<div>
<h1 v-if="!isShowEdit">{{title}}</h1>
<input v-if="isShowEdit" ref="inp" />
<button @click="editTitle">编辑</button>
</div>
</template>
<script>
export default {
data() {
return {
title: '大标题',
isShowEdit: false
}
},
methods: {
editTitle() {
this.isShowEdit = true
this.$nextTick(() => {
this.$refs.inp.focus()
})
}
}
}
</script>
通过今天的学习,对Vue组件开发有了更深入的理解,组件通信和进阶语法为构建复杂应用提供了强大工具。在实际项目中,应根据具体场景选择合适的方法,不断优化代码结构和性能。