文章目录
- 1.组件的三大组成部分 - 注意点说明
- 2.组件的样式冲突(用 scoped 解决)
- 3.data是一个函数
- 4.组件通信
- 1.什么是组件通信?
- 2.不同的组件关系 和 组件通信方案分类
- 5.prop详解
- prop 校验
- ①类型校验
- ②完整写法(类型,非空,默认,自定义)
- prop & data 单项数据流
- 6.非父子通信(拓展)- event bus 事件总线
- 非父子通信-provide-inject(拓展)
1.组件的三大组成部分 - 注意点说明
组件的三大组成部分 - 注意点说明👇
①结构 < template >:只能有一个根元素
②样式 < style >:
全局样式(默认):影响所有组件
局部样式:scoped
样式下,只作用于当前组件
③逻辑 < script >:el根实例独有,data是一个函数,其他配置项一致
(下面将详细的逐一地学一下②③部分)
2.组件的样式冲突(用 scoped 解决)
默认情况:写在组件中的样式会 全局生效→ 因此很容易造成多个组件之间的样式冲突问题。
scoped原理:
- 1.给当前组件模板的所有元素,都会被添加上一个自定义属性
data-v-hash
值(且同一个组件中的hash值是一样的,所以它就可以用不同的hash
值区分不同的组件)
- 2.css选择器的后面,被自动处理,添加上了属性选择器
- 最终效果:必须是当前组件的元素,才会有这个自定义属性,才会被这个样式作用到
//BaseOne.vue
<template><div>BaseOne</div>
</template><script>
export default{
}
</script>
<style>
/* 默认的style样式,会作用于全局 */div {border: 3px solid blue;margin: 30px;}
</style>//BaseTwo.vue
<template><div>BaseTwo</div>
</template>
<script>
export default{
}
</script>
<style>
</style>//App.vue
<template><div id="app"><BaseOne></BaseOne><BaseTwo></BaseTwo></div>
</template><script>
import BaseOne from './components/BaseOne.vue';
import BaseTwo from './components/BaseTwo.vue'
export default{name:'App',components:{BaseOne:BaseOne,BaseTwo:BaseTwo}}
</script><style></style>
效果图如下
👇加上scoped进行修改
<style scoped>
/* 1.默认的style样式,会作用于全局2.加上scoped属性的style样式,只会作用于当前组件👉局部样式
组件应该有着自己独立的样式,推荐加上scoped*/div {border: 3px solid blue;margin: 30px;}
</style>
效果图如下
3.data是一个函数
①一个组件如果要提供数据,也是通过data
②一个组件的data
选项必须是一个函数
。👉 保证每一个组件实例,维护独立
的一份数据对象。
每次创建新的组件实例,都会新执行一次data函数,得到一个新的对象
<template><div class="base-count"><button @click="count--">-</button><span>{{ count }}</span><button @click="count++">+</button></div>
</template><script>
export default{// data必须是一个函数 → 保证每个组件实例data (){return{count: 999}}
}
</script>
<style scoped>
.base-count{margin: 20px;
}
</style>
图文总结👇
4.组件通信
1.什么是组件通信?
组件通信,就是指组件与组件之间的数据传递
- 组件的数据是独立的,无法直接访问其他组件的数据。
- 想用其他组件的数据 → 组件通信
思考:1.组件之间有哪些关系? 2.对应的组件通信方案有哪几类?
2.不同的组件关系 和 组件通信方案分类
- 组件关系分类:
1.父子关系:组件A包裹组件B.那AB就是父子关系
2.非父子关系:A包裹B和C,所以BC是非父子关系
<!-- App.vue -->
<template><div style="border:3px solid #000;margin: 10px;">我是App组件<!-- 老爹的这个组件,儿子要用 --><!-- 1. 给组件标签,添加属性的方式传值--><Son :title="myTitle"></Son></div>
</template><script>import BaseSon from './components/BaseSon.vue'
export default{data () {return {myTitle:'黑马程序员'}},components:{BaseSon:BaseSon,}
}
</script>
<style>
</style><!-- BaseSon.vue -->
<template><div style="border:3px solid #000;margin: 10px;"><!-- 3.渲染使用 -->我是组件Son {{ title }}</div>
</template><script>
export default{// 2.通过props进行接受,且props里面的名字要和老爹里面要传过来的东西同名props:['title']//3.这边接收完毕,页面中就可以直接渲染使用了
}
</script>
<style>
</style>
<!-- App.vue -->
<template><div style="border:3px solid #000;margin: 10px;">我是App组件<!-- 老爹的这个组件,儿子要用 --><!-- 1. 给组件标签,添加属性的方式传值--><Son :title="myTitle" @changeTitle="handelChange"></Son></div>
</template><script>import BaseSon from './components/BaseSon.vue'
export default{data () {return {myTitle:'黑马程序员'}},components:{BaseSon:BaseSon,},methods:{//3.提供处理函数,提供逻辑handelChange (newTitle) {this.myTitle = newTitle}}
}
</script>
<style>
</style><!-- BaseSon.vue -->
<template><div style="border:3px solid #000;margin: 10px;">我是组件Son {{ title }}<button @click="changeFn">修改titile</button></div>
</template><script>
export default{props:['title'],methods:{changeFn () {//1.通过$emit,向父组件发送消息通知this.$emit('changeTitle','传智教育')}}
}
</script>
<style>
</style>
图文总结👇
5.prop详解
什么是prop
Prop定义:组件上注册一些自定义属性
prop作用:向子组件传递数据
特点:
- 可以传递任意数量的
prop
- 可以传递任意类型的
prop
写法:1.在父组件中提供一些数据(字符串,对象,布尔值等)
2.然后通过给当前这个组件以添加属性的方式传值
3.传了之后,在子组件内部,就可以通过props
接收
4.最终一面当中就可以做渲染了
👇
App.vue<template><div class="app"><!-- 父传子 --><UserInfo:username="username":age="age":isSingle="isSingle":car="car":hobby="hobby"></UserInfo></div>
</template><script>
import UserInfo from './components/UserInfo.vue'
export default {data() {return {username: '小帅',age: 28,isSingle: true,car: {brand: '宝马',},hobby: ['篮球', '足球', '羽毛球'],}},components: {UserInfo,},
}
</script><style>
</style>UserInfo.vue<template><div class="userinfo"><h3>我是个人信息组件</h3><div>姓名:{{ username }}</div><div>年龄:{{ age }}</div><div>是否单身:{{ isSingle ? 'yes' : 'no' }}</div><div>座驾:{{ car.brand }}</div><div>兴趣爱好:{{ hobby.join('、') }}</div> <!-- 加上 .join('、') 就可以给三个爱好的文字之间加上顿号 --></div>
</template><script>
export default {props:['username','age','isSingle','car','hobby']
}
</script><style>
.userinfo {width: 300px;border: 3px solid #000;padding: 20px;
}
.userinfo > div {margin: 20px 10px;
}
</style>
prop 校验
思考:组件的prop可以乱传吗?(不可以,比如一个百分比数字不可以给它传布尔值)
作用:为组件的prop指定验证要求,不符合要求,控制台就会有错误提示 → 帮助开发者快速发现错误
语法:
①类型校验
②非空校验
③默认值
④自定义校验
①类型校验
需要将原来props接收数据的写法改成对象,对象里面就可以写键值对了。值就是对应的类型要求:比如传进来的是Number类型,就写number
App.vue
<template><div class="app"><BaseProgress :w="width"></BaseProgress></div>
</template><script>
import BaseProgress from './components/BaseProgress.vue'
export default {data() {return {width: 'abc',}},components: {BaseProgress,},
}
</script><style>
</style>BaseProgress.vue
<template><div class="base-progress"><div class="inner" :style="{ width: w + '%' }"><span>{{ w }}%</span></div></div>
</template><script>
export default {// props: ["w"],props:{w:Number//Number String Boolean Array Object Function}// 1.基础写法(类型校验)// 2.完整写法(类型、是否必填、默认值、自定义校验)
}
</script><style scoped>
省略
</style>
②完整写法(类型,非空,默认,自定义)
<script>
export default {// props: ["w"]//1.基础校验(类型校验)// props:{// w:Number//Number String Boolean Array Object Function// }// 2.完整写法(类型、是否必填、默认值、自定义校验)props:{w:{//对类型有要求type:Number,required:true,//需要非空default:0,//如果希望有一个默认值。而不是必填validator (value){//写这样一个方法,这个方法的形参,是可以拿到//你传递过来的prop传值的,拿到传值,就可以对他进行判断if (value >=0 && value <=100) {return true}return false}}}
}
</script>
prop & data 单项数据流
共同点:都可以给组件提供数据
区别:
- data的数据是自己的 → 随便改
- prop的数据是外部的 → 不能直接改 ,要循环单向数据流
App.vue
<template><div class="app"><BaseCount></BaseCount></div>
</template><script>
import BaseCount from './components/BaseCount.vue'
export default {components:{BaseCount},data(){return {count:100}},methods:{}
}
</script><style></style>erzi
<template><div class="base-count"><button @click="count--">-</button><span>{{ count }}</span><button @click="count++">+</button></div>
</template><script>
export default {// 1.自己的数据随便修改 (谁的数据 谁负责)// 意思就是,打开网页,想加就加,想减就减// data () {// return {// count: 100,// }// },// 2.外部传过来的数据(由父组件提供) 不能随便修改props:{count:Number}
}
</script><style>
.base-count {margin: 20px;
}
</style>
上面的代码,数据由父组件提供,一保存会直接报错(外部数据不能直接改)
需要提供对应的函数
app.vue
<template><div class="app">、<!-- 老爹监听 --><BaseCount@changeCount="handleChange"></BaseCount></div>
</template><script>
import BaseCount from './components/BaseCount.vue'
export default {components:{BaseCount},data(){return {count:666}},methods:{handleChange (newCount) {this.count = newCount}}
}
</script><style></style>~.vue
<template><div class="base-count"><button @click="handleSub">-</button><span>{{ count }}</span><button @click="handleAdd">+</button></div>
</template><script>
export default {// 1.自己的数据随便修改 (谁的数据 谁负责)// 意思就是,打开网页,想加就加,想减就减// data () {// return {// count: 100,// }// },// 2.外部传过来的数据(由父组件提供) 不能随便修改//单项数据流:父组件的prop更新,会单向的向下流动,影响到子组件props:{count:Number},methods:{handleAdd(){//通过子传父 this.$emit(事件名,参数)//告诉老爹改数字,然后在老爹里面需要监听一下this.$emit('changeCount',this.count + 1)},handleSub(){this.$emit('changeCount',this.count - 1)}}
}
</script><style>
.base-count {margin: 20px;
}
</style>
6.非父子通信(拓展)- event bus 事件总线
作用:非父子组件之间,进行简易消息传递。(复杂场景 → Vuex)
1.创建一个都能访问到的事件总线(空 Vue实例)→ utils/EventBus.js
impprt Vue for 'vue'
const Bus = new Vue()
export default Bus
2.A组件(接收方),监听Bus实例的事件监听事件(订阅消息)
created () {Bus.$on('sendMsg',(msg) =>{this.msg = msg})
3.B组件(发送方),触发Bus实例的事件
Bus.$emit('sendMsg','这是一个消息')
非父子通信-provide-inject(拓展)
provide & inject 作用:跨层级共享数据
1.父组件`provide``提供数据
(provide写成一个函数,在里面return,return就是共享的数据,里面可以写多个数据【简单(string)/复杂(对象)类型】)
export default {provide () {return {//普通类型【非响应式】colour:this.color,//复杂类型【响应式】userInfo:this.userInto,}}
}
子/孙组件(无论多少层)
inject
取值使用(直接写你要接收的属性名就可以了,将来也可以通过this
直接访问)
export default {inject: ['color','userInfo'],created () {console,log(this.color,this.userInfo)}
}