谈谈Vue两大核心之一:组件如何应用如何通信

在使用Vue技术栈开发项目时,必不可少的会用到组件,不得不说,Vue的组件功能特别强大,使整个项目功能模块及各种view页面分类井井有条,对于我这种看到大堆代码就急躁的人很是友好了。

本文目录

  • 1.Vue组件
  • 2.组件注册(全局和局部)
  • 3.动手创建一个组件
  • 4.实现父子通信props(静态动态和传值类型)
  • 5.实现父子通信$ref
  • 6.实现子组件向父组件通信$emit
  • 7.实现子向子组件的通信

1.Vue组件

  1. 组件是可复用的 Vue 实例,是一段可重用的UI,它封装了其中的所有行为和功能。
    例如:Chrome的日期输入框()就是浏览器提供的一个组件,只需要在使用的地方添加一个类型(type)为date的输入框(input),即:,就可以得到出现日期的功能。

  2. 组件使代码具有模块化、可重用性、易于维护。

  3. 可以使用独立可复用的小组件来构建大型应用,很多款应用的页面都可以抽象成一棵组件树。如下图
    谈谈Vue两大核心之一:组件如何应用如何通信
    一个项目中通常只有一个根组件,比如App(<App>)组件。对于上图,根组件引用了layout组件,layout组件可以引用它的header、footer、content等组件。一些公共组件用于页面不同部分及不同页面的引用。

    一个项目中只有一个实例化对象,就是new Vue的实例。如下代码:

     new Vue({
          el: document.getElementById('app'),
          components: { // 声明要用的组件们
            // key 是组件名,value 是组件对象,es6写法可省略key
           ComA,
           ComB,
          },
          template: `
            <div>
             <ComA> <ComA/>
             <ComB> <ComB/>
            </div>
          `
        })
    

2.组件注册

子组件注册成功以后才可以在父组件中引用。

  1. 全局注册
    注册组件的时候,我们要给它一个组件名称,如注册一个全局组件语法格式如下:

    Vue.component(componentName, options)
    

    componentName 为组件名,options 为配置选项。注册后,我们可以使用以下方式来调用组件:

    <componentName></componentName>
    

    全局组件在注册之后可以用在任何新创建的 Vue 根实例 (new Vue) 的模板中。

  2. 局部注册
    在实例选项中注册局部组件,这样组件只能在这个实例中使用。称为局部组件。

    <div id="app">
        <ComponentA ></ComponentA >
    </div>
     
    <script>
    var ComponentA= {
      template: '<h1>局部注册组件!</h1>'
    }
     
    // 创建根实例
    new Vue({
      el: '#app',
      components: {
        ComponentA,
      }
    })
    </script>
    

    在 ES2015+ 中,在对象中放一个类似 ComponentA 的变量名其实是 ComponentA: ComponentA 的缩写。

    局部注册的组件在其子组件中不可用。
    例如,如果你希望 ComponentA 在 ComponentB 中可用,则你需要这样写:

    var ComponentA = { /* ... */ }
    
    var ComponentB = {
      components: {
        'component-a': ComponentA
      },
    }
    

3.创建一个组件

  1. 我在Vue中创建一个button组件,button.vue组件部分代码如下:

    <template lang="html">
    	<div class="btn">
    		<div class="btn-header">&nbsp&nbsp btn title</div>
    		<div class="btn-body">&nbsp&nbspbtn body</div>
    		<div class="btn-content">&nbsp&nbspbtn content</div>
    	</div>
    
    </template>
    
    <script>
        export default {
      }
    </script>
    
    <style>
    	.btn{
    		width: 500px;
    		margin: 0 auto;
    	}
    	.btn-header{
    		height: 50px;
    		background-color: blue;
    	}
    	.btn-body{
    		height: 50px;
    	}
    	.btn-content{
    		height: 50px;
    		background-color: red;
    	}
    </style>
    

    在App.vue中使用组件,使用方式如下:

    <template>
      <div id="app" class="container">
      	//引用组件<Button/>
        <Button/>
      </div>
    </template>
    
    <script>
    	//通过webpack 使用 ES2015 import模块导入组件
      import Button from "./components/button.vue"
      export default {
        name: 'App',
        //components中定义想要使用的组件
        components: {
          Button,
        }
      }
    </script>
    
    

    一个简单的组件需要一个名称(name,比如上例中的Button )作为元素的标签和由该标签渲染的模板(<template>)。
    效果如下:
    谈谈Vue两大核心之一:组件如何应用如何通信

4.父子通信props

组件通信:父组件向子组件传递数据使用props。

  1. 介绍属性

    prop 是父组件用来传递数据的一个自定义属性。

    父组件的数据需要通过 props 把数据传给子组件,子组件需要显式地用 props 选项声明 。

    在Vue中,组件通过props接收外部数据。

  2. 在button组件上添加了Props,在模板
    template中使用{{title1}}、{{title2}}、{{title3}}给html元素动态的传值,从而取代了之前的静态文本。如下代码:

    button.vue文件

    <!--button.vue文件-->
    <template lang="html">
    	<div class="btn">
    		<div class="btn-header">{{title1}}</div>
    		<div class="btn-body">{{title2}}</div>
    		<div class="btn-content">{{title3}}</div>
    	</div>
    
    </template>
    
    <script>
        export default {
        	name: "button",
        	props: ["title1","title2","title3"],
        	//data()必须是函数
        	data() {
        		return {};
        	}
      }
    </script>
    

    App.vue文件:

    <template>
      <div id="app" class="container">
        <Button v-for="(item, index) in titleList" :title1="item.title1" :title2="item.title2" :title3="item.title3" :key="index"/>
      </div>
    </template>
    
    <script>
      import Button from "./components/button.vue"
      export default {
        name: 'App',
        components: {
          Button,
        },
        //
        data() {
          return {
            titleList:[
            {
              title1:'first',
              title2: "i am the first title",
              title3: "i am the first2 title",
            },
            {
              title1:'second',
              title2: "i am the second title",
              title3: "i am the second2 title",
            },
            {
              title1:'three',
              title2: "i am the three title",
              title3: "i am the three2 title",
            }]
          }
        }
      }
    </script>
    

    类似于用 v-bind 绑定 HTML 特性到一个表达式,也可以用v-for、 v-bind 动态绑定 props 的值到父组件的数据中。每当父组件的数据变化时,该变化也会传导给子组件。

    上面代码运行效果为:
    谈谈Vue两大核心之一:组件如何应用如何通信

  3. 【注意:】
    prop 是单向绑定的,即单向数据流,当父组件的属性变化时,将传导给子组件,但是不会反过来。这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解。

  4. prop 除了可以接收数组形式的值,还可以是对象形式。实际上任何类型的值都可以传给一个 prop。
    如果希望每个 prop 都有指定的值类型。可以以对象形式列出 prop,这些属性的名称和值分别是 prop 各自的名称和类型:

    props: {
      title: String,
      likes: Number,
      isPublished: Boolean,
      commentIds: Array,
      author: Object,
      callback: Function,
      contactsPromise: Promise // or any other constructor
    }
    
  5. 禁用特性继承。
    如果你不希望组件的根元素继承特性,可以在组件的选项中设置 inheritAttrs: false。例如:

    Vue.component('my-component', {
      inheritAttrs: false,
      // ...
    })
    

    inheritAttrs: false 选项不会影响 style 和 class 的绑定

5.父子通信$ref

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

对官方文档的解释:

  • 如果ref在普通的 DOM 元素上使用,引用指向的就是 DOM 元素即通过$ref可能获取到该DOM 的属性集合,继而访问到DOM元素,作用与jquary筛选器类似。

  • 如果ref用在子组件上,引用就指向组件实例,可以理解为对子组件的索引,通过$ref可能获取到在子组件里定义的属性和方法。
    如下ref用在子组件的例子:
    App.vue文件

    <template>
      <div id="app" class="container">
        <Button ref="msg"/>
      </div>
    </template>
    
    <script>
      import Button from "./components/button.vue"
      export default {
        name: 'App',
        components: {
          Button,
        },
         mounted:function(){
          console.log(this.$refs.msg);
          this.$refs.msg.getMessage('$refs引用子组件')//减少获取dom节点的消耗
        },
       
      }
    </script>
    

    子组件button.vue文件:

    <template lang="html">
    	<!--模板要有一个根节点-->
    	<div class="btn">
    		<div class="btn-header"><p>{{message}}</p></div>
    		<p>{{message}}</p>
    	</div>
    </template>
    
    <script>
        export default {
        	name: "button",
        	//props: ["title1","title2","title3"],
        	//data()必须是函数
        	data() {
        		return {
        			message:''
        		}
        	},
        	methods:{
        		getMessage(m){
        			this.message=m;
        		}
        	}
      }
    </script>
    

    上方代码通过ref=‘msg'将子组件button的实例指给$ref,并且通过.msg.getMessage()调用子组件的getMessage()方法,然后成功将参数传递给子组件。实现了父组件向子组件间的通信。
    代码运行效果如下:
    谈谈Vue两大核心之一:组件如何应用如何通信
    打印的this.$refs.msg内容,便于看到$ref获取的内容。谈谈Vue两大核心之一:组件如何应用如何通信
    $ref使用过后发现取DOM、操作DOM十分方便。

    prop$ref之间有些许差别:

    • 1.prop 侧重数据的传递,不能调用子组件里的属性和方法。
    • 2.$ref 侧重索引,主要用来调用子组件里的属性和方法,其实并不擅长数据传递。而且ref用在dom元素的时候,能起到选择器的作用,这个功能比作为索引更常用。

6.子组件向父组件通信$emit

props$refs属性都是用于父组件向子组件传值,实现父子通信。而子组件向父组件传递数据使用$emit$emit用于触发当前实例上的事件,通过事件传递数据给父组件。
父组件App.vue文件

<template>
  <div id="app" class="container">
    <h2>{{message}}</h2>
    <!--通过v-on绑定子组件getMessage方法,并为其赋值为showMessage方法-->
    <Button v-on:getMessage="showMessage"/>
  </div>
</template>

<script>
  import Button from "./components/button.vue"
  export default {
    name: 'App',
    components: {
      Button,
    },
    //data()必须是函数
      data() {
        return {
          message:''
        }
      },
    methods:{
        showMessage(m){
          this.message=m;
        }
      },
  }
</script>

子组件button.vue文件:

<template lang="html">
	<!--模板要有一个根节点-->
	<div class="btn">
		<h2>我是子组件,我的内容会通过$emit传给父组件。</h2>
	</div>
</template>

<script>
    export default {
    	name: "button",
    	//props: ["title1","title2","title3"],
	 	mounted:function(){
	      console.log(this.$emit);
	      this.$emit('getMessage','我是父组件,这是通过$emit从子组件获得。')
	    },
  }
</script>

代码运行效果为:
谈谈Vue两大核心之一:组件如何应用如何通信
子组件主要通过事件传递数据给父组件,如打印的this.$emit结果为event事件函数,如下:
谈谈Vue两大核心之一:组件如何应用如何通信

7.实现子向子组件的通信

Vue 没有直接子组件对子组件传递数据的方法,如若事项,是先把数据传到父组件,然后再由父组件传给相应的子组件。Vue 推出了一个状态管理工具 Vuex,可以很方便实现组件之间的参数传递。如果你有一处需要被多个实例间共享的状态,可以简单地通过维护一份数据来实现共享。可以参考vuex官方教程

【待续】

每天进步一点点、快乐一点点、充实一点点、加油!@girl