Vue的组件是可复用的 Vue 实例,且带有一个名字 。我们可以在一个通过 new Vue 创建的 Vue 根实例中,把这个组件作为自定义元素来使用。因为组件是可复用的 Vue 实例,所以它们与 new Vue 接收相同的选项,例如 data、computed、watch、methods 以及生命周期钩子等。仅有的例外是像 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传递方法(父组件 --> 子组件)
父组件向子组件传递方法,是通过自定义事件来实现的。子组件通过实例的$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()触发父组件的方法时,可以同时利用$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等属性来间接访问。
四 其他事项
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">),那么你完全可以忽略掉这个限制。
|
|