咔叽游戏

 找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 253|回复: 0

[JavaScript] Vue 如何使用props、emit实现自定义双向绑定的实现

[复制链接]
  • TA的每日心情
    无聊
    2019-6-2 14:11
  • 签到天数: 4 天

    [LV.2]圆转纯熟

    发表于 2020-7-6 20:46:52 | 显示全部楼层 |阅读模式
    下面我将使用Vue自带的属性实现简单的双向绑定。
    下面的例子就是利用了父组件传给子组件(在子组件定义props属性,在父组件的子组件上绑定属性),子组件传给父组件(在子组件使用$emit()属性定义一个触发方法,在父组件上的子组件监听这个事件)。

    import Vue from 'vueEsm'

    var Com = {
      name:'Com',
      props:['val'],
      template:`<input type='text' @input='handleInput'/>`,
      methods: {
        handleInput(e){
          this.$emit("input",e.target.value);
        }
      },
    }

    new Vue({
       el:'#app',
       data() {
         return {
           value:''
         }
       },
       components:{
        Com
       },
       template:`
       <div>
       <Com @input='post' :val='value'></Com>
       </div>
       `,
       methods:{
        post(data){
          this.value=data;
        }
       }
    })上面这个例子,在input标签上每次输入时触发原生事件input,在这个事件上绑定了一个handleInput方法,事件每次触发都会执行方法里的$emit属性。该属性里面第一个参数可以定义一个事件名,第二个参数可以传一个参数。这里我们把每次输入的值e.target.value传进去。在父组件的子组件上监听这个事件,定义一个post方法,方法的参数就是传入的数据。然后我们在父组件的data属性里定义一个存储值的变量value。将刚才传入的参数赋给这个变量value。最后在父组件的子组件上绑定一个自定义属性,比如val。将value传给val。在子组件定义一个props属性接受这个val。
    这个例子对于理解父组件与子组件传值特别重要。
    下方举例说明了我的一个自定义mySelect的实现过程:

    <template>
    <div class="select">
      <div class="input" @click="collapse=!collapse">
       <span v-if="currentValue">{{currentLabel||currentValue}}</span>
       <span v-else class="placeholder">{{placeholder}}</span>

       <span :class="collapse?'arrow-down':'arrow-up'"></span>
      </div>

      <div class="option-list" v-show="!collapse">
       <div class="option-item" v-for="item in data" :key="item.id" @click="chooseItem(item)">{{item[itemLabel?itemLabel:'name']}}</div>
      </div>
    </div>
    </template>

    <script>
    export default {
      name: "mySelect",
      props: [
       'value',
       'placeholder',
       'data',
       'itemLabel',
       'itemValue'
      ],
      data() {
       return {
        collapse: true,
        currentValue: '',
        currentLabel: '',
       }
      },
      watch: {
       value: {
        immediate: true,
        handler(value) {
         this.currentValue = value;
         this.$emit('input', value);
         this.data.forEach(item => {
          if (item[this.itemValue ? this.itemValue : 'id'] == value) {
           return this.currentLabel = item[this.itemLabel ? this.itemLabel : 'name'];
          }
         });
        }
       },
       data:{
        immediate: true,
        handler(arr) {
         if(this.value||!this.currentLabel){
          arr.forEach(item=>{
           if(item[this.itemValue ? this.itemValue : 'id'] == this.value){
            this.currentLabel = item[this.itemLabel ? this.itemLabel : 'name'];
            return;
           }
          })
         }
        }
       }
      },
      methods: {
       chooseItem(item) {
        if (this.currentValue !== item[this.itemValue ? this.itemValue : 'id']) {
         this.$emit('change',item[this.itemValue ? this.itemValue : 'id']);
        }
        this.currentValue = item[this.itemValue ? this.itemValue : 'id'];
        this.currentLabel = item[this.itemLabel ? this.itemLabel : 'name'];
        this.$emit('input', this.currentValue);
        this.collapse = true;
       }
      }
    }
    </script>

    <style lang="scss" scoped>
    .select {
      position: relative;

      .input {
       width: 100%;
       height: 30px;
       line-height: 30px;
       background-color: #fff;
       border: 1px solid #02b4fe;
       border-radius: 0 3px 3px 0;
       padding-left: 10px;
       color: #666;
       position: relative;
       .placeholder {
        color: #aaa;
       }
      }

      .arrow-down {
       width: 0;
       height: 0;
       border-left: 5px solid transparent;
       border-right: 5px solid transparent;
       border-top: 8px solid #02b4fe;
       position: absolute;
       right: 5px;
       top: 10px;
      }

      .arrow-up {
       width: 0;
       height: 0;
       border-left: 5px solid transparent;
       border-right: 5px solid transparent;
       border-bottom: 8px solid #02b4fe;
       position: absolute;
       right: 5px;
       top: 10px;
      }

      .option-list {
       max-height: 200px;
       overflow-y: scroll;
       position: absolute;
       top: 2rem;
       left: 0;
       z-index: 5;
       width: 100%;
       padding: 0 5px;
       font-size: 10px;
       color: #aaa;
       background-color: #fff;
       text-align: left;
       box-shadow: 0 0 5px rgba(0, 0, 0, .1);
       border: 1px solid rgb(79, 192, 232);

       .option-item {
        text-align: center;
        line-height: 1.5rem;
       }
      }
    }
    </style>如上所示,当声明了mySelect组件之后,在项目中实际使用时,就可以如下所示直接使用:

    <template>
    <mySelect v-model="testValue" placeholder="请选择" :data="testArr" item-label="id"
               item-value="name"></mySelect>
    </template>
    <script>
      import mySelect from './mySelect'

      export default{
        components:{
         mySelect
        },
         data(){
          return {
             testValue:'',
             testArr:[]
           }
         },
         mounted(){
          //预置select的下拉选择基础数据,数据为对象数组,包含id和name属性
         }
    }
    </script> 以上就是一个简单的自定义双向绑定组件的实现,包括简单的使用过程。在vue中的自定义组件,关于props的声明时,还是尽量使用官方建议的对象方式,可以声明属性的默认值和数据类型。我这边偷懒了用的是props的字符串数组简写方式,但是这样的话对使用组件时的错误调试不利。所以,尽量不要学我偷懒噢,亲~~~

    到此这篇关于Vue 如何使用props、emit实现自定义双向绑定的实现的文章就介绍到这了,更多相关Vue props、emit实现自定义双向绑定内容请搜索咔叽论坛以前的文章或继续浏览下面的相关文章希望大家以后多多支持咔叽论坛!

    原文地址:https://www.jb51.net/article/188106.htm

    QQ|免责声明|小黑屋|手机版|Archiver|咔叽游戏

    GMT+8, 2024-3-28 18:03

    Powered by Discuz! X3.4

    © 2001-2023 Discuz! Team.

    快速回复 返回顶部 返回列表