还是上面那句话,可复用组件应尽量减少对外部条件的依赖。没有特别需求且单个组件不至于过重的的前提下,不要把一个有独立功能的组件拆分成若干个小组件。
<table-wrapper> <table-header slot="header" :headers="exampleHeader"></table-header> <table-body slot="body" :body-content="exampleContents"></table-body> </table-wrapper>
TableHeader 组件和 TableBody 组件依赖当前的上下文,即 TableWrapper 组件嵌套的环境下。你可以有更好的解决办法:
<xl-table :headers="exampleHeader" :body-content="exampleContents"></xl-table>
上下文无关原则能够降低组件使用的门槛。
数据扁平化
定义组件接口时,尽量不要将整个对象作为一个 prop 传进来。
<!-- 反例 --> <card :item="{ title: item.name, description: item.desc, poster: item.img }></card>
每个 prop 应该是一个简单类型的数据。这样做有下列几点好处:
组件接口清晰
props 校验方便
当服务端返回的对象中的 key 名称与组件接口不一样时,不需要重新构造一个对象
<card :title="item.name" :description="item.desc" :poster="item.img"> </card>
扁平化的 props 能让我们更直观地理解组件的接口。
使用自定义事件实现数据的双向绑定
有时候,对于一个状态,需要同时从组件内部和组件外部去改变它。
例如,模态框的显示和隐藏,父组件可以初始化模态框的显示,模态框组件内部的关闭按钮可以让其隐藏。一个好的办法是,使用自定义事件改变父组件中的值:
<modal :show="show" @showchange="show = argument[0]"></modal>
<!-- Modal.vue --> <template> <div v-show="show"> <h3>标题</h3> <p>内容</p> <a href="javascript:;" @click="close">关闭</a> </div> </template> <script> export default { props: { show: String }, methods: { close () { this.$emit('input', false) } } } </script>
用户点击关闭按钮时,Modal 组件发送一个 input 自定义事件给父组件。父组件监听到 input 事件时,把 show 设置为事件回调的第一个参数。
特别地,当状态名称为 value,事件名称为 input 时,可以使用 v-model 指令语法糖:
<modal :value="show" @input="show = argument[0]"></modal>
等价于
<modal v-model="show"></model>
要让组件的 v-model 生效,它必须:
接受一个 value 属性
在有新的 value 时触发 input 事件
注意:由于每个组件的 input 事件只能用来对一个数据进行双向绑定,所以当存在多个需要向上同步的数据时,请不要使用 v-model,请使用多个自定义事件,并在父组件中同步新的值。
<modal :show="show" @showchange="show = argument[0]" :content="content" @contentchange="content = argument[0]"> </model>
使用自定义 watcher 优化 DOM 操作
在开发中,有些逻辑无法使用数据绑定,无法避免需要对 DOM 的操作。例如,视频的播放需要同步 Video 对象的播放操作及组件内的播放状态。可以使用自定义 watcher 来优化 DOM 的操作。
<!-- MyVideo.vue --> <template> <div> <video ref="video" src="https://www.jb51.net/article/src"></video> <a href="javascript:;" @click="togglePlay">{{ playing ? '暂停' : '播放' }}</a> </div> </template> <script> export default { props: { src: String // 播放地址 }, data () { return { playing: false // 是否正在播放 } }, watch: { // 播放状态变化时,执行对应操作 playing (val) { let video = this.$refs.video if (val) { video.play(); } else { video.pause(); } } }, method: { // 切换播放状态 togglePlay () { this.playing = !this.playing } } } </script>
示例中,自定义 watcher 在监听到 playing 状态变化时,会执行播放或暂停操作。遇到对视频播放状态的处理时,只需要关注 playing 状态即可。
项目骨架