【Vue 组件化开发 三】父组件给子组件传递数据、组件通信(父传子、子传父)、父访问子(children、ref)、动态组件(is、component)

简介: 本章学习父组件给子组件传递数据、组件通信(父传子、子传父)、父访问子(children、ref)、动态组件(is、component)。

一、父组件给子组件传递数据


1.使用props属性,父组件向子组件传递数据

const cpn = {
  template: "#cpn",
  props: { 
          cmessage: {
          type: String,
          default: 'zzzzz',
          required: true //在使用组件必传值
          }
  }
}


2.向cmessage对象传值

<div id="app">
    <cpn :cMessage="message"></cpn>
</div>
<script>    
const app = new Vue({
      el: "#app",
      data: {
        message: "你好",
        movies: ["复仇者联盟", "钢铁侠", "星际穿越", "哪吒传奇"]
      },
      components: {
        cpn
      }
    })
  </script>


2. props属性使用

1.数组写法

props: ['cmovies', 'cmessage']

2.对象写法

props: { 
          cmessage: {
          type: String,
          default: 'zzzzz',
          required: true //在使用组件必传值
          }
  }


3.props属性的类型限制

//1.类型限制(多个类使用数组)
cmovies:Array,//限制为数组类型
cmessage:String,//限制为Strin类型
cmessage:['String','Number']//限制为String或Number类型


4.props属性的默认值

// 2.提供一些默认值,以及必传值
        cmessage: {
          type: String,
          default: 'zzzzz',//默认值
       }

5.props属性的必传值

cmessage: {
          type: String,
          default: 'zzzzz',
          required: true //在使用组件必传值
        }


6.类型是Object/Array,默认值必须是一个函数

//类型是Object/Array,默认值必须是一个函数
cmovies: {
  type: Array,
  default () {
  return [1, 2, 3, 4]
  }
  },


7.自定义验证函数

vaildator: function (value) {
  //这个传递的值必须匹配下列字符串中的一个
  return ['zzzzz', 'ttttt', 'yyy'].indexOf(value) !== -1
}


8.自定义类型

function Person(firstName,lastName) {
      this.firstName = firstName
      this.lastName = lastName
    }
  cmessage:Person//限定了cmeessage必须是Person类型


二、组件通信

1 父传子

<!DOCTYPE html>
<html>
  <head>
  <meta charset="utf-8">
  <title></title>
  </head>
  <body>
  <div id="app">
    <!-- <cpn1 :msg="message"></cpn1> -->
    <!-- <cpn1 :msg="message2"></cpn1> -->
    <cpn1 :msgab="add"></cpn1>
    <h2>{{count}}</h2>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
  <script>
    const app = new Vue({
    el: "#app",
    data: {
      /* message: ['蔡英文', '吴钊燮'] */
      /* message2:{
      name:'蔡英文',
      age:56,
      sex:'女'
      } */
      count:0
    },
    methods: {
                    add:function(){
      return this.count++
      }
    },
    computed: {
    },
    components: {
      cpn1: {
      /* template: `
        <div>我是中国人{{msg.name}}{{msg.sex}}</div>
      `, */
      template: `
      <div>
        <div @click="sum">+</div>
      </div>
      `,
      props: {
        /* msg:{
        type: Array
        } */
        /* msg:{
        type: Object
        } */
        msgab:{
        type:Function
        },
      },
      methods:{
        sum(){
        this.msgab()
        }
      }
      }
    }
    })
  </script>
  </body>
</html>


2.子传父

子组件向父组件传值,使用自定义事件$emit。

<!-- 父组件 -->
  <div id="app">
    <!-- 不写参数默认传递btnClick的item -->
    <cpn @itemclick="cpnClcik"></cpn>
  </div>
  <!-- 子组件 -->
  <template id="cpn">
    <div>
      <button v-for="(item, index) in categoties" :key="index" @click="btnClick(item)">{{item.name}}</button>
    </div>
  </template>
  <script src="../js/vue.js"></script>
  <script>
    const cpn = {
      template: "#cpn",
      data() {
        return {
          categoties: [{
              id: 'aaa',
              name: '热门推荐'
            },
            {
              id: 'bbb',
              name: '手机数码'
            },
            {
              id: 'ccc',
              name: '家用家电'
            },
            {
              id: 'ddd',
              name: '电脑办公'
            },
          ]
        }
      },
      methods: {
        btnClick(item) {
          this.$emit('itemclick', item)
        }
      },
    };
    const app = new Vue({
      el: "#app",
      data() {
        return {
        }
      },
      methods: {
        cpnClcik(item) {
          console.log('cpnClick'+item.name);
        }
      },
      components: {
        cpn
      },
    })
  </script>


1.在子组件中定义一个方法btnClick(item),使用$emit,'itemclick'是事件名,item是传过去的值。

methods: {
        btnClick(item) {
          this.$emit('itemclick', item)
        }
      },


2.在子组件中监听点击事件并回调此方法

<div>
      <button v-for="(item, index) in categoties" :key="index" @click="btnClick(item)">{{item.name}}</button>
</div>


3.在父组件中定义一个方法cpnClcik(item)

methods: {
  cpnClcik(item) {
  console.log('cpnClick'+item.name);
  }
},


4.并在父组件(vue实例)中调用(不写参数默认传递btnClick的item ),父组件监听事件名为itemclick的子组件传过来的事件。



三、父访问子(children、ref)


1.children、ref基本用法

父组件访问子组件,有时候需要直接操作子组件的方法,或是属性,此时需要用到$children和$ref。


使用this.$children直接获取**当前实例的直接子组件,需要注意 $children 并不保证顺序,也不是响应式的。**如果你发现自己正在尝试使用 $children 来进行数据绑定,考虑使用一个数组配合 v-for 来生成子组件,并且使用 Array 作为真正的来源。

<!-- 父组件 -->
  <div id="app">
    <cpn></cpn>
    <cpn></cpn>
    <cpn ref="aaa"></cpn>
    <button @click="btnClick" >按钮</button>
  </div>
  <!-- 子组件 -->
  <template id="cpn">
    <div>
      我是子组件
    </div>
  </template>
  <script src="../js/vue.js"></script>
  <script>
    // 父传子:props
    const cpn = {
      template: "#cpn",
      data() {
        return {
          name:"我是子组件的name"
        }
      },
      methods: {
        showMessage(){
          console.log("showMessage");
        }
      },
    };
    const app = new Vue({
      el: "#app",
      data() {
        return {
          message:"hello"
        }
      },
      methods: {
        btnClick(){
          // 1.children
          // console.log(this.$children[0].showMessage)
          // for (let cpn of this.$children) {
          //   console.log(cpn.showMessage)
          // }
          // 2.$ref
          console.log(this.$refs.aaa.name)
        }
      },
      components: {
        cpn
      },
    })
  </script>
$children方式
// 1.children
console.log(this.$children[0].showMessage)
for (let cpn of this.$children) {
    console.log(cpn.showMessage)
}


$refs方式:ref 被用来给元素或子组件注册引用信息。引用信息将会注册在父组件的 $refs 对象上。如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例


2.ref的基本使用 用在元素上

ref的基本使用 用在元素上

<!DOCTYPE html>
<html>
  <head>
  <meta charset="utf-8">
  <title></title>
  </head>
  <body>
  <div id="app">
          <p ref="p" @click="handelClick" id="ppp">hello</p>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
  <script>
    const app = new Vue({
    el: "#app",
    data: {
    },
    methods: {
                 handelClick(){
      console.log(this.$refs.p);
      const ppp = document.querySelector('#ppp')
      console.log(ppp);
     }
    },
    computed:{
    }
    })
  </script>
  </body>
</html>


3.ref在子组件上的使用

1. ref可以调用组件中的数据

<!DOCTYPE html>
<html>
  <head>
  <meta charset="utf-8">
  <title></title>
  </head>
  <body>
  <div id="app">
           <counter ref="one" @change="handelChange"></counter>
     <counter ref="two" @change="handelChange"></counter>
     <div>total:{{total}}</div>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
  <script>
    Vue.component('counter',{
    template:'<div @click="handelclick">{{number}}</div>',
    data(){
      return {
      number:0
      }
    },
    methods:{
      handelclick(){
      this.number++;
      this.$emit('change');
      }
    }
    })
    const app = new Vue({
    el: "#app",
    data: {
      total:0
    },
    methods: {
                 handelChange(){
      this.total = this.$refs.one.number + this.$refs.two.number
     }
    },
    computed:{
    }
    })
  </script>
  </body>
</html>


2.ref可以调用组件中的方法

<!DOCTYPE html>
<html>
  <head>
  <meta charset="utf-8">
  <title></title>
  </head>
  <body>
  <div id="app">
              <helloworld ref="hello"></helloworld>
              <button @click="getHello">获取helloworld组件中的值</button>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
  <script>
    Vue.component('helloworld',{
    template:'<div></div>',
    data(){
      return {
      number:0
      }
    },
    methods:{
      handelclick(){
      console.log('被调用了');
      }
    }
    })
    const app = new Vue({
    el: "#app",
    data: {
    },
    methods: {
                  getHello(){
        this.$refs.hello.handelclick();
                      console.log(this.$refs.hello.number);
        console.log(this.$refs.hello.$el.innerHTML);
      }
    },
    computed:{
    }
    })
  </script>
  </body>
</html>


四、动态组件(is、component)


1.is用于动态组件且基于 DOM 内模板的限制来工作。

基于 DOM 内模板的限制来工作

<!DOCTYPE html>
<html>
  <head>
  <meta charset="utf-8">
  <title></title>
  </head>
  <body>
  <div id="app">
          <table>
     <tr is="row">
     </tr> 
    </table>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
  <script>
    Vue.component('row',{
    template:'<tr><td>111</td></tr>'
    })
    const app = new Vue({
    el: "#app",
    data() {
      return {}
    },
    methods: {
    },
    computed:{
    }
    })
  </script>
  </body>
</html>


2.动态组件component

<!DOCTYPE html>
<html>
  <head>
  <meta charset="utf-8">
  <title></title>
  </head>
  <body>
  <div id="app">
    <!-- <child-one></child-one>
    <child-two></child-two> -->
    <component :is="type"></component>
    <button @click="handerClick">点击</button>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
   <script>
    Vue.component('child-one',{
     template:'<div>child-one</div>'
    })
    Vue.component('child-two',{
      template:'<div>child-two</div>'
    })
    const app = new Vue({
    el:'#app',
    data(){
      return {
      type:'child-one'
      }
    },
    methods:{
      handerClick(){
      console.log('111');
      this.type=this.type==='child-one'?'child-two':'child-one';
      }
    }
    }) 
   </script>
  </body>
</html>


3.动态组件官网案例

这是动态组件官网案例

<!DOCTYPE html>
<html>
  <head>
  <meta charset="utf-8">
  <title></title>
  <style>
  .tab-button {
          padding: 6px 10px;
          border-top-left-radius: 3px;
          border-top-right-radius: 3px;
          border: 1px solid #ccc;
          cursor: pointer;
          background: #f0f0f0;
          margin-bottom: -1px;
          margin-right: -1px;
        }
        .tab-button:hover {
          background: #e0e0e0;
        }
        .tab-button.active {
          background: #e0e0e0;
        }
        .tab {
          border: 1px solid #ccc;
          padding: 10px;
        }
  </style>
  </head>
  <body>
  <div id="app">
    <button v-for="(tab,index) in tabs":key="index" @click="handelclick(tab)" :class="getStyle(tab)">{{tab}}</button>
    <component :is="currentTabComponent"></component>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
   <script>
    Vue.component('tab-home',{
    template:'<div>child-one</div>'
    })
    Vue.component('tab-posts',{
    template:'<div>child-two</div>'
    })
    Vue.component('tab-archive',{
    template:'<div>child-three</div>'
    })
    const app = new Vue({
    el:'#app',
    data(){
      return {
       currentTab: "Home",
       tabs: ["Home", "Posts", "Archive"]
      }
    },
    methods:{
      handelclick(tab){
      this.currentTab = tab
      },
      getStyle(tab){
      return ['tab-button',{active:this.currentTab===tab}]
      }
    },
    computed:{
      currentTabComponent(){
      /* return `tab-${this.currentTab}`.toLowerCase() */
      return "tab-"+this.currentTab.toLowerCase()
      },
    }
    }) 
   </script>
  </body>
</html>


相关实践学习
Serverless极速搭建Hexo博客
本场景介绍如何使用阿里云函数计算服务命令行工具快速搭建一个Hexo博客。
相关文章
|
16天前
|
JavaScript 数据管理 Java
在 Vue 3 中使用 Proxy 实现数据双向绑定的性能如何?
【10月更文挑战第23天】Vue 3中使用Proxy实现数据双向绑定在多个方面都带来了性能的提升,从更高效的响应式追踪、更好的初始化性能、对数组操作的优化到更优的内存管理等,使得Vue 3在处理复杂的应用场景和大量数据时能够更加高效和稳定地运行。
36 1
|
16天前
|
JavaScript 开发者
在 Vue 3 中使用 Proxy 实现数据的双向绑定
【10月更文挑战第23天】Vue 3利用 `Proxy` 实现了数据的双向绑定,无论是使用内置的指令如 `v-model`,还是通过自定义事件或自定义指令,都能够方便地实现数据与视图之间的双向交互,满足不同场景下的开发需求。
37 1
|
22天前
|
API
vue3知识点:响应式数据的判断
vue3知识点:响应式数据的判断
27 3
|
24天前
|
存储 缓存 JavaScript
vue表单案例练习:vue表单创建一行数据及删除数据的实现与理解
vue表单案例练习:vue表单创建一行数据及删除数据的实现与理解
44 2
|
6天前
|
JavaScript 前端开发
如何在 Vue 项目中配置 Tree Shaking?
通过以上针对 Webpack 或 Rollup 的配置方法,就可以在 Vue 项目中有效地启用 Tree Shaking,从而优化项目的打包体积,提高项目的性能和加载速度。在实际配置过程中,需要根据项目的具体情况和需求,对配置进行适当的调整和优化。
|
6天前
|
存储 缓存 JavaScript
在 Vue 中使用 computed 和 watch 时,性能问题探讨
本文探讨了在 Vue.js 中使用 computed 计算属性和 watch 监听器时可能遇到的性能问题,并提供了优化建议,帮助开发者提高应用性能。
|
6天前
|
存储 缓存 JavaScript
如何在大型 Vue 应用中有效地管理计算属性和侦听器
在大型 Vue 应用中,合理管理计算属性和侦听器是优化性能和维护性的关键。本文介绍了如何通过模块化、状态管理和避免冗余计算等方法,有效提升应用的响应性和可维护性。
|
6天前
|
存储 缓存 JavaScript
Vue 中 computed 和 watch 的差异
Vue 中的 `computed` 和 `watch` 都用于处理数据变化,但使用场景不同。`computed` 用于计算属性,依赖于其他数据自动更新;`watch` 用于监听数据变化,执行异步或复杂操作。
|
6天前
|
JavaScript 前端开发 UED
vue学习第二章
欢迎来到我的博客!我是一名自学了2年半前端的大一学生,熟悉JavaScript与Vue,目前正在向全栈方向发展。如果你从我的博客中有所收获,欢迎关注我,我将持续更新更多优质文章。你的支持是我最大的动力!🎉🎉🎉
|
7天前
|
存储 JavaScript 开发者
Vue 组件间通信的最佳实践
本文总结了 Vue.js 中组件间通信的多种方法,包括 props、事件、Vuex 状态管理等,帮助开发者选择最适合项目需求的通信方式,提高开发效率和代码可维护性。