Vue入门(五)之组件


vue的基本语法已经差不多了,我们开始学习组件。

1、组件的概念

简单概括,组件就是自定义标签。

我们知道,vue框架的两大核心:数据驱动组件化

组件封装的是完整的功能(包括:HTML、CSS、JS),而函数只封装JS(逻辑)

2、组件的使用(定义,注册,和使用)

2.1、定义组件

  • 第一种
let 组件变量名= Vue.extend({
       template:'<div class="header">{{msg}},我是header组件</div>'
       data:function(){
			return {
				msg:”hi”
			}
	   },
});

此处注意:

  • 组件变量名不一定就是组件名;

  • template中引号的用法,使用单双引号时,只要换行就会报错,除非模板都写在一行,或者直接用es6模板字符串写法换行不报错,亲测有用哦;

  • data必须为函数,且有返回值。

  • 自定义组件没有el项,切记。

  • 第二种

let 组件变量名={
        配置项
}; 
//定义组件  
let myZujian = {
    template:`
        <div>
            <p>我是自定义组件{{meg}}</p>
        </div>
    `,
    data:function(){
        return {
            meg:'真的是',
        }
    }
}

2.2、注册组件

只有经过注册的组件才能够使用

  • 全局注册
Vue.component('组件名',组件变量名);

全局注册的组件,在任何vue对象里都可以使用

  • 局部注册(用的较多)
//在vue对象的components选项里进行注册
new Vue({
     el:
     components:{
    	 组件名:组件变量名
     }     
});

局部注册的组件,只能在当前vue对象(组件)里使用

//局部注册,只能在该vue对象使用
let vm = new Vue({
    el:'.box',
    data:{
    },
    //在components里注册
    components:{
        myZujian:myZujian,
    }

})

2.3、使用组件

组件定义完了,也注册了,那怎么使用呢?
很简单,就像我们使用官方html标签一样使用就好了!

<组件名></组件名>
<!-- 组件名就是你注册时的组件名,不一定就是定义时的变量名哦 -->
<div class="box">
      <my-zujian></my-zujian>
</div>

2.4、组件编写方式与 Vue 实例的区别:

1、组件名不可和html官方的标签名同名,组件名如果是驼峰命名,那么使用时,用短横线连接,或者组件名首字母大写。

​ 2、组件没有el选项,只有根实例存在el,组件里使用template定义模板

​ 3、组件模板(html代码)只能有一个根标签

这里还有个问题:data为什么是个函数?

如果不是函数,那么,复用的组件的data共享同一块内存空间,数据会相互影响。

3、组件嵌套

把一个组件的标签写在另外一个组件的template里,就是组件嵌套。

//定义子组件1  
let myCom1 = {
    template:`
     <p>我今年{{mes}}岁了</p>
    `,
    data:function(){
        return {
            mes:12
        }
    }
}

 //定义子组件2  组件2嵌套组件1
let myCom2 = {
    template:`
         <div>
             {{mee}}
             <my-com1></my-com1>    
         </div>
    `,
    components:{
        myCom1,
    },
    data:function(){
        return {
            mee:'真的真的'
        }
    }
}

4、组件的属性

使用props(property的简写)声明组件的属性。 props是外部给组件传入的数据,而组件的data是组件内部的数据。

4.1、使用 Prop 传递静态数据

  • 声明属性

props是个数组,可以声明多个属性

 //定义子组件  
 let myCom1 = {
     //声明两个自定义属性
     props:['name','sex'],
     template:`
     <div>
          <p>年龄:{{age}}</p>
          <p>姓名:{{name}}</p>
          <p>性别:{{sex}}</p>
      </div>
     `,
     data:function(){
         return {
             age:18
         }
     }
 }
  • 传入数据
<!--  使用该自定义组件时,就可以传入数据了 -->
<div class="box">
     {{mes}}
      <my-com1 name="小芬" sex="23"></my-com1>
</div>

4.2、使用 Prop 传递动态数据

方法其实一样,就是传入数据的时候,将数据改成活的。

<!-- 使用v-bind -->
<div class="box">
       {{mes}}
       <my-com1 :name="name" :sex="sex"></my-com1>
</div>
let vm = new Vue({
    el:'.box',
    data:{
        mes:'原vue对象中的mes',
        name:'小芬',
        sex:'女',
    },
    //局部注册组件
    components:{
        myCom1,
    }
})
  • 特殊情况:当传入的数据是一个对象的所有属性
<!--  直接用v-bind="对象名" -->
<div class="box">
     {{mes}}
     <my-com1 v-bind="info"></my-com1>
</div>
let vm = new Vue({
    el: '.box',
    data: {
        info: {
            age: 21,
            name: '小芬',
            sex: '女',
        }
    },
    //局部注册组件
    components: {
        myCom1,
    }
})
  • 还有特殊情况?

在 JavaScript 中对象和数组是通过引用传入的(传的是地址),所以对于一个数组或对象类型的 prop来说,在子组件中改变这个对象或数组本身将会影响到父组件的状态(data),相当于函数的形参是引用类型。

4.3、Prop 验证

就是在声明属性的时候,对属性类型进行验证,注意:此时props是一个对象,而不是数组

//定义子组件  
let myCom1 = {
    //prop验证
    props:{
        "name":{
            type:String,
            required:true,
        },
        "sex":{
            type:[String,Number]
        },
        "age":[Number]
    },     
    template: `
   <div>
        <p>年龄:{{age}}</p>
        <p>姓名:{{name}}</p>
        <p>性别:{{sex}}</p>
    </div>
   `,
}

4.4、单向数据流

Prop 是单向绑定的:当父组件的属性(数据)变化时,将传导给子组件,但是反过来不会。
​ 另外,每次父组件更新时,子组件的所有 prop 都会更新为最新值。这意味着你不应该在子组件内部改变 prop。

  • 让我们再加深一下props的理解:

在组件内部用props声明的属性,相当于封装函数时声明的形参。
使用组件时,相当于调用函数,传递实参。

5、组件的事件

1、绑定事件:

​    HTML(标签)里的绑定方式:v-on
    JS(Vue)里绑定方式: vue对象.$on(“事件名”)2、触发事件 : 

    vue对象.$emit(“事件名”,参数);

我们在使用html官方标签绑定事件时,触发事件功能官方帮我们做好了,但是我们自定义组件(标签)时,需要自己去做触发事件的功能。

我们先看一段实例代码:

<div class="box">
     <my-com1 @myclick="fn"></my-com1>
</div>
//定义子组件  
let myCom1 = {
    template: `
        <input type="button" @click="add" value="点击加1"/>
   `,
   data:function(){
       return {
           mes:17
       }
   },
   methods:{
       add(){
           //触发事件,自定义标签的触发事件,
           // 也可以传递参数给父组件
           this.$emit('myclick','我是子组件的人');
       }
   }
   
}

let vm = new Vue({
    el: '.box',
    data: {

    },
    //局部注册组件
    components: {
        myCom1,
    },
    methods:{
        fn(str){
            console.log('从子组件传来的数据:',str);
        }
    }
})

步骤分析:

  1. 子组件myCom1模板里调用触发事件函数add;
  2. 使用该组件时,调用事件处理函数fn

6、组件的内容(插槽)

组件的内容就是标签的innerHTML。vueJS里使用Slot(插槽)分发内容。
将父组件的内容(DOM)放到子组件指定的位置叫作内容分发

props用来处理标签的属性 ,slot用来处理标签的内容。

6.1、单个插槽

子组件中插入一条父组件内容

<slot></slot>
<div class="box">
	   <my-com1>
	       <p>我在子组件里面</p>
	   </my-com1>
</div>
//定义子组件  
 let myCom1 = {
     template: `
     <div>
         <div>我是子组件,并附带{{mes}}</div>
         <slot></slot>
     </div>
        
    `,
    data:function(){
        return {
            mes:17
        }
    },
 }

 let vm = new Vue({
     el: '.box',
     data: {
     },
     //局部注册组件
     components: {
         myCom1,
     }
 })

6.2、具名插槽

父级给子级传来了多个DOM(HTML元素),而且需要把不同的DOM放在子组件不同位置时,就需要给slot起名字,这就是具名插槽
slot元素可以用一个特殊的属性name 来配置如何分发内容。

<slot name="插槽名"></slot>
<div class="box">
      <my-com1>
          <p slot="p3">我在子组件里面1</p>
          <p slot="p2">我在子组件里面2</p>
          <p slot="p1">我在子组件里面3</p>
      </my-com1>
</div>
 //定义子组件  
 let myCom1 = {
      template: `
      <div>
          <slot name="p1"></slot>
          <div>我是子组件,并附带{{mes}}</div>
          <slot name="p2"></slot>
          <slot name="p3"></slot>
      </div>          
     `,
     data:function(){
         return {
             mes:17
         }
     },
  }

6.3、编译作用域

即 父组件模板的内容在父组件作用域内(父组件对应的对象的作用域)编译;子组件模板的内容在子组件作用域内编译。

有兴趣可以点击插槽作用域

7、组件(间)的通信

vue组件之间的通信(传递数据)是必然的,根据vue组件之间的关系不同(父子,兄弟,或者无关组件),方法也不同。

7.1、父子组件间的通信

  • 父—> 子: props, 子—> 父:事件

    这个其实我们前面也用到过,组件事件里讲过子组件触发事件时可以带参传给父组件,达到父子组件的通信。

  • 父---->子:refs

    父传子也可以使用refs

    什么是refs,可以理解为给组件一个id名,通过这个id值获取DOM,也就是标签,也即组件对象,从而操作组件对象里的属性。

    refs—>dom----->组件对象----->操作组件对象的属性

  • 如果某个元素使用ref属性,那么,在vue对象里,就能用this.$refs 访问。

//基本格式
 <p ref = "pId"> {{msg}}</p>

 methods:{
     testf:function(){
       	this.$refs.pId.innerHTML = "hi";
     }
}
<div class="box">
      <input type="button" value="点击加1" @click="add">
      <my-com1 ref="mcon">
          <p slot="p2">{{mes}}</p>
      </my-com1>
</div>
//定义子组件  
 let myCom1 = {
     template: `
     <div>
         <div>我是子组件,并附带{{mes}}</div>
         <slot name="p2"></slot>
     </div>          
    `,
    data:function(){
        return {
            mes:17
        }
    },
 }

 let vm = new Vue({
     el: '.box',
     data: {
         mes:22
     },
     //局部注册组件
     components: {
         myCom1,
     },
     methods:{
         add(){
             this.mes++;
             //获取子组件对象,改变子组件mes的值
             this.$refs.mcon.mes ++;
         }
     }
 })

7.2、兄弟组件/无关组件间的通信

  • 事件总线(event-bus)

    event-bus实质就是创建一个vue实例,通过一个空的vue实例作为桥梁实现vue组件间的通信。它是实现兄弟组件(或者任何无关组件,也可以实现父子)通信的一种解决方案。

基本步骤:

1、单独new一个Vue空对象:  let bus= new Vue();
2、在组件A里,绑定一个自定义事件(相当于定义了一个自定义事件):
	    bus.$on('eclick',target => {
          console.log(target) 
        })
3、在组件B里,触发事件
      bus.$emit('eclick',"b传给a的");

实例代码:

<div class="box">
        <my-com1></my-com1>
        <my-com2></my-com2>
</div>
//定义子组件1  
 let myCom1 = {
     template: `
     <input type="button" value="点击触发" @click="add"/>      
    `,
    data(){
        return {
            mes:40
        }
    },
    methods:{
        add(){
         vm2.$emit('click',this.mes)
        }
    }

 }

 //子组件2
 let myCom2 = {
     template:`
         <p>我是子组件二,附带{{mes2}}</p>
     `,
     data:function(){
         return {
             mes2:20
         }
     },
     created(){
         // created函数是,组件自动调用的
         vm2.$on('click',(str)=>{
             // console.log(str);
             this.mes2 = str;
         }) 
     }
 }
 
 //单独new一个空的Vue对象  绑定事件一方为接收方 ,触发事件为发送方
 let vm2 = new Vue({})

 let vm = new Vue({
     el: '.box',
     data: {
         mes:22
     },
     //局部注册子组件
     components: {
         myCom1,myCom2
     },
 })

  • 集中管理($root)

    把数据存到根实例的data选项,其他组件直接修改或者使用

基本格式:

//根组件内定义
new Vue({
  data:{a:1}
})

//子组件内部使用
this // 子组件本身
this.$root // vm 根实例
this.xx //组件内部数据
this.$root.a //根实例数据

实例代码:

//定义子组件1  
 let myCom1 = {
     template: `
     <input type="button" value="点击触发1" @click="add"/>      
    `,
    data(){
        return {
            mes:'我是子组件1数据'
        }
    },
    methods:{
        add(){
            //修改根组件数据
             this.$root.mes = this.mes;
        }
    }

 }

 //子组件2
 let myCom2 = {
     template:`
     <input type="button" value="点击触发2" @click="add"/>  
     `,
     data:function(){
         return {
             mes2:'我是子组件2数据'
         }
     },
     methods:{
        add(){
            //修改根组件数据
             this.$root.mes = this.mes2;
        }
    }
 }
 
 let vm = new Vue({
     el: '.box',
     data: {
         mes:'我是根组件数据',
     },
     //局部注册子组件
     components: {
         myCom1,myCom2
     },
 })

8、动态组件

有的时候,在不同组件之间进行动态切换是非常有用的。即页面的某个位置要显示的组件是不确定的,是会变化的。

<component  :is="组件名变量">

实例代码:选项卡

<div id="box">
        <button @click="fn(0)">娱乐新闻</button>
        <button @click="fn(1)">体育新闻</button>
        <button @click="fn(2)">天文新闻</button>
        <div>
            <component :is="currCom"></component>
        </div>
</div>
//选项卡功能
let news1 = {
    template: "<div>娱乐新闻</div>"
}
let news2 = {
    template: "<div>八卦新闻</div>"
}
let news3 = {
    template: "<div>体育新闻</div>"
}

new Vue({
    el: "#box",
    data: {
        currCom: "news1",
        coms: ["news1", "news2", "news3"]
    },
    methods: {
        fn(index) {
            this.currCom = this.coms[index];
        }
    },
    components: {
        news1, news2, news3
    }
});

花了一早上才写完,心塞!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值