zoukankan      html  css  js  c++  java
  • 如何抽象一个 Vue 公共组件

    之前一直想写一篇关于抽象 Vue 组件的随笔,无奈一直没想到好的例子。恰巧最近为公司项目做了一个数字键盘的组件,于是就以这个为例聊聊如何抽象 Vue 的组件。

    先上 Demo源码。(demo最好在浏览器里以手机模式浏览)

     

    在讲具体实现前,我想先分享下自己认为的理想的公用组件是什么样的:

    1. 黑盒性,即除了你自己以外,其他的开发者在快速阅读使用文档之后可以立刻上手,而不用关心你的内部实现;

    2. 独立性,即做好解耦,不与父组件有过多关联;

    3 自定义性,适当地暴露一些输入接口或者方法给外部用于自定义,同时也要设置好这些属性在外部未输入时的默认值。

     

    下面我们先以黑盒的方式看看 demo 中数字键盘组件是如何调用的(已省略非关键部分代码)。

    App.vue

    <template>
    ......
        <keyboard @submit-event='handleSubmit' @change-event='handleChange'></keyboard>
    </template>
    <script>
    import keyboard from 'Keyboard.vue';
    export default {
        data() {
            return {
                value: ''
            };
        },
        methods: {
            handleChange(value, currentValue) {
                console.log(value, currentValue);
                this.value = value;
            },
            handleSubmit() {
                console.log('submit: ' + this.value);
            }
        }
    }
    </script>

    如上,最基本的调用就完成了。效果如下:

    接着,点击 1-6 与“确定”。如果如下:

    可以看到数字键盘已经如我们预期工作了,接下来分析下该数字键盘组件所有的输入项。

    @change-event:该事件为自定义事件,父组件通过 v-on 注册监听,子组件内部通过 $emit 进行触发(更多自定义事件相关内容请参考:Vue官方教程)。

    每一次点击数字按键以及退格键均会触发该事件,其传递两个参数:value,累积点击的字符组合;currentValue,当前点击的字符。父组件通过 handleChange 方法接收该事件的回调内容。

    @submit-event:当点击“确定”键即会触发该事件,其不传递参数,只是告诉父组件“我的确定按钮被点击了,你要做什么操作自己看着办吧,之前点击的数字已经通过 change-event 传给你了”。父组件通过 handleSubmit 方法接收回调。

    如果只写这两个方法未免也太没诚意了,我还根据一些场景编写了以下几个自定义属性。

    max:最大输入长度,超过的部分将不会触发 change-event 事件,默认无限制。

    <keyboard max='6'></keyboard>

    sp-key:自定义的特殊字符,如身份证输入时的“X”,会添加到左下角空白格,默认无。

    <keyboard sp-key='X'></keyboard>

    random:是否打乱数字顺序,一些有关银行账户或密码的输入经常会见到这种场景,默认 false。

    <keyboard random='true'></keyboard>

    从上面的几个自定义属性与事件,我们大概知道了父组件是如何向子组件传值以及监听子组件的变化,但父组件该如何直接调用子组件内部的函数呢?我们看下面这个场景。

    数字键盘上的键盘图标,点击之后会将数字键盘收起隐藏。组件内部拥有一个方法 keyboardToggle(true|false) 来控制键盘的弹起和收回,那么如果在组件外部也想调用这个方法呢?比如当父组件中的 input 获取到焦点时。

    可以通过 Vue 中的 ref 属性来获取到键盘的组件引用,从而调用其内部的方法,如下:

    $refs.[refName].keyboardToggle(true|false)

    <template>
        <input type='text' @focus='handleShowKeyboard($event)' />
        <keyboard ref='kbref'></keyboard>
    </template>
    <script>
    import keyboard from 'Keyboard';
    export default {
        //...
        methods: {
            handleShowKeyboard(e) {
                e && e.preventDefault();
                this.$refs.kbref.keyboardToggle(true);
            }
        }
    }
    </script>

    以上面这种形式便可以在父组件上下文中调用子组件内的方法。

    $refs.[refName].handleInit()

    数字键盘组件内部的初始化方法,用于重新渲染组件。若 random 属性为 true,则数字键会刷新随机排列。

    $refs.[refName].handleClear()

    清除之前输入的字符组合,并触发 change-event 且返回空字符串。

     

    上面分享了这个组件所有对外的属性与事件,可以发现我们并未看过该组件内部的一行代码,但已经可以完整的使用它了,下面来聊聊内部实现。

    首先来看看布局,我将键盘分为左右两部分,右边部分不用多说,左边的部分是将一个键位数组通过 v-for 循环生成。

    那么是如何让 0 和 9 之间空出一格呢,下面看下初始化键盘组件的方法。

    keyboard.vue

    handleInit()

    <template>
        <div>
            <div class='kb-left'>
                <div class='kb-item' v-for='item in keyArr'>{{item}}</div>
                <div class='kb-item kb-toggle'></div> //键盘图标
            </div>
            <div class='kb-right'>
                //...        
            </div>
        </div>
    </template>
    <script>
    export default {
        data() {
            return {
                baseArr: []
            }
        },
        computed: {
            keyArr() {
                this.handleInit();
                return this.baseArr;
            }
        },
        methods: {
            handleInit() {
                this.baseArr = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0'];
                this.baseArr.splice(this.baseArr.length - 1, 0, '');
            }
        }
    }
    </script>

    即在键位数组倒数第二位插入一个空字符,然后循环生成按键。下面看下自定义参数是如何生效的。

    sp-key

    <script>
    export default {
        props: ['spKey'],
        data() {
            return {
                baseArr: []
            }
        },
        //....
        methods: {
            handleInit() {
                let spKey = this.spKey;
                this.baseArr = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0'];

           this.baseArr.splice(this.baseArr.length - 1, 0, spKey); } } } </script>

    在组件内部通过 props 属性接收父组件传递的 spKey,之后就可在组件内的属性和方法中通过 this.spKey 进行访问。首先判断 spKey 值是否有效,并代替空字符插入键位数组倒数第二项。

    random

    <script>
    export default {
        props: ['spKey', 'random'],
        data() {
            return {
                baseArr: []
            }
        },
        //....
        methods: {
            handleInit() {
                let spKey = this.spKey;
                this.baseArr = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0'];
    
              if (this.random && this.random != 'false') {
                 this.baseArr.sort(function() {
                   return Math.random() - Math.random();
                });
               }
    
                this.baseArr.splice(this.baseArr.length - 1, 0, spKey);
            }
        }
    }
    </script>

    将键位打乱顺序其实也很简单,只要通过数组的 sort 方法。sort 方法可以接收一个函数作为参数,若函数返回正数则交换前后两项的位置,若函数返回负数则不作交换。所以将两个随机数相减的结果返回,即可将键位数组随机排序。

    下面看看在组件内部是如何触发 change-event 的。

    handleInput()

    <template>
        <div>
            <div class='kb-left'>
                <div @click='handleInput(item)' class='kb-item' v-for='item in keyArr'>{{item}}</div>
                <div class='kb-item kb-toggle'></div> //键盘图标
            </div>
            //...
        </div>
    </template>
    <script>
    export default {
        data() {
            return {
                inputStr: ''
            }
        },
        //...
        methods: {
            handleInput(value) {
                this.inputStr += value;
                this.$emit('change-event', this.inputStr, value);
            }
        }
    }
    </script>

    增加了 max 属性后修改方法如下:

    handleInput(value) {
        let max = Number(this.max);
        if (!isNaN(max) && this.inputStr.length+1 > max) {
            return;
        }
    
        this.inputStr += value;
        this.$emit('change-event', this.inputStr, value);
    }

    最后看看退格删除是如何实现的。

    handleDelete()

    handleDelete() {
        let str = this.inputStr;
       if (!str.length) return;
    
        this.inputStr = str.substring(0, str.length - 1);
        this.$emit('change-event', this.inputStr);
    } 

    上面的例子都是些核心代码的片段,并且其他辅助函数并未列出,想查看完整的代码请看源码

    感谢你的浏览,希望能有所帮助。

  • 相关阅读:
    基于屏幕的可用区域
    Legacy Browser/Windows/Office Support
    getBoundingClientRect 和 getClientRect
    基础健康知识——12.自限性疾病
    基础健康知识——11.弊病
    基础健康知识系列
    基础健康知识——常见疾病:腹泻
    基础健康知识——常见疾病:感冒
    基础健康知识——10.就医
    基础健康知识——9.反复感染
  • 原文地址:https://www.cnblogs.com/ghost-xyx/p/7640204.html
Copyright © 2011-2022 走看看