5分钟即可控制的前端高效利器:JavaScript 战略形式

浅谈 JavaScript 中战略形式的应用:

  • 什么是想象形式
  • 什么是战略形式
  • 战略形式在 JavaScript 中的应用(应用战略形式封装百度AI辨认挪用)
  • 战略形式在 Vue 组件封装中的应用(应用战略形式封装Select组件)

什么是想象形式

想象有一个电子爱好者,虽然他没有经由正规的培训,然则却穷年累月地想象并制作出了很多有效的电子设备:业余无线电、盖革计数器、报警器等。有一天这个爱好者决议从新回到学校去攻读电子学学位,来让本身的才获得正式的承认。跟着课程的睁开,这个爱好者倏忽发明课程内容都素昧平生。素昧平生的不是术语或表述的体式格局,而是背地的观点。这个爱好者不停学到一些称号和道理,虽然这些称号和道理本来他并不知道,但事实上他多年以来一向都在应用。全部历程只不过是一个接一个的顿悟。

想象形式寻思录 ,John Vlissides, 第一章 1.2节

我们在写代码的时刻,肯定也碰到过很多相似的场景。跟着履历的增添,我们关于这些罕见场景的处置惩罚愈来愈随心所欲,以至总结出了针对性的“套路”,下次碰到此类题目直接应用“套路”处理,费心又省力。这些在软件开辟历程当中逐步积聚下来的“套路”就是想象形式。

想象形式的目的之一就是进步代码的可复用性、可扩大性和可保护性。正因如此,虽然有时刻我们不知道某个想象形式,然则看了相干书本或文章后会有一种“啊,本来这就是想象形式”的恍然大明白。

假如你看完这篇文章后也有此觉得,那末祝贺你,你已在高效程序员的道路上一起疾走了。

什么是战略形式

战略形式是一种简朴却经常使用的想象形式,它的应用场景非常普遍。我们先相识下战略形式的观点,再经由过程代码示例来更清楚的熟悉它。

战略形式由两部份组成:一部份是封装差别战略的战略组,另一部份是 Context。经由过程组合和托付来让 Context 具有实行战略的才能,从而完成可复用、可扩大和可保护,而且防止大批复制粘贴的事情。

战略形式的典范应用场景是表单校验中,关于校验划定规矩的封装。接下来我们就经由过程一个简朴的例子详细相识一下:

粗拙的表单校验

一个罕见的登录表单代码以下:

<form id='login-form' action="" method="post">
    <label for="account">手机号</label>
    <input type="number" id="account" name="account">
    <label for="password">暗码</label>
    <input type="password" id="password" name="password">
    <button id='login'>登录</button>
</form>
<script>
    var loginForm = document.getElementById('login-form');

    loginForm.onsubmit = function (e) {
        e.preventDefault();  
        var account = document.getElementById("account").value;
        var pwd = document.getElementById("password").value;

        if(account===null||account===''){
            alert('手机号不能为空');
            return false;
        }
        if(pwd===null||pwd===''){
            alert('暗码不能为空');
            return false;
        }
        if (!/(^1[3|4|5|7|8][0-9]{9}$)/.test(account)) {
            alert('手机号花样毛病');
            return false;
        }
        if(pwd.length<6){
            alert('暗码不能小于六位');
            return false;
        }
        // ajax 发送要求
    }
</script>

以上代码,虽然功用没题目,然则瑕玷也很显著:

代码里各处都是 if 语句,而且它们缺少弹性:每新增一种、或许修正原有校验划定规矩,我们都必须去改loginForm.onsubmit内部的代码。别的逻辑的复用性也很差:假如有别的表单也是用一样的划定规矩,这段代码并不能复用,只能复制。当校验划定规矩发生变化时,比方上文的正则校验并不能婚配假造运营商14/17号段,我们就须要手动同步多处代码变动(Ctrl+C/Ctrl+V)。

优异的表单考证

接下来我们经由过程战略形式的思绪改写一下上段代码,首先抽离并封装校验逻辑为战略组:

var strategies = {
    isNonEmpty: function (value, errorMsg) {
        if (value === '' || value === null) {
            return errorMsg;
        }
    },
    isMobile: function (value, errorMsg) { // 手机号码花样
        if (!/(^1[3|4|5|7|8][0-9]{9}$)/.test(value)) {
            return errorMsg;
        }
    },
    minLength: function (value, length, errorMsg) {
        if (value.length < length) {
            return errorMsg;
        }
    }
};

接下来修正 Context:

var loginForm = document.getElementById('login-form');

loginForm.onsubmit = function (e) {
    e.preventDefault(); 
    var accountIsMobile = strategies.isMobile(account,'手机号花样毛病');
    var pwdMinLength = strategies.minLength(pwd,8,'暗码不能小于8位');
    var errorMsg = accountIsMobile||pwdMinLength; 
    if(errorMsg){
        alert(errorMsg);
        return false;
    }
}

对照两种完成,我们能够看到:分离了校验逻辑的代码假如须要扩大校验范例,在战略组中新增定义即可应用;假如须要修正某个校验的完成,直接修正响应战略即可全局见效。关于开辟和保护都有显著的效力提拔。

扩大:史诗的表单校验

有兴致的朋侪能够相识下 async-validator ,element-ui 和 antd 的表单校验都是基于 async-validator 封装的,能够说是史诗级别的表单校验了

经由过程表单校验的对照,置信人人都对战略形式有所相识,那末接下来经由过程两个例子详细相识下 JavaScript 中战略形式的应用:

应用战略形式挪用百度AI图象辨认

由于百度AI图象辨认的接口范例差别,所需的参数花样也不尽相同。但是图象的紧缩及上传、毛病处置惩罚等部份是公用的。所以能够采纳战略形式封装:

定义战略组

经由过程定义战略组来封装差别的接口及其参数:比方身份证辨认接口的side字段,自定义辨认的templateSign字段,以及行驶证辨认的吸收参数为poparamstData

/**
 * 战略组
 * IDCARD:身份证辨认
 * CUSTOMIZED:自定义辨认
 * VL:行驶证辨认
 */
var strategies = {
    IDCARD: function (base64) {
        return {
            path: 'idcard',
            param: {
                'side': 'front',
                'base64': base64
            }
        };
    },

    CUSTOMIZED: function (base64) {
        return {
            path: 'customized',
            param: {
                'templateSign': '52cc2d402155xxxx',
                'base64': base64
            }
        };
    },
    VL: function (base64) {
        return {
            path: 'vehicled',
            poparamstData: {
                'base64': base64
            }
        };
    },
};

定义 Context

var ImageReader = function () { };

/**
 * 读取图象,挪用接口,猎取辨认效果
 * 
 * @param {*} type 待辨认文件范例
 * @param {*} base64 待辨认文件 BASE64码
 * @param {*} callBack 辨认效果回调
 */
ImageReader.prototype.getOcrResult = function (type, base64, callBack) {
    let fileSize = (base64.length / (1024 * 1024)).toFixed(2);
    let compressedBase64 = '';
    let image = new Image();
    image.src = base64;
    image.onload = function () {
        /**
         * 图片紧缩处置惩罚及非常处置惩罚,代码略
         */
         

        let postData = strategies[type](compressedBase64);

        ajax(
            host + postData.path, {
                data: postData.param,
                type: 'POST',
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded'
                },
                success: function (res) {
                    var data = JSON.parse(res);
                    // 暴露给 UI 层的一致的毛病码
                    if (data.error_code !== undefined && data.error_code !== 0) {
                        var errorData = {
                            error: 1,
                            title: '毛病 ' + data.error_code,
                            content: 'error message'
                        };
                        callBack(errorData);
                    } else {
                        callBack(data);
                    }
                }
            });
    };
};

挪用体式格局

var imageReader = new ImageReader();
imageReader.getOcrResult('IDCARD', this.result.toString(), callback);

应用战略形式封装 Vue Select 组件

某项目中多处用到了 element-ui 的 select 组件,其内涵逻辑相似,都是初始化时猎取下拉列表的数据源,然后在选中某一项时 dispatch 差别的 action。遂斟酌应用战略形式封装。

Context

在本例中,组件向外部暴露一个 prop,挪用方指定该 prop 从而加载差别的战略。那末定义 Context 以下:

<template>
  <el-select v-model="selectedValue" placeholder="请挑选" @change="optionChanged" size="mini" clearable>
    <el-option v-for="item in options" :key="item.id" :label="item.name" :value="item.id">
    </el-option>
  </el-select>
</template>
data() {
    return {
      selectedValue: undefined,
      options: [],
      action: "",
    };
  },
  props: {
    // 暴露给外部的 select-type
    selectType: {
      type: String
    },
  },
  created() {
   // 猎取 options
   this.valuation();
  },
    methods: {
    optionChanged() {
      this.$emit(this.action, this.selectedValue);
    },
    setOptions(option) {
      this.$store.dispatch(this.action, option);
    },
    valuation() {
      // 猎取 options 数据
    }
  },

外部经由过程以下体式格局挪用组件:

<MySelect selectType="product"/>

strategies

然后定义战略组:

let strategies = {
    source: {
        action: "sourceOption",
        getOptions:  function() {
            // 拉取 options
        }
    },
    product: {
        action: "productOption",
        getOptions:  function() {
            // 拉取 options
        }
    },
    ...
}

异步

至此该组件的基础构造已清楚,但还存在一个题目:组件加载时是异步拉取的 options, 而页面初始化的时刻极可能 options 还没有返回,致使 select 的 options 仍为空。所以此处应当修正代码,同步猎取 options:

// 战略组修正
source: {
    action: "sourceOption",
    getOptions: async function() {
        // await 拉取 options
    }
  },
// 组件修正
methods: {
    ...
    async valuation() {
        ...
    }
}

继承优化

但我们不是每次加载组件都须要拉取 options,假如这些 options 在其他组件或许页面也被应用到,那末能够斟酌将其存入 vuex 中。

最最先的思绪是高阶组件,即定义一个包装后的select模板,经由过程高阶组件的体式格局扩大其数据源与action(变化的部份)但是这个思绪不是那末的vue(主如果slots不太好处置惩罚) 因而斟酌战略形式改写该组件

总结

经由过程以上两个例子,我们能够看到:

  • 战略形式相符开放-关闭准绳
  • 假如代码里须要写大批的if-else语句,那末斟酌应用战略形式
  • 假如多个组件(类)之间的区分仅在于它们的行动,斟酌采纳战略形式

参考
JavaScript想象形式与开辟实践(曾探) 第五章 战略形式

    原文作者:hengg
    原文地址: https://segmentfault.com/a/1190000016293336
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞