美文网首页
Vue学习笔记[09]-组件化进阶

Vue学习笔记[09]-组件化进阶

作者: 神楽花菜 | 来源:发表于2019-11-05 21:18 被阅读0次

    tag:父子组件通信,data为什么为函数事件总线

    组件的可选属性

    组件不能直接访问顶层Vue实例中的数据:

    <script>//✖!
      Vue.component('cpn', {
        template: "<div>{{number}}</div>"
      })
      const vm = new Vue({
        el: "#app",
        data: {
          message:"some text"
    },
      })
    

    组件中也可以有data属性和methods等属性:

     Vue.component('cpn', {
        template: "#cpn-t",
        data() {
          return {
            message: "some text"
          }
        },
      })
      const vm = new Vue({
        el: "#app",
      })
    

    可以发现,子组件中的data是一个函数并且返回一个对象,并且如果不写成函数的形式而是写成对象的形式时会报错(“data”属性应该是在组件定义中返回一个对象实例的函数。)

    错误信息
    在每一次使用组件时,vue每次都会对调用data函数并且返回一个对象

    解析:
    函数返回内部对象时相当于会创建一个新的实例对象并返回该对象(的地址),而在返回外部对象时并不会创建新的对象而是返回外部对象的地址,这就会造成组件中数据污染到外部对象或是其他组件的数据

      function f1() {
    
        return{
          name: 'Sam',
          age: 20
        };
      }
    let a = f1();//创建新的对象并返回
    let b = f1();//创建新的对象并返回
    let c = f1();//创建新的对象并返回
    //-----------------------------------------
    const person2 = {
      name: "Alex",
      age: 21
    }
    function f2(){
      return person2;//这里return的是外部对象的引用(地址)
    }
    let d = f2();//外部对象的引用
    let e = f2();//外部对象的引用
    let f = f2();//外部对象的引用
    //d,e,f指向同一个对象,修改任意一个其他的对象属性也会变.(当对象为const时属性仍然可修改)
    

    data为函数:

    Vue.component('button-counter2', {
      data: function () {
        return {
          count: 0;
          }
      },
      template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'
    })
    new Vue({ el: '#components-demo3' })
    
    三个组件拥有独立的data
    var buttonCounter2Data = {
      count: 0
    }
    Vue.component('button-counter2', {
      data: function () {
        return buttonCounter2Data
      },
      template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'
    })
    new Vue({ el: '#components-demo3' })
    
    三个组件共享data

    组件通信

    由于Vue父子组件中的数据是互相不可见的,因此有时需要传递数据时就需要组件通信.

    • 通过props向子组件传递数据

    <div id="app">
       <cpn></cpn> 
     </div>
    <template id="cpn-t">
      <div>
        <h4>title</h4>
        <cpn-children :message="message"></cpn-children>
      </div>
    </template>
    

    在父组件中 使用子组件时,需要绑定prop数组中的值到父组件的数据.由于html标签中不支持大小写,所以props中的变量名应为小写,若使用驼峰标识,在html标签内要转化为短横线命名.

    <script>
    
      Vue.component('cpn', {
        template: "#cpn-t",
        data() {
          return {
            message: "some text"
          }
        },
        components: {
          cpnChildren: {
            template: '<div>{{message}}</div>',
            //数组形式:props: ['message',/*...*/]  ,在Vue风格指南中作为反例
            //对象形式:
            props: {
              message: String,
              name: {
                type: String, //类型
                default: "Smith", //默认值  
                required:false
              }
            }
          }
        }
      })
    

    如果props中的数据是对象类型或数组类型,在默认值处应用工厂方法返回一个数组或对象:

    props:{
      fruit:{
          type:Array,
          default:function(){
              return [];
          }
       }
    }
    
    • 通过自定义事件向父组件发送消息

    <body>
      <div id="app">
        <cpn></cpn>
      </div>
    </body>
    
    <template id="cpn-t">
      <div>
        <cpn-children @emit-btn-click="consoleLog"></cpn-children> //监听子组件发射自定义事件并处理事件!该方法不需要参数,默认传入.
      </div>
    </template>
    
    <script>
      Vue.component('cpn', { //定义父组件
        template: "#cpn-t",
        methods: {
          consoleLog(item) { //默认传入自定义事件中的传递的数据,由父组件处理发射事件
            console.log("Clicked", item.name);
          }
        },
    
        components: { //定义子组件
          cpnChildren: {
            template: `<div>
            <button v-for="item in fruit" @click="btnclicked(item)">{{item.name}}</button>
            </div>
            `,
            data() {
              return {
                fruit: [{
                    id: 200,
                    name: "apple",
                    price: 2
                  },
    
                  {
                    id: 201,
                    name: "banana",
                    price: 2.5
                  },
    
                  {
                    id: 202,
                    name: "orange",
                    price: 0.5
                  }
                ]
              }
            },
            methods: {
              btnclicked(item) {
                this.$emit("emit-btn-click",item) // 子组件向父组件发射自定义事件
              },
            }
    
          }
        }
      })
      const vm = new Vue({
        el: "#app",
        methods: {
    
        }
      })
    </script>
    
    

    Vue<ROOT>
    |-cpn =>监听发射事件,,接收数据,处理发射,
     |-cpnChildren =>发射事件,数据

    补充:通过事件总线发送消息

    当某些组件层次过深或传递复杂时,一方面我们可以用Vuex来进行管理事件,另一方法是在vue原型上添加$bus属性,通过另一个vue实例来对事件进行发送和监听。

    //main.js
    import Vue from 'vue'
    import App from './App.vue'
    //vue原型添加事件总线
    Vue.prototype.$bus = new Vue()
    
    Vue.config.productionTip = false
    
    new Vue({
      router,
      store,
      render: h => h(App)
    }).$mount('#app')
    
    
    //CpnSendMsg.vue
    //发送消息(事件)的组件
    <template>
      <div>
        <button @click="sendMsg">
          send
        </button>
      </div>
    </template>
    
    <script>
    export default {pn
      name:"Cpn",
      methods: {
        sendMsg(){
            //利用新建的vue实例来发送自定义事件
          this.$bus.$emit("cpnSendMsg")
        }
      }
    }
    </script>
    
    //CpnReseveMsg.vue
    //接受自定义事件的组件
    <template>
      <div>
    。。。
      </div>
    </template>
    
    <script>
    export default {
      name:"CpnReseveMsg",
      created(){
        this.$bus.$on("cpnSendMsg",()=>{
          //事件处理
        })
      }
    }
    </script>
    

    补充:使用vue原型包装全局组件

    使用场景:封装Toast(弹窗)组件,由于该组件需要在不同场景下使用,,十分频繁,因此将该组件挂载到vue原型上。

    //toast/Toast.vue
    <template>
      <div class="toast" v-show="isShow">
        {{message}}
      </div>
    </template>
    
    <script>
    export default {
      name: 'Toast',
     data() {
       return {
         message:'',
         isShow:false
       }
     },
      methods: {
        show(message,time){
          this.message = message
          this.isShow = true
          setTimeout(() => {
            this.isShow = false
            this.message = ''
          }, time);
        }
      },
    
    };
    </script>
    
    <style lang='scss' scoped>
    .toast{
      position: fixed;
      top:50%;
      left: 50%;
      transform: translate(-50%,-50%);
      z-index: 1000;
      padding: 20px 40px;
      color: rgb(110, 110, 110);
      background-color: rgba(236, 236, 236, 0.85);
      border-radius: 15px;
    }
    </style>
    
    //toast/index.js
    import Toast from "./Toast"
    const obj = {
    }
    obj.install = function (Vue) {
      //1.创建组件构造器
      const constructor = Vue.extend(Toast)
      //2.使用new方法创建组件
      const toast = new constructor()
      //3.手动挂载组件对象
      toast.$mount(document.createElement('div'))
      //4.已经挂载的对象具有了el属性
      document.body.appendChild(toast.$el)
      Vue.prototype.$toast = toast
    /********************or***************************/
     //1.创建组件
      const toast = new Vue(Toast)
      //2.手动挂载组件对象
      toast.$mount(document.createElement('div'))
      //3.已经挂载的对象具有了el属性
      document.body.appendChild(toast.$el)
      Vue.prototype.$toast = toast
    }
    export default obj
    
    //main.js
    import Vue from 'vue'
    import App from './App.vue'
    import router from './router'
    import store from './store'
    //导入toast
    import toast from "components/common/toast/index"
    //vue原型添加事件总线
    Vue.prototype.$bus = new Vue()
    Vue.config.productionTip = false
    //安装toast
    Vue.use(toast)
    new Vue({
      router,
      store,
      render: h => h(App)
    }).$mount('#app')
    
    
    //use
    //...
    methods:{
      someEventAcitve(){
      this.$toast.show("MESSAGE",2000)
      }
    }
    

    相关文章

      网友评论

          本文标题:Vue学习笔记[09]-组件化进阶

          本文链接:https://www.haomeiwen.com/subject/buflbctx.html