在编写组件时,记住是否要复用组件有好处。一次性组件跟其它组件紧密耦合不要紧,可是可复用组件应当定义一个清晰的公开接口。--官方文档css
在看这篇文章前,强烈推荐你们看这篇文章Thinking in Vue之一:组件扩展的尝试
项目Github
演示Demohtml
就像官方文档所说的同样,本身一般写的都是一次性的组件,没有往可复用组件方面去思考。vue
对vue的render函数有所了解,对自定义事件有所了解,没有也不要紧。
render函数 文档
自定义事件 文档git
// 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中使用它:github
<BaseButton>Base</BaseButton>
渲染的结果:浏览器
组件树的结果:ide
此时这个BaseButton就是一个可用的组件了,那么如何复用它呢?这篇文章也有提到Thinking in Vue之一:组件扩展的尝试
根据这篇文章提供的思路,建立一个TypeButton组件而后将BaseButton组件给包裹起来,这样就不须要为每个Button组件重新复制粘贴那些默认样式了。函数
// 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中挂载上去
渲染的结果:ui
DOM节点结果:this
组件树结果:
固然,此时能够说完成了复用,可是并不完美。咱们能够作一个工厂方法同样的组件复用。此时咱们就会用到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组件。