前言
无论在任何的语言或框架中, 我们都提倡代码的复用性. 对于 vue 来说也是如此, 相同的代码逻辑会被封装成组件, 除了复用之外, 更重要的是统一管理提高开发效率. 我真就接手过一个项目, 多个页面都会用到的列表, 没有去封装列表组件, 只要有一点改动, 每个页面都得加上. 很肯定的说, 没有用组件化开发的 Vue 项目是没有灵魂的. 所以如何封装一个优雅且复用性高的组件成为我们必需的技能.
Tab 自定义组件
首先来看一个 Tab 组件的实现, 看看它存在什么问题, 哪里可以改进?
效果
组件
- <template>
- <div class="tabs">
- <div
- class="tab-item"
- :class="{'tab--active':item===activeName}"
- v-for="(item,index) in tabs"
- :key="index"
- @click="tabChange(item)">
- {{item}}
- </div>
- </div>
- </template>
- <script>
- export default {
- props:{
- tabs:{
- type: Array,
- default: ()=> []
- },
- activeName:{
- type: String,
- default: ''
- }
- },
- methods:{
- tabChange(item){
- this.$emit('tabChange',item)
- }
- },
- }
- </script>
使用
- <template>
- <div>
- <Tabs :tabs="tabs" :activeName="activeName" @tabChange="tabChange" />
- </div>
- </template>
- <script>
- import Tabs from '../components/Tabs'
- export default {
- components:{
- Tabs
- },
- data(){
- return{
- tabs:['黄金体验','败者食尘','绯红之王','白金之星','波纹疾走'],
- activeName: '黄金体验'
- }
- },
- methods:{
- tabChange(item){
- this.activeName = item
- }
- },
- }
- </script>
分析
这个组件最大的问题就是, activeName 需要使用者额外通过事件来手动更新, 假如有另一个使用者接手, 在不知道这种情况下使用, 会出现 tab 没有切换的情况. 然后要去看组件内部实现, 再回来修改代码, 很显然这样的组件是失败的. 本着所有的脏活累活都由组件实现的原则, 理想的状态应该是使用者不需要管理 activeName, 而是由组件内部去更新.
如何改进
修改 prop?
可能有人会想到, 既然要由内部管理, 那在组件内部修改 prop 的值是不是就可以了? 来看下这样的做法是否可行
修改组件 tabChange 方法, 在点击时更新 prop 的值
- tabChange(item){
- this.activeName = item
- this.$emit('tabChange',item)
- }
使用时, 控制台抛出警告
由于 prop 是单向数据流, 父级 prop 的更新会向下流动到子组件中, 相反的在子组件内部直接更新状态, 会导致数据的流向不明确. 例如, 在父组件中有多个子组件依赖同一个属性, 其中一个子组件更新该属性, 会引发其余子组件发生改变, 发生问题时不容易被找到, 因此 Vue 不推荐我们这样做. 另外, 在父组件发生更新时, 子组件的 prop 会被刷新为最新的值.
单向数据流:
正解: model 选项
改进组件
组件 model 选项
允许一个自定义组件在使用 v-model 时定制 prop 和 event. 默认情况下, 一个组件上的 v-model 会把 value 用作 prop 且把 input 用作 event, 但是一些输入类型比如单选框和复选框按钮可能想使用 value prop 来达到不同的目的. 使用 model 选项可以回避这些情况产生的冲突.
model: https://cn.vuejs.org/v2/api/#model
在 model 选项里, 我们可以绑定一个属性, 并为其添加事件, 只需在调用方法时传入值即可更新属性.
- <script>
- export default {
- model:{
- prop: 'activeName',
- event: 'update'
- },
- props:{
- tabs:{
- type: Array,
- default: ()=> []
- },
- activeName:{
- type: String,
- default: ''
- }
- },
- methods:{
- tabChange(item){
- this.$emit('update',item) // 这里更新 activeName
- this.$emit('tabChange',item)
- }
- }
- }
- </script>
注意你仍然需要在组件的 props 选项里声明 prop.
使用
使用组件双向绑定后, 属性在组件内部更新时, 父组件的 activeName 也会随之更新, 这样组件使用起来显得更优雅
<Tabs :tabs="tabs" v-model="activeName" />
来源: http://www.bubuko.com/infodetail-3373703.html