高阶函数
高阶函数在函数式编程中经常出现,就是你原本有一个函数,你可以通过另外一个函数进行包裹,这个新的函数既具有原来函数的功能,又可以添加自己的功能,这种方式称为高阶函数。
我们现在有一个函数组件Avatar
,它接收一个src属性并显示到img标签
<avatar url="/path/to/image.png"></avatar>
上面是它的使用方式,可以看到我们使用的时候需要传递完整的图片地址,这很不方便。现在希望我们只需要传递用户名,就可以自动根据用户名查询到url。
这里我们使用高阶组件,创建一个函数组件withAvatarURL
,这个函数接收内部组件,本例中内部组件就是Avatar
,我们在外部函数中完成根据用户名查询头像地址,在内部函数组件Avatar
中实现渲染头像。
fetchURL
只是用来模拟网络请求的,我们设置0.5s后返回数据,Avatar
是基础组件,只负责把传入src属性显示到一个图片标签。
<script src="../node_modules/vue/dist/vue.js"></script>
<div id="app">
<smart-avatar username="vuejs"></smart-avatar>
</div>
<script>
// mock API
function fetchURL (username, cb) {
setTimeout(() => {
// hard coded, bonus: exercise: make it fetch from gravatar!
cb('https://avatars3.githubusercontent.com/u/6128107?v=4&s=200')
}, 500)
}
const Avatar = {
props: ['src'],
template: `<img :src="src">`
}
function withAvatarURL (InnerComponent) {
return {
props: {
username: String
},
data () {
return {
url: 'http://via.placeholder.com/200x200'
}
},
created () {
fetchURL(this.username, (url) => { this.url = url })
},
render (h) {
return h(InnerComponent, { props: { src: this.url } })
}
}
}
const SmartAvatar = withAvatarURL(Avatar)
new Vue({
el: '#app',
components: { SmartAvatar }
})
</script>
this.$attrs
用于获取组件所有属性,这是2.4之后才支持的功能,下面代码我们把高阶组件设置的属性传递给原始组件。
render (h) {
return h(InnerComponent, {
attrs: this.$attrs, // 2.4 only
props: {
src: this.url || 'http://via.placeholder.com/200x200'
}
})
}
高阶函数和mixin的选择
上面的demo同样可以使用mixin实现,但是高阶组件有如下优势
- 重用性。mixin对原组件有侵入性,会导致原组件可重用性降低。而高阶组件不会,高阶组件对原组件只是一个调用关系,并没有修改原来组件任何内容。
- 可测试性。因为高阶组件只是一个嵌套关系,在组件测试的时候,可以单独的测试原始组件和高阶组件。
- 层级问题。高阶组件也有他的弊端,如果你高阶组件嵌套层级太深,会导致出错的时候调试困难的问题,所以到底使用高阶组件和minxin需要看实际场景。