prop和$emit
props
和$emit
父组件向子组件传递数据是通过prop
传递的,子组件传递数据给父组件是通过$emit
触发事件来做到的
//父向子传值
<template 父>
<div>
<aaa :msg="传参"></aaa>
</div>
</template>
import aaa from './aaa.vue'
<template-子>
<div>
{{ msg }}
</div>
</template>
---------------------------------
//子向父组件传值
<template 父>
<div>
<aaa @send="send"></aaa>
</div>
</template>
send(val){
val为接收的参
}
<template 子>
<div>
<div @click="send">改变字符串</div>
</div>
</template>
change(){
this.$emit('send', '我是参数')
}
prop 只可以从上一级组件传递到下一级组件(父子组件),
即所谓的单向数据流。而且 prop 只读,不可被修改,所有修改都会失效并警告。第一,不应该在一个子组件内部改变 prop,这样会破坏单向的数据绑定,导致数据流难以理解。如果有这样的需要,可以通过 data 属性接收或使用 computed 属性进行转换。
第二,如果 props 传递的是引用类型(对象或者数组),在子组件中改变这个对象或数组,父组件的状态会也会做相应的更新,利用这一点就能够实现父子组件数据的“双向绑定”,虽然这样实现能够节省代码,但会牺牲数据流向的简洁性,令人难以理解,最好不要这样去做。
想要实现父子组件的数据“双向绑定”,可以使用 v-model 或 .sync。
$ emit 向父组件传数据,父组件在子组件通过v-on监听函数并接收参数,vue框架就在子组件监听了你v-on="fn"的fn事件函数,在子组件使用$emit就能触发了
v-model
语法糖
<template 父>
<aaa v-model="data"></aaa>
</template>
// <!-- v-model等价于以下写法 -->
// <!-- <aaa :value="data" @input='val=>data=val'> </aaa> -->
<template 子>
{{value}}
<input type="text" @click="send">
</template>
props: {
value:{ // 这里的值的名字一定要叫value 因为v-modle双向绑定的就是value属性
type:Number,
required:true,
}
},
send(){
// $emit触发input事件修改接收的值 父组件中跟着变化
this.$emit('input',this.value+1)
}
v-model 是用来在表单控件或者组件上创建双向绑定的,他的本质是 v-bind 和 v-on 的语法糖,在一个组件上使用 v-model,默认会为组件绑定名为 value 的 prop 和名为 input 的事件。
attrs和listeners
- 子组件使用$attrs可以获得父组件除了props传递的属性和特性绑定属性 (class和 style)之外的所有属性
- 子组件使用$listeners可以获得父组件(不含.native修饰器的)所有v-on事件监听器
//父组件
<template>
<div>
<Child @parentFun="parentFun" :msg1="msg1" :msg2="msg2" />
</div>
</template>
<script>
import Child from './Child'
export default {
components:{
Child
},
data(){
return {
msg1:'子组件msg1',
msg2:'子组件msg2'
}
},
methods: {
parentFun(val) {
console.log(`父组件方法被调用,获得子组件传值:${val}`)
}
}
}
</script>
//子组件
<template>
<div>
<button @click="getParentFun">调用父组件方法</button>
</div>
</template>
<script>
export default {
methods:{
getParentFun(){
this.$listeners.parentFun('我是子组件数据')
}
},
created(){
//获取父组件中所有绑定属性
console.log(this.$attrs) //{"msg1": "子组件msg1","msg2": "子组件msg2"}
//获取父组件中所有绑定方法
console.log(this.$listeners) //{parentFun:f}
}
}
</script>
provide和inject
适用于隔代组件通信
祖先组件中通过 provider 来提供变量,然后在子孙组件中通过 inject 来注入变量。
不论子组件嵌套有多深, 只要调用了inject 那么就可以注入provide中的数据,而不局限于只能从当前父组件的props属性中回去数据
// A.vue
<template>
<div>
<comB></comB>
</div>
</template>
<script>
import comB from './comB.vue'
export default {
name: "A",
provide: {
for: "demo"
},
components:{
comB
}
}
</script>
// B.vue
<template>
<div>
{{demo}}
<comC></comC>
</div>
</template>
<script>
import comC from './comC.vue'
export default {
name: "B",
inject: ['for'],
data() {
return {
demo: this.for
}
},
components: {
comC
}
}
</script>
// C.vue
<template>
<div>
{{demo}}
</div>
</template>
<script>
export default {
name: "C",
inject: ['for'],
data() {
return {
demo: this.for
}
}
}
</script>
p a r e n t 和 parent和 parent和children
- $parent: 子组件获取父组件Vue实例,可以获取父组件的属性方法等
- $children: 父组件获取子组件Vue实例,是一个数组,是直接儿子的集合,但并不保证子组件的顺序
<template 父>
<div>
<div>{{msg}}</div>
<aaa></aaa>
<button @click="click">点击改变子组件值</button>
</div>
</template>
data() {
return {
msg: '父的参'
}
},
click() {
// 获取到子组件A
this.$children[0].messageA = 'this is new value'
}
<template 子>
<div >
<span>{{messageA}}</span>
<p>获取父组件的值为: {{parentVal}}</p>
</div>
</template>
data() {
return {
messageA: 'this is old'
}
},
computed:{
parentVal(){
return this.$parent.msg;
}
}
要注意边界情况,如在#app上拿$ parent得到的是new Vue()的实例,在这实例上再拿$ parent得到的是undefined,而在最底层的子组件拿$ children是个空数组。
也要注意得到parent和parent和parent和children的值不一样,$ children 的值是数组,而$ parent是个对象,props, $ emit 、$parent KaTeX parse error: Expected 'EOF', got '#' at position 76: …的通信。 要注意边界情况,如在#̲app上拿 parent得到的是new Vue()的实例,在这实例上再拿$ parent得到的是undefined,而在最底层的子组件拿$ children是个空数组。也要注意得到parent和parent和parent和children的值不一样,$ children 的值是数组,而$parent是个对象
props $emit 、 $parent $children两种方式用于父子组件之间的通信, 而使用props进行父子组件通信更加普遍,二者皆不能用于非父子组件之间的通信。
EventBus/mitt
适用于隔代组件通信。eventBus 又称为事件总线,在vue中可以使用它来作为沟通桥梁的概念, 就像是所有组件共用相同的事件中心,可以向该中心注册发送事件或接收事件, 所以组件都可以通知其他组件。会存在”一呼百应“的问题
bus.js
/* eslint-disable no-unused-vars */
class Bus {
constructor() {
this.callbacks = {}
}
on(name, fn) {
this.callbacks[name] = this.callbacks[name] || []
this.callbacks[name].push(fn)
}
emit(name,args){
if(this.callbacks[name]){
this.callbacks[name].forEach(callback => {
callback(args)
});
}
}
}
module.exports = Bus
// 然后挂载到Vue.prototype.$bus上
-
子组件1发送数据代码
<button @click="send">发送</button> <script> export default { name: "Child", methods: { send() { this.$bus.emit("send", "我是子组件1"); }, }, }; </script>
-
子组件2接收数据代码
export default { name: "Child2", mounted() { this.$bus.on("send", (value) => { console.log("value :>> ", value); }); }, };
$refs
-
子组件
<template> <div ref="c1" class="hello"> {{ msg }} </div> </template> <script> export default { name: "Child", data() { return { msg: "I'm child.", }; }, mounted() { console.log("child msg :>> ", this.msg); }, methods: { childConsole(value) { console.log("parent value is :>> ", value); }, }, }; </script>
-
父组件
<Child ref="child1" /> mounted() { // ** 注意组件挂载顺序 // 获取子组件data数据 console.log("this.$refs.child1.msg :>> ", this.$refs.child1.msg); // 修改子组件data数据 this.$refs.child1.msg = "parent modify data"; // 调用子组件函数 this.$refs.child1.childConsole("hello"); }
Vuex
-
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。对一个中大型单页应用来说是不二之选。
-
state:用于数据的存储,是store中的唯一数据源
-
getters:如vue中的计算属性一样,基于state数据的二次包装,常用于数据的筛选和多个数据的相关性计算
-
mutations:类似函数,改变state数据的唯一途径,且不能用于处理异步事件
-
actions:类似于mutation,用于提交mutation来改变状态,而不直接变更状态,可以包含任意异步操作
-
modules:类似于命名空间,用于项目中将各个模块的状态分开定义和操作,便于维护
Pinia
- 本质是和vuex是一样的,不过做了优化
- https://pinia.vuejs.org/zh/
- https://pinia.web3doc.top/
slot
-
父组件
<template> <div> 父组件 <hr> <Child> <template slot-scope="num"> <span>父组件中使用:{{ num.data }}</span> </template> </Child> </div> </template> <script> import Child from './child.vue' export default { name: 'slotVal', components: { Child }, } </script>
-
子组件
<template> <div> <slot :data="num"></slot> <el-button @click="add">子组件中点击增加</el-button> </div> </template> <script> export default{ name:'Child', data(){ return{ num:0 } }, methods:{ add(){ this.num ++ } } } </script>