Vue之组件
组件
1.组件(component)是Vue最强大的功能,组件可以扩展HTML元素,封装可重用的代码,在较高层面上,组件是自定义元素,Vue编译器为它添加新特性,在有些情况下,组件也可以是原生的HTML形式,以is特性扩展。
2.使用组件
1.全局组件:Vue.component
<div id="box"> <hello></hello> </div>
//vue组件component。全局组件,可以在多个定义的Vue根元素里面使用,状态没办法在状态中进行共享,name不能在VM的根元素data中定义,data如果要在组件中使用就必须设置成一个function而不是对象 Vue.component('hello',{ template:` <div> 我是hello全局组件,实现data里面的对象通信,data中的name为{{name}} <hellochild></hellochild> </div>`, data:function(){//data必须要设置成方法才能让状态共享 return{ name:'xiaowang', } }, //也可以写子组件 components:{ 'hellochild':{ template:``, } }, methods: { }, })
2.局部组件
<div id="box"> <hello3></hello3> </div>
var vm = new Vue({ el:'#box', data:{ myText:'', }, components:{ 'hello3':{ template:` <div>我是属性的hello3局部组件,</div>`, }, } });
3.data必须是函数
4.注意点
1.自定义组件需要有一个root element
2.父子组件的data无法共享,
3.组件有data,methods,computed。。但是data必须是一个函数
5.组件意味着协同工作,通常父子组件会是这样的关系,组件A在它的模板中使用了组件B,它们之间必然需要通信,父组件要给子组件传递数据,子组件需要将组件内发生的事情告知父组件。然后,在一个良好定义的接口中将父子组件解耦是很重要的,这保证了各个组件在相对独立的空间书写和理解,也大幅度的提高了组件的可维护性和重用性。在Vue中,父子组件通信可总结为,props down,events up,父组件通过props给子组件传递信息,而子组件则通过events给父组件报告数据
3.prop
<div id="box"> <!-- 设置子组件的属性,通过使用props来进行通信 --> <hello2 name="sarah" age="100"></hello2> </div>
var vm = new Vue({ el:'#box', data:{ name:'xiaolv', }, //局部组件,只能在vm #box这个根元素中使用 components:{ 'hello2':{ template:` <div> 我是属性name为{{name}},age为{{age}}的hello2局部组件,用来实现子组件的属性绑定 </div>`, //组件中属性的通信, props:['name','age'], }, }, });
1.prop传递数据,props:["name"]
2.单向数据流,防止组件无意间改变了父组件的状态。
注意:不要在子组件修改prop-单向数据流
3.prop验证 props:{name:Number},我们可以为组件props指定验证规格,如果传入的数据不符合规格,Vue会发出警告,当组件给其他人使用时,这很有用,要指定验证规格,要使用对象形式,而不能使用字符串数组。
props:{
name:String,
age:Number
},
4.自定义事件
1.使用$on(eventname)监听事件
使用$emit(eventname)触发事件
<div id="box"> <!-- 设置父组件监控子组件的方法名event --> <hello @event="handleParentClick"></hello> </div>
Vue.component('hello',{
//在template这个key值可以写HTML的DOM结构,用es6的字符串模板,chrome是支持的,如果不支持的浏览器可以用wellpack
template:`
<div>
我是hello全局组件,实现data里面的对象通信,data中的name为{{name}}
<ul>实现data对象中dataList的属性遍历
<li v-for="data in dataList">{{data}}</li>
<li><button @click="handleclick">用event和emit实现父子组件通信click事件</button></li>
</ul>
</div>`,
data:function(){//必须要设置成方法才能让状态共享
return{
name:'xiaowang',
dataList:['aaa','bbb','ccc'],
}
},
methods: {
handleclick(){
//子组件事件分发,使用父组件方法中的函数名event,因为将来一个子组件可能会有很多的触发事件,所以要对应每一个方法名,也可以给父元素的方法传送数据,逗号后面跟上需要传送的数据。父元素中加入形参data就行
this.$emit('event',this.name);
},
},
})
var vm = new Vue({
el:'#box',
data:{
name:'xiaolv',
},
methods:{
//父组件监听子组件hello的事件
handleParentClick(data){
console.log('parentClick' + data);
},
},
});
2.v-model父子通信
props:["value"];
this.$emit("input","***");
<input v-model="something"/> 实际上等于下面语法糖 <input v-bind:value="something" v-on:input="something = $event.target.value"/>
<!-- 用v-model父子通信, --> <div id="box2"> <!-- 用v-model来实现父子组件的通信,将会更简单,在父元素data中添加myText的字符串属性 --> <p>用v-model来实现父子组件的通信,我是父组件data中的myText值{{myText}},可以接收来自子组件hello3的input中的值{{myText}}</p> <hello3 v-model="myText"></hello3> </div>
//用来实现v-model父组件和子组件的通信 var vm2 = new Vue({ el:'#box2', data:{ myText:'', }, components:{ //用来实现v-model的父子组件通信 'hello3':{ template:` <div> //我是属性的hello3局部组件,用来实现v-model父组件和子组件的通信 <input type="text" :value="value" @input="handleInput"/> </div>`, //如果要实现父子组件通信,这里的值必须用value,它会将父组件中data中的myText值传递给子组件的中的input中:value的值,如果要实现子组件对父组件的传递信息则要添加@input="handleInput",并且在子组件的methods方法中添加handleInput方法this.$emit('input',ev.target.value); props:['value'], methods:{ handleInput(ev){ //因为监听事件v-model实际会分成v-on:input this.$emit('input',ev.target.value); } }, }, } });
3.非父子通信
1.中央事件总线 bus = new Vue();
注意:mounted生命周期中进行监听
<!-- 非父子通信,可以使用子传父,然后父传子的方式,也可以使用中央bus的方法,其实就是创建一个空Vue然后监听事件。 --> <div id="box3"> <zujian1></zujian1> <zujian2></zujian2> </div>
//非父子通信,用中央bus来实现,这种方式只适用于简单的场景,如果是大型的网站的项目,我们必须要用Vuex做状态管理。 var bus = new Vue();//声明一个bus总线来调度通信 var vm3 = new Vue({ el:'#box3', data:{ name:'ak47', }, components:{ zujian1:{ data:function(){ return{ name:'我是组件1的data中的name', } }, template:`<div>我是组件1 <button @click="handleClick">实现非父子通信</button> </div>`, methods:{ handleClick(){ //用一个Vue的空组件来发送一个xiaoming的事件 bus.$emit('xiaoming',this.name) }, }, }, zujian2:{ template:`<div>我是组件2</div>`, //只要是组件都会有生命周期,当DOM组件渲染挂载完成的时候,这个声明周期就会自动的执行,声明周期的钩子函数, mounted() { console.log('来自钩子函数'); //用Vue的空控件bus来监听$on 接收 xiaoming这个事件 bus.$on('xiaoming',function(value){ console.log(value); }); }, }, }, });
2.vuex状态管理
非父子通信,用中央bus来实现,这种方式只适用于简单的场景,如果是大型的网站的项目,我们必须要用Vuex做状态管理
5.插槽分发内容
为了让组件可以组合,我们需要一种方式来混合父组件的内容与子组件自己的模板,这个过程就叫做内容分发。(或transclution,如果你熟悉angular)。Vue实现了一个内容分发API,参照了当前Web组件规范草案,使用特殊的<slot>元素作为原始内容的插槽
1.单个slot
2.具名slot
<!-- 实现内容分发,直接在组件中写如button,这样直接在元素中写指令的时候,就不存在什么父子组件传递信息的问题,直接在父组件调用methods方法就可以了。 --> <div id="box4"> <zujian> <button @click="handleClick" slot="leftBtn">leftbutton</button> <div>我在中间</div> <button @click="handleClick" slot="rightBtn">rightbutton</button> </zujian> </div>
/* 插槽分发内容 */ var vm4 = new Vue({ el:'#box4', data:{ }, methods:{ handleClick(){ console.log(11); } }, components:{ zujian:{ template:`<div>我是组件 <slot name="leftBtn"></slot>//具名slot <slot></slot>//单个slot <slot name="rightBtn"></slot> </div>`,//在字符串模板中写如slot插槽,在html中插入zujian这个元素会自动插入这个位置,可以给插槽具名,让对应的元素插入这个插槽 }, } });
6.动态组件
1.component元素,动态的绑定多个组件到它的is的属性
2.keep-alive,保留状态,避免被重复渲染。
html,body{width: 100%;height: 100%;} #box5{overflow-x: hidden;} #box5 ul{display: flex;width: 100%;height: 40px;position: fixed;left: 0;bottom: 0;} #box5 ul li{flex: 1;height: 40px;line-height: 40px;background: #333;list-style: none;} /* css动画效果animate,确保html元素transition的name和类开始名字一致bounce */ .bounce-enter-active{animation: bounce-in .5s;} .bounce-leave-active{animation: bounce-in .5s reverse;} @keyframes bounce-in { 0%{transform: scale(0);} 50%{transform: scale(1.5);} 100%{transform: scale(1);} }
<!-- 动态组件,使用component组件,在components的组件中定义好每个组件和名字,用v-bind它的属性is的值来自定义的来动态的实现切换页面,在footer的点击事件中传入相应的值是和子组件的名字一一对应的。 每次点击都会重新创建页面和销毁前一个页面,但是如果页面内容中有需要保持内容的信息存在,比如在home页中有一个输入框文本按钮,需要用到keep-alive来实现内容的缓存--> <div id="box5"> <keep-alive> <!-- 可以呈现的组件添加一个过渡效果,这里实现的是多组件的过渡效果 --> <transition name="bounce" mode="out-in"> <component :is="current"></component> </transition> </keep-alive> <footer> <ul> <li @click="handleClick('home')">home</li> <li @click="handleClick('list')">list</li> <li @click="handleClick('shopcart')">shopcart</li> </ul> </footer> </div>
/* 动态组件 */ var vm5 = new Vue({ el:'#box5', data:{ current:'home', }, components:{ home:{ template:`<div>我是home<input type="text"/> </div>`, }, list:{ template:`<div>我是list</div>`, }, shopcart:{ template:`<div>我是shopcart</div>`, }, }, methods:{ handleClick(data){ this.current = data; }, }, })

浙公网安备 33010602011771号