Vue组件应用

  Vue的组件是可复用的 Vue 实例,且带有一个名字 。我们可以在一个通过 new Vue 创建的 Vue 根实例中,把这个组件作为自定义元素来使用。因为组件是可复用的 Vue 实例,所以它们与 new Vue 接收相同的选项,例如 datacomputedwatchmethods 以及生命周期钩子等。仅有的例外是像 el 这样根实例特有的选项。

 

一  创建组件

  Vue提供了三种不同的方式来定义组件,分别是:全局组件,私有组件,单文件组件。接下来就让我一一道来。

 

  1,全局组件

  注册全局组件非常简单,也是很常用的一种方式。

1 Vue.component('myCom',{
2     template:'<div><p>我是一个全局<span>组件</span></p></div>'
3 });

  Vue.component()方法需要两个参数:

    第一个,组件名称;

    第二个,实例(初始化)对象,可以包含所有使用new方式创建Vue实例时提供的所有属性,除了el。

  注意:组件的实例对象必须提供一个template属性,用作该组件的HTML代码模板,且在该模板中有且只能有一个根元素。全局组件的注册必须在创建Vue实例之前。

  小技巧:由于在编写JS时,一般没有HTML代码提示,创建组件模板代码会很不方便,所有可以在HTML文件中使用<template>元素创建模板,然后在组件的template属性中使用id选择器引用该模板。

  注意:<template>元素必须在new Vue实例接管的根元素外部。

1 <template id="tem">
2     <div>
3         <p>我是组件内的p</p>
4         <span>我是组件中的span</span>
5     </div>
6 </template>
7 <!-- 在HTML中 -->
1 Vue.component('myCom',{
2     template:'#tem'
3 });
4 //在组件中

  

  2,私有组件

  全局创建的组件在所有Vue实例中均可以使用,有时候这并不符合我们的需求。你可以通过以下方式定义Vue实例的私有组件,这些组件只能在该Vue实例根元素内部使用。

1 var vm = new Vue({
2     el:'#app',
3     components:{
4         mycom:{
5             template:'#tem'
6         }
7     }
8 });

  通过Vue实例的components属性可以定义私有组件,该属性绑定一个对象,对象的属性名是组件名,属性值是组件实例对象。

  

  3,单文件组件

  Vue的单文件组件是一个以.vue为后缀名的文件。由于HTML和JavaScrip不能识别.vue文件,所以不能直接使用这种方式的组件,必须配合webpack或vue-cli工具才能正确解析.vue文件。这里的重点是Vue单文件组件,所以有兴趣的同学请移步webpack中文网

1 <tempalte>
2     //HTML模板
3 </template>
4 <script>
5     //JS代码
6 </script>
7 <style>
8     //CSS代码
9 </style>

  .vue文件的名称就是组件的名称,其结构非常简单、清晰:

    <template>标签是组件的HTML模板;

    <script>标签是逻辑代码;

    <style>标签中是样式代码。

 

二  组件的使用

  不管以哪种方式创建Vue组件,我们最终的目的是在HTML页面中展示出来。本节将详细介绍Vue组件使用方式。

 

  1,组件标签

  要把我们创建的Vue组件添加到页面中去,只需要把组件名当做标签来使用即可。

1 Vue.component('myCom',{
2     template:"#tem"
3 });
4 var vm = new Vue({
5     el:"#app"
6 });
7 //JS部分
1 <div id="app">
2     <my-com></my-com>
3 </div>
4 <!-- HTML部分 -->

  小技巧:注册组件时,建议使用全小写形式命名,因为HTML标签要求使用小写字母。如果你一定要遵守小驼峰命名规则,那么你应该在使用组件时用“-”短横线把单词分隔开。

  

  2,组件复用

  Vue的组件可以重复使用。

1 <div id="app">
2     <my-com></my-com>
3     <my-com></my-com>
4     <my-com></my-com>
5 </div>

  当然,全局组件可以在任何地方使用,而私有组件只能在实例接管元素内部使用。

  组件不仅可以简单的重复使用,还可以嵌套。

 1 var vm = new Vue({
 2     el:'#app',
 3     compontents:{
 4         mycom1:{
 5             template:'<div>组件一  <mycom2></mycom2></div>'
 6         },
 7         mycom2:{
 8             template:'<div>组件二</div>'
 9         }
10     }
11 });

  

  3,另一种使用方式

1 var mycom = {
2     tempalte:'<div id="app2">hello</div>'
3 };
4 var vm = new Vue({
5     el:'#app',
6     render:function(createEl){
7         return createEl(mycom);
8     }
9 });

  使用render方式渲染组件:给Vue实例添加render属性,该属性值是一个接收一个方法作为参数的函数。参数用于创建Vue组件,return该组件将替代Vue实例接管的#app元素。最终的表现是:页面上将不再出现#app的div,取而代之的是#app2的div。

  这种方式一般配合单文件组件使用,如果要渲染多个组件,只需要创建多个Vue实例即可。

 

三  数据传递(通信)

 

  1,props传递数据(父组件 --> 子组件)

  通过在子组件绑定props属性,实现父组件向子组件传递数据。props属性值是一个数组,数组元素被定义用来接收父组件传递来的数据,然后通过v-bind指令指定数组元素接收哪些数据。子组件通过访问props数组元素就可以访问到父组件传递过来的数据了,就如同访问data里面的值。

 

1 <div id="app">
2     <mycom :fromFatherMsg="toSonMsg"></mycom>
3 </div>
4 <!-- HTML部分 -->
 1 Vue.component({
 2     template:"<div>{{fromFatherMsg}}</div>",
 3     props:["fromFatherMsg"]
 4 });
 5 var vm = new Vue({
 6     el:'#app',
 7     data:{
 8         toSonMsg:'这是给子组件的数据'
 9     }
10 });
11 //JS部分

  通过上面的例子,我们可以将这个过程简单的分为三步:

    第一步,在子组件上添加一个数组属性props,在数组中定义用来接收数据的变量(以字符串形式存储);

    第二步,使用子组件时通过v-bind指令,绑定预先定义的接收变量和父组件将要传递过来的值;

    第三步,在子组件中,如同访问data中的数据一样,访问props数组元素接收到的数据。

  

  2,$emit传递方法(父组件 --> 子组件)

  父组件向子组件传递方法,是通过自定义事件来实现的。

  监听(同时注册)自定义事件有两种方式:组件上使用v-on指令、实例的$on()方法。这里我们将使用第一种方式来做演示。

  子组件通过实例的$emit()方法触发自定义事件,这里的事件名将成为$emit()方法的第一个参数。

1 <div id="app">
2     <mycom @fromFatherFun="toSonFun"></mycom>
3 </div>
4 <!-- HTML部分 -->
 1 Vue.component({
 2     template:"<div><button @click="myFun">点击执行来自父组件的方法</button></div>",
 3     methods:{
 4         myFun:function(){
 5             this.$emit('fromFatherFun');
 6         }
 7     }
 8 });
 9 var vm = new Vue({
10     el:'#app',
11     methods:{
12         toSonFun(){
13             console.log( "这是给子组件的方法");
14     }
15 });
16 //JS部分

  注意:和传递数据一样,子组件不能直接使用父组件的方法。子组件需要通过实例的$emit()方法间接执行来自父组件的方法。

  这一过程也可以分为三步:

    第一步,使用子组件时,通过v-on指令自定义一个事件;

    第二步,绑定自定义事件和父组件需要传递的方法(指定回调函数);

    第三步,通过子组件的$emit()方法(通过实参指定需要触发的自定义事件)触发父组件的方法执行;

  

  3,子组件抛出值(子组件 --> 父组件)

  子组件在通过$emit()触发自定义事件时,可以同时利用方法的第二个参数,向外抛出一个值。回调函数(上面父组件传递下来的方法)需要定义一个形参来接收这个子组件抛出的值。

 1 //接上面的例子
 2 Vue.component({
 3     template:"<div><button @click="myFun">点击执行来自父组件的方法</button></div>",
 4     data(){
 5         return {name:'ren'};
 6     },
 7     methods:{
 8         myFun:function(){
 9             this.$emit('fromFatherFun',this.name);
10         }
11     }
12 });
13 var vm = new Vue({
14     el:'#app',
15     data:{
16         nameFromSon:null
17     }
18     methods:{
19         toSonFun(data){
20         this.nameFromSon = data;
21     },
22 });

  子组件抛出一个值的原理和父组件给子组件传递方法原理是一样的,只不过是不同的用法而已。虽然有点绕,但有用哦。

  

  4,获取子组件的引用

  在使用子组件时,通过绑定ref属性,父组件可以通过Vue实例的$refs属性拿到子组件的引用,然后就可以直接访问子组件的属性或方法了。

1 <div id="app">
2  <mycom ref="sonCom"></mycom>
3   <button @click="printMsgFromSon">点击打印子组件的信息</button>
4 </div>
5 <!-- HTML部分 -->
 1 Vue.component('mycom',{
 2     data:function(){return {name:'ren'}}
 3 });
 4 
 5 var vm = new Vue({
 6     el:'#app',
 7     methods:{
 8         printMsgFromSon:function(){
 9             console.log(this.$refs.sonCom.name);
10         }
11     }
12 });
13 //JS部分

  小技巧:ref属性不仅可以用在组件上,也可以用在其他标准HTML标签上,这样Vue实例就可以获取到原生的DOM对象了。

  注意:即使子组件是Vue实例的私有组件,实例也不能直接使用组件的相关数据,还是需要通过$refs等属性来间接访问。

 

  5,兄弟组件间传值

   兄弟组件间实现通信也是采用自定义事件的形式。不过,这一般需要一个空的Vue实例作为中介,配合使用$on()和$emit()实现兄弟组件间的传值。

 1 <div id="app">
 2     <com1></com1>
 3     <com2></com2>
 4 </div>
 5 <template id="com1">
 6   <div>
 7     <p>com1组件:{{name}}</p>
 8     <button @click="send">将数据发送给com2</button>
 9   </div>
10 </template>
11 <template id="com2">
12   <div>
13     <p>com2组件:{{name}}</p>
14   </div>
15 </template>
16 <!-- HTML部分 -->

 

 1 var event = new Vue();
 2 var vm = new Vue({
 3     el:'#app',
 4     components:{
 5         com1:{
 6             template:'#com1',
 7             data(){
 8                 return{name:'ren'};
 9             },
10             methods:{
11                 send(){
12                     event.$emit('sendMsg',this.name);
13                 }
14             }
15         },
16         com2:{
17             template:'#com2',
18             data(){
19                 return {name:null};
20             },
21             created(){//因为不知道什么时候会被触发,所以一般选择在生命周期钩子中监听
22                 event.$on('sendMsg',name => {this.name = name;})
23                 //这里需要使用箭头函数,使其内部的this指向com2
24             }
25         }
26     }
27 });
28 //JS部分

 

 

四  其他事项

 

  1,单独的data

  经过上面的学习,你可能已经发现了一个问题:组件中的data属性是一个函数返回的对象。

1 Vue.component("mycom",{
2     template:"",
3     data(){
4         return { //some code };
5     }
6 });
7 //这是ES6的写法,等同于data:function(){return {some code};}

  由于data属性绑定的是一个对象,而对象是一个引用类型,为了保证为每个组件维护一份独立的数据,组件的data属性必须是一个函数。

  

  2,插槽<slot>

  当你读到这里时,你可能会有一个疑问:既然我们可以用标签形式使用Vue组件,那么是否可以在开始标签和结束标签之间填些内容呢?如果可以的话,该如何做呢?Vue的答案是肯定的。

  首先请看下面的例子:

1 <div id="app">
2     <com>我是插槽内容</com>
3 </div>
4 <!-- HTML部分 -->
1 Vue.compenent('com',{
2     template:'<div><p>我是组件</p><slot>我是默认值<slot></div>'
3 });
4 var vm = new Vue({
5     el:'#app'
6 });
7 //JS部分

  "我是插槽内容"将替换com组件中<slot>元素。

  注意:如果在使用子组件时没有提供插槽值,那么<slot>元素中的默认值将会生效,前提是你已经定义了这些值。

  上面的例子中,组件最终渲染的HTML结构如下:

1 <div>
2     <p>我是组件</p>
3     我是插槽内容
4 </div>

  注意:插槽的内容不仅可以是文本内容,还可以是HTML代码,甚至另一个组件。

  如果你需要在一个组件中定义多个插槽,那么你应该需要用到<slot>元素的name属性,来指定每个插槽应该拥有怎么样的模板。

 1 <div>
 2     <com>
 3         <template v-slot:"header">
 4             <!-- 单独的HTML模板 -->
 5         </template>
 6         <div><p>我是默认的模板</p></div>
 7         <template v-slot:"footer">
 8             <!-- 单独的HTML模板 -->
 9         </template>
10     </com>
11 </div>
12 <!-- HTML部分 -->
1 Vue.component('com',{
2     tempalte:'<div><slot name="header"></slot><slot></slot><slot name="footer"></slot></div>'
3 });
4 var vm = new Vue({
5     el:'#app'
6 });

  具名的插槽需要在使用组件时,用<template>元素单独定义模板,并通过v-slot指令以参数的形式指定:“我是xxx插槽的模板”。

  其他所有没有包裹在<template>元素内的模板,将自动归为匿名的<slot>元素下面。

 

  

  3,特殊的嵌套元素

  有些 HTML 元素,诸如 <ul><ol><table> 和 <select>,对于哪些元素可以出现在其内部是有严格限制的。而有些元素,诸如 <li><tr> 和 <option>,只能出现在其它某些特定的元素内部。要怎样才能在这些元素中正确的渲染组件呢?幸好,Vue提供了is特性:

1 <table>
2   <tr is="mycom"></tr>
3 </table>

  注意:如果你使用字符串定义组件模板(例如:template: '...')、或者单文件组件(.vue)、或者<script>标签(<script type="text/x-template">),那么你完全可以忽略掉这个限制。

 

posted @ 2019-12-09 16:58  任风来去匆匆  阅读(1137)  评论(0编辑  收藏  举报