Vue学习四:组件的三大组成部分、组件通信和进阶语法
一、组件的三大组成部分
<template>
里面只能有一个根元素
<style>
全局样式(默认):影响所有组件
局部样式: scoped 下样式,只作用于当前组件
<script>
el根实例独有, data是一个函数,其他配置项一致
二、scoped设置局部样式
默认情况:写在组件中的样式会全局生效→因此很容易造成多个组件之间的样式冲突问题。
1.全局样式:默认组件中的样式会作用到全局
2.局部样式:可以给组件加上scoped属性,可以让样式只作用于当前组件
scoped原理?
1.当前组件内标签都被添加data-v-hash值的属性
2. css选择器都被添加[data-v-hash值]的属性选择器
最终效果:必须是当前组件的元素,才会有这个自定义属性,才会被这个样式作用到
三、data函数
一个组件的data选项必须是一个函数。→保证每个组件实例,维护独立的一份数据对象。
每次创建新的组件实例,都会新执行一次data函数,得到一个新对象。
组件内提供数据的是data函数,每个组件实例都会开辟新的一个空间存储这个数据,组件实例之间互不影响,也就是说data数据作用域只在组件实例范围内。
四、组件通信
组件关系:父子关系使用props&$emit
父传子props:
①父中给子添加属性传值②子props接收③使用
子传父$emit:
①子$emit发送消息②父中给子添加消息监听③父中实现处理函数
案例如下:父组件中有个数据myTitle传给子组件,子组件将数据接受并展示到页面上;子组件上加个按钮,按钮加个点击事件,点了以后会将通知和一个myTitle的新值传给父组件,父组件监听到通知后将myTitle更新。因为使用v-bind动态传递数据,父组件中数据更新,单向数据流,子组件接受的数据也更新并展示。
父组件代码

<template> <div class="App"> 父组件 <AlongHeader :title="myTitle" :hobby="hobby" @newTitle="newTitleFn"></AlongHeader> </div> </template> <script> import AlongHeader from './components/AlongHeader.vue' export default{ components:{ AlongHeader, }, data(){ return { myTitle:'阿龙学前端', hobby:['唱','跳','rap','篮球'] } }, methods:{ newTitleFn(newValue){ this.myTitle = newValue } } } </script> <style> .App{ border: 2px solid black; margin: 10px; } </style>
子组件代码

<template> <div class="AlongHeader"> 子组件 {{title}} <button @click="changeTitle"></button> <p>{{hobby[0]}}、{{hobby[1]}}、{{hobby[2]}}、{{hobby[3]}}</p> </div> </template> 、 <script> export default{ props:['title','hobby'], methods:{ changeTitle(){ this.$emit('newTitle','好好学习') } } } </script> <style scoped> .AlongHeader{ border: 1px solid black; margin: 10px; } </style>
prop
Prop定义:组件上注册的一些 自定义属性(结合v-bind成为动态属性)
Prop作用:向子组件传递数据
特点:
●可以传递任意数量的prop
●可以传递任意类型的prop
props校验
作用:为组件的prop指定验证要求,不符合要求,控制台就会有错误提示→帮助开发者,快速发现错误
语法
props:{
校验的属性名:{
type:类型,
required:true,//是否必填
default:默认值,
validator(){
//自定义校验逻辑
return 是否通过校验
}
}
}
prop & data.单向数据流
共同点:都可以给组件提供数据。
区别:
●data 的数据是自己的→随便改
●prop 的数据是外部的→不能直接改, 要遵循单向数据流
单向数据流:父级prop的数据更新,会向下流动,影响子组件。这个数据流动是单向的。
组件关系非父子-event bus事件总线
作用:非父子组件之间,进行简易消息传递。(复杂场景→Vuex)
1.创建一个都能访问到的事件总线(空Vue实例)→utils/EventBus.js
2. A组件(接收方),监听Bus实例的事件
3. B组件(发送方),触发Bus实例的事件
案例代码:A和B为父组件中的两个并列组件,没有父子关系。A组件为发送方,B组件为接受方,要求点击A组件的发送按钮,B组件能接受到“下雨了,快收衣服”这条消息。
创建一个js文件,里面创建一个空的vue实例,然后将实例导出,充当事件总线。

import Vue from 'vue'
const Bus = new Vue()
export default Bus
A组件中按钮添加点击事件,点击事件中会使用$emit函数向刚创建的vue实例发送消息和发送的数据,这个$emit发送的位置和父子关系不一样(父子关系是this)

<template> <div class="AlongHeader"> 发送方(A组件) <button @click="clickSend">发送消息</button> </div> </template> 、 <script> import Bus from '../units/EventBus.js' export default{ methods:{ clickSend(){ Bus.$emit('Message','下雨了,快收衣服') } } } </script> <style scoped> .AlongHeader{ height: 200px; width: 200px; border: 1px solid black; margin: 10px; } </style>
B组件使用created钩子,在创建阶段就开始监听vue,一旦监听到有自己需要的消息就开始执行一系列的操作。

<template> <div class="AlongMain"> 接收方(B组件) <p>{{msg}}</p> </div> </template> <script> import Bus from '../units/EventBus.js' export default{ //监听Bus中的事件,在created数据创建完成的时候就开始监听 created(){ Bus.$on('Message',(msg)=>{ this.msg = msg }) }, data(){ return { msg:'' } } } </script> <style> .AlongMain{ height: 200px; width: 200px; border: 1px solid black; margin: 10px; } </style>
四、v-model详解
v-model原理
原理: v-model本质上是一个语法糖。例如应用在输入框上,就是value属性和input事件的合写。
v-model="msg"等价于:value="msg" @input="msg=$event.target.value"
作用:提供数据的双向绑定
①数据变,视图跟着变:value
②视图变,数据跟着变@input
注意: $event 用于在模板中,获取事件的形参
表单类组件封装& v-model简化代码
1.表单类组件封装
①父传子:数据应该是父组件props传递过来的,v-model 拆解绑定数据
②子传父:监听输入,子传父传值给父组件修改
对于父组件中传来的数据,子组件中是不能直接使用v-model关联的,因为数据单向流动,子组件不能直接修改父组件中的数据。
案例如下:父组件向子组件选择框传递一个初始值,子组件中的选择框选择结果也会传递给父组件,然后父组件修改值。当父组件给子组件传递值绑定的动态属性为:value,给动态属性赋值的数据变量名为value,接受消息为@input,子组件传的通知名为input。这样刚好符合了v-model的语法,父组件中可以使用v-model简化代码。但没必要那么可以得去为了v-model而拼凑,只需完成从父组件向子组件传递数据,然后子组件能将视图改变的值传回父组件,然后父组件修改数据,修改好的数据再传到子组件渲染,这样其实就已经完成了父组件数据和子组件视图的双向绑定。
父组件

<template> <div class="App"> <AlongHeader :value='value' @input="value=$event"></AlongHeader> </div> </template> <script> import AlongHeader from './components/AlongHeader.vue' export default{ components:{ AlongHeader, }, data(){ return { value:2 } } } </script> <style> .App{ height: 500px; width: 500px;; border: 2px solid black; margin: 10px; } </style>
子组件

<template> <div class="AlongHeader"> <select :value='value' @change="changeHobby"> <option value="1">唱</option> <option value="2">跳</option> <option value="3">rap</option> <option value="4">篮球</option> </select> </div> </template> <script> export default{ props:['value'], methods:{ changeHobby(e){ this.$emit('input',e.target.value) } } } </script> <style> .AlongHeader{ height: 200px; width: 200px; border: 1px solid black; margin: 10px; } </style>
ref和$refs
作用:利用ref和$refs可以用于获取dom元素,或组件实例
特点:查找范围→当前组件内(更精确稳定)
①获取dom:
1.目标标签-添加ref属性
2.恰当时机,通过this.$refs.xxx,获取目标标签
Vue异步更新、$nextTick
1. Vue是异步更新DOM的
2.想要在DOM更新完成之后做某件事,可以使用$nextTick
案例需求:编辑标题,编辑框自动聚焦1.点击编辑,显示编辑框2.让编辑框,立刻获取焦点。如果单纯地按照上面的步骤来是完成不了的,因为创建dom的速度比获取焦点的速度要慢得多,而vue的dom又是异步的,所以连dom都没有就获取dom焦点肯定是不行的。所以需要将dom完成后要做的事包裹在$nextTick里。

<template> <div class="App"> <div v-if="isShow==true"> <input ref="inp" type="text"> <button type="button">确认</button> </div> <div v-else> <span>{{title}}</span> <button @click="change" type="button">编辑</button> </div> </div> </template> <script> export default{ data(){ return { title:'大标题', isShow:false, } }, methods:{ change(){ this.isShow = true this.$nextTick(()=>{ this.$refs.inp.focus() }) } } } </script> <style> .App{ height: 500px; width: 500px;; border: 2px solid black; margin: 10px; } </style>
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY