父子组件通信
1、父子组件通过prop传递数据
父组件可以将一条数据传递给子组件,这条数据可以是动态的,父组件的数据更改的时候,子组件接收的也会变化。
子组件被动的接收父组件的数据,子组件不要再更改这条数据了。
组件实例的作用域是孤立的,父组件不能直接使用子组件的数据,子组件也不能直接使用父组件的数据。
父组件在调用子组件的时候给子组件传递数据:
<template id="father">
<div class="father">
<p>我是父组件,这是我的fMsg:{{fMsg}}</p>
<input type = "text" v-model = "fMsg">
<hr>
<son msg = "你好"></son>
</div>
</template>
父组件给子组件传递数据的时候,子组件需要利用props的属性来确定自己的预期数据,如果儿子没有通过props属性接受传递过来的数据,则数据会以自定义属性的方式,放在儿子最外层的根元素上面。
子组件通过props来接受父组件传递过来的数据,并且通过{{msg}}使用
components:{
son:{
template:"<div>我是son子组件!这是父组件传递给我的msg:{{msg}}</div>",
//接收父组件传递来的属性 msg
props:["msg"]
}
}
2、父组件通过v-bind指令传递自身变量给子组件
我们可以用 v-bind 来动态地将 prop 绑定到父组件的数据。每当父组件的数据变化时,该变化也会传导给子组件。
<template id="father">
<div class="father">
<p>我是父组件,这是我的fMsg:{{fMsg}}</p>
<input type = "text" v-model = "fMsg">
<hr>
<!-- <son msg = "你好"></son> -->
<son :msg = "fMsg"></son>
</div>
</template>
如果如果父组件传递属性给子组件的时候键名有'-'
<son :f-msg = "fMsg"></son>
子组件接收、使用的时候写成小驼峰的模式
components:{
son:{
template:"<div>我是son子组件!这是父组件传递给我的msg:{{fMsg}}</div>",
//接收父组件传递来的属性 msg
props:["fMsg"]
}
}
3、prop验证传递过来的数据
我们可以为组件的 prop 指定验证规则。如果传入的数据不符合要求,Vue 会发出警告。这对于开发给他人使用的组件非常有用
验证主要分为:类型验证、必传验证、默认值设置、自定义验证
类型验证
// 类型验证
// num:Number //父组件传递过来的num必须是Number类型
// 添加多个类型
num: [Number,String],
规定传递过来的数值类型必须是number,否则系统会报错
必传验证
规定父组件必须给子组件传递该值
//必传验证
// num:{
// required: true
// }
默认值设置
当父组件不给子组件传递该值的时候,给子组件设定一个默认值
// 默认值设置
// num:{
// default:100
// }
自定义验证
//自定义验证
num:{
validator(val){
return val > 100
}
}
规定传递过来的数值必须要大于100,否则系统会报错
4、父子组件依靠应用类型的地址传递共享数据
单向数据流
Prop 是单向绑定的:当父组件的属性变化时,将传递给子组件,但是反过来不会。这是为了防止子组件无意间修改了父组件的状态,来避免应用的数据流变得难以理解。
<template id="father">
<div class="father">
<input type = "text" v-model = "message">
<hr>
<son :message = "message"></son>
</div>
</template>
<template id = "son">
<div>
<p>这是子组件</p>
<input type = "text" v-model = "message"></input>
</div>
</template>
另外,每次父组件更新时,子组件的所有 prop 都会更新为最新值。这意味着你不应该在子组件内部改变 prop。如果你这么做了,Vue 会在控制台给出警告。
所以如果我们想实现父子间的数据共享,依靠的就是应用类型的地址传递,应将message写成对象的形式,传递的时候将对象传递给子组件,子组件引用的时候使用对象的value值。
<template id="father">
<div class="father">
<input type = "text" v-model = "message.value">
<hr>
<!-- 传递的时候将对象传递给子组件 -->
<son :message = "message"></son>
</div>
</template>
<template id = "son">
<div>
<p>这是子组件</p>
<!-- 引用的时候使用对象的value值 -->
<input type = "text" v-model = "message.value"></input>
</div>
</template>
这时候更改父组件的value值,子组件的数据同步更改,子组件修改value值的时候也同步修改了父组件的数据。这是因为不管是子组件还是父组件,我们操作的都是同一个对象,父组件直接把引用类型的地址传递给子组件,子组件没有直接修改对象,只是更改了里面的属性值。
父组件如果将一个引用类型的动态数据传递给子组件的时候,数据会变成双向控制的,子组件改数据的时候父组件也能接收到数据变化,因为子组件改的时候不是在改数据(地址),而是在改数据里的内容,也就是说引用类型数据的地址始终没有变化,不算改父组件数据。
注意:在 JavaScript 中对象和数组是引用类型,指向同一个内存空间,如果 prop 是一个对象或数组,在子组件内部改变它会影响父组件的状态。 message:{val:""}
$父子间数据共享(双向控制),基本不会使用,违背了单向数据流(父=》子)$
5、viewmodel关系链
在组件间可以用过ref形成ref链,组件还拥有一个关系链($parent),通过这两种链;理论来说,任意的两个组件都可以互相访问,互相进行通信。
$parent:父组件
$children:子组件
$root:根组件
当子组件在set方法中修改父组件传递过来的值时,系统会报错,因为子组件不能修改父组件的数据。
Vue.component("bbb",{
template:"#bbb",
props:["msg"],
computed:{
/* ownMessage(){
return this.msg;
} */
ownMessage:{
get(){
return this.msg;
},
set(val){
this.msg = val //系统报错:子组件不能更改父组件传递的数据
}
}
}
})
所以这时候要使用$parent,让父组件自己更改自己的数据
set(val){
// this.msg = val //系统报错:子组件不能更改父组件传递的数据
// console.log(this)
// 相当于父组件自己更改了msg数据
this.$parent.msg = val;
}
6、父组件通过ref标记获取子组件的数据
父组件在调用子组件的时候使用ref做标记
<template id="aaa">
<div>
<button @click = "get">点击获取bbb数据</button>
<!-- 组件间不仅可以用过$root/$parent/$children来获取对应关系的组件,父组件还可以主动的通过ref为子组件做标记 -->
<bbb ref = "b"></bbb>
</div>
</template>
父组件的this属性上有$refs标记,通过refs标记拿到子组件
// 通过ref标记更改子组件的数据
// this.$refs.b.message = "哈哈"
组件间不仅可以用过$parent/children/root来获取对应关系的组件,父组件还可以主动的通过ref为子组件做标记 也可以给dom做标记,也会形成ref链,也可以交互.
<button ref="btn" @click="get">get</button>
<bbb ref="b></bbb>
注意多个子组件标记的是同一个键名,获取到的应该是一个数组
<bbb ref = "b" v-for = "(item,index) in 3" :key = "index"></bbb>
// 通过下标修改对应的数值
this.$refs.b[0].message = "哈哈"
运行效果:
子父组件通信
1、子组件通过父组件传递的方法来更改父组件的数据
父组件可以将更改自身数据的方法传递给子组件,子组件调用这个方法的时候,就可以给父组件传递数据,父组件被动的接收子组件的数据。
子组件声明一条自身的msg
Vue.component("son",{
template:"#son",
// 子组件接收父组件传递过来的方法
props:["change"],
data(){
return{
msg:"我是子组件"
}
}
})
父组件先声明一条自己的数据
data(){
return{
// 父组件先声明一条自己的数据
parentMsg:""
}
}
再写一个可以更改自身数据的方法
methods:{
// 写一个可以更改自身数据的方法
change(msg){
this.parentMsg = msg
}
}
将写好的change方法传递给子组件
<template id="father">
<div>
<p>这是父组件</p>
<p>子组件传递过来的值是:{{parentMsg}}</p>
<hr>
<!-- 调用子组件的时候,将更改自身数据的方法传递给子组件 -->
<son :change = "change"></son>
</div>
</template>
子组件通过props接收父组件传递过来的change方法
props:["change"]
给p标签添加点击事件,点击即触发change方法,同时将自身的msg传递给父组件,相当于父组件的change方法被执行。
<template id="son">
<div>
<p>子组件说:{{msg}}</p>
<p @click = "change(msg)">点击我触发父亲的change方法</p>
</div>
</template>
父组件可以在页面中渲染子组件传递过来的数据
<p>子组件传递过来的值是:{{parentMsg}}</p>
运行效果:
2、通过自定义事件实现子父通信
每一个组件或者实例都会有自定义事件,和触发事件的能力,父组件给子组件绑定一个自定义事件,这个事件的处理程序却是父组件的一个方法,当子组件触发这个事件的时候,相当于父组件的方法被执行。
父组件想获取子组件的数据时,在调用子组件的时候给子组件绑定一个自定义事件change-event
<template id="father">
<div>
<p>这是父组件</p>
<p>子组件传递过来的值是:{{parentMsg}}</p>
<hr>
<!-- 给子组件绑定一个自定义事件 -->
<son @change-event = "change"></son>
</div>
</template>
在子组件中定义一个点击事件,点击p标签执行changeWord方法
<p @click = "changeWord">点击我触发父亲的change方法</p>
在方法中编写changeWord方法,通过this.$emit来触发绑定在自己身上的自定义事件,第一个参数为事件名称change-event,第二个参数为触发这个函数的时候给他传递的数值:自身的msg。
methods:{
changeWord(){
//触发自身绑定的change事件
this.$emit("change-event",this.msg)//第一个参数为触发事件的名字,第二个参数为触发这个函数的时候给他传递的数值
}
}
一旦触发绑定在自身上的自定义事件,相当于父组件的change方法被执行。
兄弟组件通信
1、通过viewmodel关系链
定义哥哥组件,给哥哥组件添加一个点击事件,点击触发hitLittle方法
<template id = "big-brother">
<div>
<p>我是哥哥</p>
<button @click = "hitLittle">打弟弟</button>
</div>
</template>
定义弟弟组件,给弟弟组件添加一个p标签,由crying数据控制其显示与隐藏
<template id="little-brother">
<div>
<p>我是弟弟</p>
<p v-if = "crying">呜呜呜</p>
</div>
</template>
在弟弟组件的data中声明crying数据,默认为false
Vue.component("little-brother",{
template:"#little-brother",
data(){
return{
crying:false
}
}
})
在哥哥组件的methods中定义hitLittle方法,通过viewmodel关系链更改弟弟组件中的crying方法
Vue.component("big-brother",{
template:"#big-brother",
methods:{
hitLittle(){
//在兄弟组件之间的通信,可以采用关系链和ref链去使用,解决兄弟之间通信问题。
this.$parent.$children[1].crying = true;//让littel改变自身的crying状态
}
}
})
运行效果:
2、viewmodel关系链+ref链
在弟弟组件中添加ref标记
<little-brother ref = "little"></little-brother>
在哥哥组件的hitLittle方法中通过viewmodel和ref链配合使用更改弟弟组件中的crying数据
hitLittle(){
//在兄弟组件之间的通信,可以采用关系链和ref链去使用,解决兄弟之间通信问题。
// this.$parent.$children[1].crying = true;//让littel改变自身的crying状态
//viewmodel链和ref链配合使用
this.$parent.$refs.little.crying = true;
}
3、eventbus事件总线
创建一个空的实例
var angle = new Vue();
弟弟组件自己定义一个更改自身状态的方法
methods:{
cry(){
this.crying = true
}
}
在mounted生命周期函数中绑定一个自定义事件,第一个参数为自定义事件名,第二个函数为需要处理的函数
mounted(){
// 绑定一个自定义事件,第一个参数为自定义事件名,第二个函数为需要处理的函数
angle.$on("hit-little",this.cry)
}
在哥哥组件中触发自定义事件
hitLittle(){
//触发little-brother组件的hit-little事件
angle.$emit("hit-little")
}
4、vuex状态管理
vuex是vue提供的一个全局的状态管理工具,主要处理项目中多组件间状态共享。
Vuex是vue官方的一款状态管理工具,什么是状态呢?我们在前端开发中有一个概念:数据驱动,页面中任意的显示不同,都应该有一条数据来控制,而这条数据又叫做state,状态。
在vue中。组件间进行数据传递、通信很频繁,而父子组件和非父子组件的通信功能也比较完善,但是,唯一困难的就是多组件间的数据共享,这个问题由vuex来处理
(1)创建store
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
(2)设置state
state就是一个纯对象,上面有一些状态挂载
state: {
num:0,
name:"list"
}
(3)在根实例里配置store
这样,我们就可以在任意的组件中通过this.$store来使用关于store的api
import store from './store/index'
Vue.config.productionTip = false
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
(4)在Home组件中使用state
computed:{
...mapState({
num:state=>state.num
})
},
(5)使用mutations更改state
mutations也是一个纯对象,里面包含很多更改state 的方法,这些方法的形参接收到state,在函数体里更改,这时,组件用到的数据也会更改,实现响应式。
mutations: {
changeNum(state){
state.num++
}
}
(6)在组件中调用mutations方法,更改组件中的state。
//使用vuex提供的mapMutations帮助我们在组件中调用mutations方法
...mapMutations(["changeNum"]),
//给按钮添加点击事件
<button @click = "changeNum">点击更改num值</button>
运行效果:
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。