Bootstrap

Vue2之组件通信

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和 parentchildren

  • $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>
    

localStorage

sessionStorage

cookie

;