自定义组件实现v-model

在项目中需要做一个标题展开框,点击展开才会显示下面的内容。因为多个地方都需要这样的可展开标题,所以做了一个自定义组件。组件需要一个双向绑定的值,控制展开/缩放。于是就思考,父向子可以用props传值,子传父用事件触发,也这样实现了。后来了解到v-model其实就是数据绑定和事件触发的语法糖,和我之前实现的类似,只不过我自己的实现是自己定义个事件名,而v-model的事件是input事件。于是改写成了v-model,使得子组件被复用起来更简单。

文章目录

    • 1. 实现效果
    • 2. 代码
    • 3. 分析
    • 4. v-model语法糖
    • 5. 扩展(在render函数中的使用)

1. 实现效果

在这里插入图片描述
点击展开时
自定义组件实现v-model_第1张图片
title组件控制下面表格的显示/隐藏

2. 代码

父组件中参考如下:(用pug写的)

div.hour-forecast-table
    TitleCollapse.title-collapse-wrap(:title="'时段_'+title" v-model="collapse")
    div.table-wrap(v-if="collapse")
      Table(:columns="columns" :data="tableData" border max-height="500")

TitleCollapse子组件如下

<template lang="pug">
div.title-collapse
  div.title-header
    div.title-text {{title}}
    div.collapse-button(@click="clickCollapseButton")
      span.collapse-text {{collapseText}}
      Icon(:type="value?'ios-arrow-up':'ios-arrow-down'")
</template>

<script>
    export default {
      name: "TitleCollapse",
      props: {
        title: {
          type: String
        },
        value: {
          type: Boolean
        }
      },
      data () {
        return {
          collapse: this.value
        }
      },
      watch: {
        value (val) {
          this.collapse = val
        },
        collapse (val) {
          this.$emit('input', val)
        }
      },
      computed: {
        collapseText () {
          if (this.collapse) {
            return '收起'
          } else {
            return '展开'
          }
        },
      },
      methods : {
        clickCollapseButton () {
          this.collapse = !this.collapse
        },
      }
    }
</script>

<style scoped lang="scss">
.title-collapse {
  .title-header{
    display: -webkit-flex;
    justify-content: left;
    position: relative;
    .title-text{
      font-size: 16px;
    }
    .collapse-button{
      font-size: 14px;
      color: #1890FF;
      position: absolute;
      right: 0;
    }
  }
}
</style>

3. 分析

实现双向绑定有两步,第一步是父传给子,通过props传,v-model在props中是value。然后按按钮子组件里的值变化,不可以直接改props里的值,所以要在data里声明一个新值collapse当中介,赋初始值为value,并且watch value,在value改变时改变collapse。按按钮改变collapse的值,在watch里监控collapse,collapse改变时向外触发input事件,这样就实现了双向绑定。
总结来看,v-model其实就是v-bind和v-on的语法糖。

4. v-model语法糖

其实在vue中,在使用v-model绑定数据之后,既绑定了数据,又添加了事件监听,这个事件就是input事件
例如官方文档给出:
自定义组件实现v-model_第2张图片
可以看出v-modal就是v-bind和v-on的语法糖,自定义组件的实现就是接受了一个value值,在有新的value(比如点击展开/收起按钮的时候新值改变)去触发input事件把新值传出来。这样之前的代码就很好理解了

5. 扩展(在render函数中的使用)

使用了iview的table,但在table中要自定义一个checkbox选择框,所以需要用到render函数。选择框希望可以双向绑定,发现render函数里需要自己去实现v-model,这就又涉及到了v-model的语法糖,实现如下

            tableColumns: [
              {
                title: '序号',
                key: 'id'
              },
              {
                title: '分类',
                key: 'targetTypeCategory'
              },
              {
                title: '是否参与计算',
                key: 'factor',
                render:(h, params) => {
                  return  h('Checkbox', {
                      style:{
                        margin:"15px 0px 15px 15px",
                        display:"block"
                      },
                      props: {
                        value: this.tableData[params.index].factor,
                      },
                      on: {
                        input: (event) => {
                          this.$set(this.tableData[params.index], 'factor', event);
                        },
                      },
                    })
                }
              },
              {
                title: '是否进行公式替换',
                key: 'funReplace',
                render:(h, params) => {
                  return h('Checkbox', {
                    style:{
                      margin:"15px 0px 15px 15px",
                      display:"block"
                    },
                    props: {
                      value: this.tableData[params.index].funReplace,
                    },
                    on: {
                      input: (event) => {
                        this.$set(this.tableData[params.index], 'funReplace', event);
                      },
                    },
                  })
                }
              }
              }
            ]

你可能感兴趣的:(vue,项目开发)