不容错过的Vue2.0组件开发

简述


(本文针对于有Vue有一定基础的人学习了解,环境搭建等入门教程网上很多,大家自行学习,也可留言,我会给出一些不错的教程博客供大家学习。)

一、通过一个小demo,让大家深入了解组件开发。并将教会大家以下知识。
  1. (特色) v-model 在自定义组件中的使用。在送索引擎中,搜索 v-model 相关知识,绝大部分都是如何使用v-model这个指令进行数据双向绑定。然而,如何自定义一个组件,并且使用这个组件时可以用v-model,让自己的组件也支持数据双向绑定,是一个问题,也很少有教程。

2.自定义组件,组件的使用。这部分内容网络上的教程很多,不作为重点介绍。

3.组件间的通讯。本章中将会使用$emit进行父子组件中的通讯。并且提供工具类,降低耦合性。

4.<strong>(特色)两个组件配合使用,完成特定功能</strong>两个组件嵌套使用,完成效果和对数据的管理。

二、demo 说明

废话不多说,先上效果图。

demo.png
demo.gif

实现思路,自定义两个组件,一个标签组件“seltipoption”,方便使用v-for进行渲染,一个父组件"seltiplist",管理v-model,接收"seltipoption"事件等。

编写

1 新建组件“seltipoption.vue”

该组件需要完成的功能是:显示标签内容、响应点击事件、高亮显示是否被选中。

<template>
  <span class="tip" @click="selectOptionClick" :class="{'active':itemSelected }">{{currentLabel}}</span>
</template>
<script type="text/ecmascript-6">
  import Emitter from "../emitter";
  var underscore = require("../underscore-min");
  export default{
    mixins: [Emitter],
    componentName: "seltipoption",
    props: {
      value: {
        required: true
      },
      label: [String, Number],
    },
    //属性计算
    computed: {
      currentLabel() {
        return this.label || ((typeof this.value === 'string' || typeof this.value === 'number') ? this.value : '');
      },
      parent() {
        let result = this.$parent;
        while (!result.istipSelect) {
          result = result.$parent;
        }
        return result;
      },
      itemSelected() {
        if (!this.parent.multiple) {
//由于现实问题,此处underscore..isEqual中间为下划线_
           return underscore._.isEqual( this.parent.value, this.value);
        } else {
          let isSelected = false;
          this.parent.value.forEach((item, index) => {
//由于现实问题,此处underscore..isEqual中间为下划线_
            if (underscore._.isEqual(item, this.value)) {
              isSelected = true;
            }
          });
          return isSelected;
        }
      },
    },
    methods: {
      selectOptionClick(){
        this.dispatch('seltiplist', 'handleOptionClick', this.value);
      }
    }
  }
</script>

2 新建组件 “seltiplist.vue”

该组件需要实现的功能:子组件内容分发、响应子组件“seltipoption”选中和取消选中事件、实现v-model的数据双向绑定。

<template>
  <div>
    <slot></slot>
  </div>
</template>
<script type="text/ecmascript-6">
 import Emitter from "../emitter";
var underscore = require("../underscore-min");
export
default {
        mixins:[Emitter],
        componentName: "seltiplist",//组件名称,自定义字段。方便用这个属性进行组件间交流
        data() {
            return {
                istipSelect: true
            }
        },
        props: {
            value: {},
            multiple: {
                type: Boolean,
                default:false
            }//属性标识筛选条件是单选项还是多选项

        },

        mounted() {
            this.$on('handleOptionClick', this.handleOptionSelect);
        },
        methods: {
            handleOptionSelect(val) {
//由于现实问题,此处underscore..isArray中间为下划线
                if (this.multiple && underscore._.isArray(this.value) )  {
                    this.value.forEach( (item, index ) => {
//由于现实问题,此处underscore..isEqual中间为下划线_
                        if (underscore._.isEqual(item, val)) {
                            optionIndex = index;
                        }
                    });
                    if (optionIndex < 0) {
                        this.value.push(val);
                    } else {
                        this.value.splice(optionIndex, 1)
                    }
                } else {
                    if (val != this.value) {
                        this.$emit('input', val);
                    } else {
                        this.$emit('input', "");
                    }
                }

            }
        }
    }
</script>
</code>
3 组件使用
 <div class="selectlist">
        <div class="title">地区</div>
        <div class="select-box">
          <seltiplist  v-model="loacl_select">
            <seltipoption v-for=" item in local " :label="item.label" :value="item"></seltipoption>
          </seltiplist>
        </div>
      </div>
      <div class="selectlist">
        <div class="title">薪资要求</div>
        <div class="select-box">
          <seltiplist  v-model="salary_select">
            <seltipoption v-for=" item in salary " :label="item.label" :value="item"></seltipoption>
          </seltiplist>
        </div>
      </div>
      <div class="selectlist">
        <div class="title">福利待遇</div>
        <div class="select-box">
          <seltiplist :multiple="true"  v-model="treatment_select">
            <seltipoption v-for=" item in treatment " :label="item.label" :value="item"></seltipoption>
          </seltiplist>
        </div>
      </div>
<script>
  import seltiplist from "./seltiplist.vue";
  import seltipoption from "./seltipoption.vue";

  export default{
    components: {
      seltiplist,
      seltipoption
    },
    data(){
      return{
        loacl_select:"",
        treatment_select:[{
          id:4,
          label:"项目奖金"
        }],
        salary_select:"",
        local:[{
          id:1,
          label:"北京"
        },{
          id:2,
          label:"安徽"
        },{
          id:3,
          label:"上海"
        },{
          id:4,
          label:"广州"
        }],
        treatment:[{
          id:1,
          label:"五险一金"
        },{
          id:2,
          label:"交通补助"
        },{
          id:3,
          label:"带薪年假"
        },{
          id:4,
          label:"项目奖金"
        }],
        salary:[{
          id:1,
          label:"4000以下"
        },{
          id:2,
          label:"4000-6000"
        },{
          id:3,
          label:"6000-8000"
        },{
          id:4,
          label:"8000以上"
        }]
      }
    },
    methods: {}
  }
</script>
4 代码解读

1,如何实现的自定义组件v-model数据双向绑定?
如果要让组件的 v-model 生效,则这个组件它必须:

  • 接受一个 value 属性
  • 在有新的 value 时触发 input 事件,直接赋值给this.value是无效的,无法触发更新。
  • 当为多选项时,更新seltiplist组件的value数组即可,但是当为单选项时,需要使用this.$emit('input', val);来触发更新。

2,父子组件通讯。
我加入了一个minx混合,也就是组件中看到引入的Emitter

function broadcast(componentName, eventName, params) {
  this.$children.forEach(child => {
    var name = child.$options.componentName;
    if (name === componentName) {
      child.$emit.apply(child, [eventName].concat(params));
    } else {
      broadcast.apply(child, [componentName, eventName].concat(params));
    }
  });
}
export default {
  methods: {
//事件向上传播,参数:接收事件组件的componentName属性,事件名称,传入的参数
    dispatch(componentName, eventName, params) {
      var parent = this.$parent || this.$root;
      var name = parent.$options.componentName;
//根据组件的componentName值,找到父组件
      while (parent && (!name || name !== componentName)) {
        parent = parent.$parent;
        if (parent) {
          name = parent.$options.componentName;
        }
      }
///父组件触发$emit回调
      if (parent) {
        parent.$emit.apply(parent, [eventName].concat(params));
      }
    },
//事件向下传播,参数:接收事件组件的componentName属性,事件名称,传入的参数(原理同上)
    broadcast(componentName, eventName, params) {
      broadcast.call(this, componentName, eventName, params);
    }
  }
};

当 seltipoption.vue 响应click事件后,通过this.dispatch('seltiplist', 'handleOptionClick', this.value);
触发父组件 seltiplist.vue 的监听this.$on('handleOptionClick', this.handleOptionSelect);实现组件间的通讯。并且在一定程度上可以起到解耦的作用。

3, seltipoption 的选择状态判断
当为单选项时 ,判断seltipoption的value是否等于seltiplist的value;
当为多选项时,seltipoption的value存在于seltiplist value的数组中。
为了判断两个value是否值相等。此处引入工具underscore
github:https://github.com/jashkenas/underscore
文档:http://underscorejs.org/

总结

代码量不多,也没有对每个方法进行说明,但是非常适合准备入手自定义组件同行们。提供了一些组件开发的思路,有很多时候组件配合使用更为便捷。
demo的代码已上传至github,以便参考学习。
github:https://github.com/DevilLeven/select_tip

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容

  • Vue 实例 属性和方法 每个 Vue 实例都会代理其 data 对象里所有的属性:var data = { a:...
    云之外阅读 2,198评论 0 6
  • UI组件 element - 饿了么出品的Vue2的web UI工具套件 Vux - 基于Vue和WeUI的组...
    鲁大师666阅读 43,366评论 5 97
  • UI组件 element - 饿了么出品的Vue2的web UI工具套件 Vux - 基于Vue和WeUI的组件库...
    卞卞村长L阅读 1,696评论 0 8
  • 八三班的第二次家长会 赵利娜 今天是开学的第一个周末,我把八三班的...
    鸣鸥阅读 395评论 0 5
  • 一条条泛滥的河 闪着金光流向用武之地 在喧嚣的岁月里 衰微成一叶孤岛 老人的眼泪 滴落在无人的角落 弃婴的哭喊 哑...
    朕要生吃西红柿阅读 81评论 1 5