写在开头
在编写组件时,记住是否要复用组件有好处。一次性组件跟其它组件紧密耦合没关系,但是可复用组件应当定义一个清晰的公开接口。--官方文档
在看这篇文章前,强烈推荐大家看这篇文章Thinking in Vue之一:组件扩展的尝试
项目Github
演示Demo
思考
就像官方文档所说的一样,自己通常写的都是一次性的组件,没有往可复用组件方面去思考。
前提
对vue的render函数有所了解,对自定义事件有所了解,没有也没关系。
render函数 文档
自定义事件 文档
可复用的Button组件的实现
实现一个基础的Button组件(顺便也将后面的css样式加入进来):
// src/components/BaseButton.vue
<script>
export default{
name: 'base-button',
props: [],
render(createElement) {
return createElement(
'a',
{
class: ['button', 'ripple'],
},
this.$slots.default
)
},
methods: {
}
};
</script>
<style scoped>
/*button默认样式*/
a.button {
display: inline-block;
line-height: 1;
text-align: center;
white-space: nowrap;
cursor: pointer;
user-select: none;
border: none;
border-radius: 2px;
position: relative;
padding: 8px 30px;
margin: 10px 1px;
font-size: 14px;
font-weight: 500;
text-transform: uppercase;
letter-spacing: 0;
will-change: box-shadow, transform;
transition: box-shadow 0.2s cubic-bezier(0.4, 0, 1, 1), background-color 0.2s cubic-bezier(0.4, 0, 0.2, 1), color 0.2s cubic-bezier(0.4, 0, 0.2, 1);
text-decoration: none;
background: transparent;
background-color: #EEEEEE;
color: rgba(0, 0, 0, 0.87);
}
a.button:active {
box-shadow: 0 4px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 10px 0 rgba(0, 0, 0, 0.12), 0 2px 4px -1px rgba(0, 0, 0, 0.2);
}
/*button 扩展样式*/
a.button.default {
background-color: #EEEEEE;
color: rgba(0, 0, 0, 0.87);
}
a.button.primary {
background-color: #009688;
color: rgba(255, 255, 255, 0.84);
}
a.button.success {
background-color: #4caf50;
color: rgba(255, 255, 255, 0.84);
}
a.button.info {
background-color: #03a9f4;
color: rgba(255, 255, 255, 0.84);
}
a.button.warning {
background-color: #ff5722;
color: rgba(255, 255, 255, 0.84);
}
a.button.danger {
background-color: #f44336;
color: rgba(255,255,255, 0.84);
}
.ripple {
position: relative;
overflow: hidden
}
/*button 水波纹点击效果*/
.ripple:after {
content: "";
display: block;
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
pointer-events: none;
background-image: radial-gradient(circle, #000 10%, transparent 10.01%);
background-repeat: no-repeat;
background-position: 50%;
transform: scale(10, 10);
opacity: 0;
transition: transform .5s, opacity 1s
}
.ripple:active:after {
transform: scale(0, 0);
opacity: .2;
transition: 0s
}
</style>
完成了一个Button的基础样式,然后通过render函数来创建一个想过等同于<a class="button"></a>
的template.
在App.vue中使用它:
<BaseButton>Base</BaseButton>
渲染的结果:
组件树的结果:
此时这个BaseButton就是一个可用的组件了,那么如何复用它呢?这篇文章也有提到Thinking in Vue之一:组件扩展的尝试
根据这篇文章提供的思路,创建一个TypeButton组件然后将BaseButton组件给包裹起来,这样就不需要为每一个Button组件从新复制粘贴那些默认样式了。
实现一个基础的TypeButton组件
// src/components/TypeButton.vue
<script>
import Button from './BaseButton.vue'
export default{
name: "type-button",
render(createElement){
return createElement(Button,
{
class:["info"]
},
this.$slots.default
)
},
methods: {
},
components: {}
};
</script>
<style scoped>
</style>
在App.vue中挂载上去
渲染的结果:
DOM节点结果:
组件树结果:
当然,此时可以说完成了复用,但是并不完美。我们可以做一个工厂方法一样的组件复用。此时我们就会用到prop来传递属性。
// 进一步修改src/components/TypeButton.vue
<script>
import Button from './BaseButton.vue'
export default{
name: "type-button",
//将传递过来的type在props中注册,并进行验证
props: {
type: {
validator: function (typeStr) {
if (typeof typeStr === 'string') {
switch (typeStr) {
case 'defaul':
case 'primary':
case 'info':
case 'success':
case 'warning':
case 'danger':
return true;
default:
return false;
}
} else return false;
}
}
},
render(createElement){
return createElement(Button,
{
class: [this.type],
on:{
click:this.click
}
},
this.$slots.default
)
},
methods: {
},
components: {}
};
</script>
<style scoped>
</style>
此时我们就可以这样使用组件了
<TypeButton type="defaul">defaul</TypeButton>
<TypeButton type="primary">primary</TypeButton>
<TypeButton type="info">info</TypeButton>
<TypeButton type="success">success</TypeButton>
<TypeButton type="warning">warning</TypeButton>
<TypeButton type="danger">danger</TypeButton>
就很完美了有木有!!!!
实现自定义的点击事件(最后一部分)
此时如果你想这样来实现点击事件:<TypeButton @click="defualt" type="defaul">defaul</TypeButton>
,你会发现是不可行的。这是就需要自定义事件。
// App.vue的methods
methods: {
defaul(){
console.log('defaul')
}
}
// TypeButton.vue的render函数
<script>
import Button from './BaseButton.vue'
export default{
render(createElement){
return createElement(Button,
{
class: [this.type],
//添加on对象
on:{
click:this.click
}
},
this.$slots.default
)
},
//添加
methods: {
click(){
//触发组件中定义的click
this.$emit('click')
}
},
};
</script>
// BaseButton.vue
export default{
name: 'base-button',
props: [],
render(createElement) {
return createElement(
'a',
{
class: ['button', 'ripple'],
on: {
click: this.click
}
},
this.$slots.default
)
},
methods: {
click(){
this.$emit('click')
}
}
};
此时在组件中使用@click<TypeButton @click="defualt" type="defaul">defaul</TypeButton>
就可以被触发。
也就实现了一个Material Design风格,水波纹点击效果的Button组件。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。