赞
踩
假设想在表格便签内使用一个组件作为行,直接使用组件是不行的,原因在于tbody内只能放tr便签,否则浏览器就会将tbody内的标签解析到外面,从而导致结构的错乱。
为了解决这个问题,我们可以使用is特性,将is特性设置为组件名称,这样既保证了tbody和tr之间的层级关系,也使用了组件。
一个组件的 data 选项必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝。
在Vue中,可以为DOM节点设置ref属性,从而可以通过实例名(实例引用this).$ref.属性名
来访问DOM节点。
Prop 是你可以在组件上注册的一些自定义 attribute。当一个值传递给一个 prop attribute 的时候,它就变成了那个组件实例的一个 property。
<body> <div id="root"> <counter :count="count1"></counter> <counter :count="count2"></counter> </div> <script> var counter = { props:['count'], data:function(){ // 子组件不能直接修改父组件传过来的值,因此需要将附件传过来的值复制一份 return { number:this.count } }, template:'<div @click="handleClick">{{number}}</div>', methods:{ handleClick:function(){ this.number ++; } } } var vm = new Vue({ el:"#root", data:{ count1:0, count2:1 }, components:{ counter:counter } }) </script> </body>
<body> <div id="root"> <counter :count="count1" @change="handleChange"></counter> <counter :count="count2" @change="handleChange"></counter> <div>{{total}}</div> </div> <script> var counter = { props:['count'], data:function(){ // 子组件不能直接修改父组件传过来的值,因此需要将附件传过来的值复制一份 return { number:this.count } }, template:'<div @click="handleClick">{{number}}</div>', methods:{ handleClick:function(){ this.number ++; this.$emit('change',1); } } } var vm = new Vue({ el:"#root", data:{ count1:0, count2:1, total:1 }, components:{ counter:counter }, methods:{ handleChange:function(step){ this.total += step; } } }) </script> </body> </html>
通过在子组件中触发自定义事件$emit('事件名',参数列表...)
,父组件可以监听到这个事件。
== 一句话总结 ==:父组件通过props给子组件传值,子组件通过事件触发给父组件传值。
假设我们在子组件中限定,父组件必须传一个字符串给子组件。
<body> <div id="root"> <child :content="content"></child> </div> <script> // 全局子组件 Vue.component('child',{ props:{ content:String }, template:'<div>{{content}}</div>' }) var vm = new Vue({ el:'#root', data:{ content:123 } }) </script> </body>
当父组件传了一个数字后,Vue会警告如下:
当允许多个类型时,使用数组语法:
props:{
content:[Number,String],//数组语法表示,该参数既可以接受Number类型也可以接受String类型
}
更详细的参数校验:
<body> <div id="root"> <child :content="content"></child> </div> <script> Vue.component('child',{ props:{ content:{ type:String, required:true, default:'default value', // 校验长度 validator:function(value){ return (value.length>5) } } }, template:'<div>{{content}}</div>' }) var vm = new Vue({ el:'#root', data:{ content:"Hello,world" } }) </script> </body>
若父组件向子组件传递了一个参数content
,但子组件并未接受(即子组件的props
属性内不包含content
)时,父组件的content
特性成为非props特性,当然在子组件中无法获取该特性的值。非props属性会显示在DOM属性中,而props属性不会。
当我们想给子组件上绑定原生事件,例如click,我们首先可能会想到这么做:
<body> <div id="root"> <child @click="handleClick"></child> </div> <script> Vue.component('child',{ template:'<div>Hello,world</div>' }) var vm = new Vue({ el:'#root', methods:{ handleClick:function(){ alert("click"); } } }) </script> </body>
这样做的结果是:无论点击多少次,都无法触发click事件处理函数。
原因在于,直接给子组件的DOM元素上绑定的事件全部为自定义事件,click也被识别为自定义事件,我们没有关于click的触发定义,因此不会触发handleClick。
那么怎么给子组件绑定原生事件呢?
我们需要把事件绑定到子组件的模板上,并且相关的事件处理函数也应该定义在子组件内。
<body> <div id="root"> <child></child> </div> <script> Vue.component('child',{ template:'<div @click="handleClick">Hello,world</div>', methods:{ handleClick:function(){ alert("click"); } } }) var vm = new Vue({ el:'#root', }) </script> </body>
要想触发原生事件,可以在子组件的原生事件处理函数中使用$emit('click')
<body> <div id="root"> <child @click="handleClick"></child> </div> <script> Vue.component('child',{ template:'<div @click="handleChildClick">Hello,world</div>', methods:{ handleChildClick:function(){ alert("click child"); this.$emit('click'); } } }) var vm = new Vue({ el:'#root', methods:{ handleClick:function(){ alert("click"); } } }) </script> </body>
以上的做法过于繁琐,Vuet提供了组件修饰符:
<child @click.native="handleClick"></child>
@事件名.native
表明该事件是一个原生事件
方式一:使用Vuex
方式二:使用发布订阅模式(总线机制/BUS/发布订阅模/观察者模式)
下面介绍方式二:
Vue.prototype.bus = new Vue();
在Vue类的prototype上挂了一个bus属性,该属性是Vue类型的对象。因为是在原型上,Vue类的每个实例都会有一个bus属性,而且指向同一个Vue实例。
我们可以通过bus属性来统一地触发事件并携带参数。
<body> <div id="root"> <child content="dell"></child> <child content="lee"></child> </div> <script> Vue.prototype.bus = new Vue(); // 在Vue类的prototype上挂了一个bus属性,该属性是Vue类型的对象 // 因为是在原型上,Vue类的每个实例都会有一个bus属性,而且指向同一个Vue实例 Vue.component('child',{ data:function(){ return { selfContent:this.content } }, props:{ content:String }, template:'<div @click="handleClick">{{selfContent}}</div>', methods:{ handleClick:function(){ this.bus.$emit('change',this.selfContent); } }, mounted:function(){ var that = this; this.bus.$on('change',function(msg){ that.selfContent = msg; }) } }) var vm = new Vue({ el:"#root" }) </script> </body>
使用场景:当子组件中有一部分DOM元素时根据父组件传递过来的值进行渲染的,这时使用props传值会直接将HTML标签渲染在页面上,为了避免这一错误,我们可能需要使用div来包裹,并且使用v-html指令来确保其格式正确,如下:
<body> <div id="root"> <child content='<h2>World</h2>'></child> </div> <script> Vue.component('child',{ props:['content'], template:`<div> <h1>Hello</h1> <div v-html="content"></div> </div>` }) var vm = new Vue({ el:"#root" }) </script> </body>
这样的方式在传递的代码量大的情况下会非常糟糕。
这种情况下,可以使用插槽。
<body> <div id="root"> <child> <h2>World</h2> </child> </div> <script> Vue.component('child',{ props:['content'], template:`<div> <h1>Hello</h1> <slot></slot> </div>` }) var vm = new Vue({ el:"#root" }) </script> </body>
在这个例子中,我们将需要插入到子组件的内容,直接以HTML标签的形式写在了子组件的占位标签内<child></child>
,在子组件的模板中使用<slot></slot>
标签可以接收到自己占位标签内的所有内容。
可以在插槽内指定默认内容,该内容会在父组件未给子组件的插槽传递值时显示,若传递了,则默认隐藏。
<slot>默认内容</slot>
具名插槽
为了在子组件中使用多个插槽,而每个插槽拥有不同的内容,我们需要给插槽起名:在外部,给传进插槽的DOM元素指定slot特性;在内部,给插槽指定name特性,与外部要传入该插槽的slot特性名一致。
<body> <div id="root"> <body-content> <div class='header' slot='header'>header</div> <div class='footer' slot='footer'>footer</div> </body-content> </div> <script> Vue.component('body-content',{ props:['content'], template:`<div> <!-- header部分 --> <slot name='header'></slot> <div class='content'>content</div> <!-- footer部分 --> <slot name='footer'></slot> </div>` }) var vm = new Vue({ el:"#root" }) </script> </body>
子组件在渲染数据时,可以不指定具体的渲染样式,而由父组件来决定该数据渲染成什么样子。
做法:
在子组件的模板中定义插槽:
template:`<div>
<ul>
<slot v-for="item in list" :item=item></slot>
</ul>
</div>`
在父组件对应子组件的位置使用<template>
标签声明作用域插槽,并使用slot-scope
属性接受参数。
作用域插槽必须包裹在template标签内
<template slot-scope="props">
<li>{{props.item}}</li>
</template>
<body> <div id="root"> <child> <template slot-scope="props"> <li>{{props.item}}</li> </template> </child> </div> <script> Vue.component('child',{ data:function(){ return{ list:[1,2,3,4] } }, template:`<div> <ul> <slot v-for="item in list" :item=item></slot> </ul> </div>` }) var vm = new Vue({ el:"#root" }) </script> </body>
如果我们想根据一些条件动态地决定渲染哪些组件,不渲染哪些组件,我们可以这样做:
<body> <div id="app"> <child-one v-if="type === 'child-one'"></child-one> <child-two v-if="type === 'child-two'"></child-two> <button @click="handleSwtich">切换</button> </div> <script> Vue.component('child-one',{ template:'<div>child-one</div>' }) Vue.component('child-two',{ template:'<div>child-two</div>' }) var vm = new Vue({ el:"#app", data:{ type:'child-one' }, methods:{ handleSwtich:function(){ this.type = this.type === 'child-one' ? 'child-two' : 'child-one'; } } }) </script> </body>
以v-if的方式切换组件的渲染与否,每次切换都会有一个组件被销毁,有一个组件被渲染,若每次销毁和新建的组件内容一样,则会非常浪费性能。
为解决这个问题,可以使用v-once
指令,使用了v-once
指令的组件,会在第一次渲染时就被加载进内存,以后使用v-if切换时不会销毁和重新创建,而是直接从内存中加载,这样做能提高性能。
Vue提供了动态组件,可以更好地解决这个问题:
<component :is="type"></component>
<component>
所代表的组件为动态组件,它的is
特性指明它的类型。通过动态地改变is
特性达到动态改变组件类型的目的。
Vue.component('child-one',{
template:'<div v-once>child-one</div>'
})
Vue.component('child-two',{
template:'<div v-once>child-two</div>'
})
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。