zoukankan      html  css  js  c++  java
  • 高性能的js第三方库——lodash、 Underscore、async、md5及moment

    背景:为了实现某些功能,如:数据排序、分组、筛选、深拷贝等,自己写的函数或网上搜索处理的转换函数质量无法保证,这时直接使用成熟的js第三方库是首选。

    *注:“framework(框架)”,“library(库)”和“tool(工具)” 可以根据情境,在不同时期,对不同的人,意味着不同的东西。

    一、LodashUnderscore(推荐参考阮一峰的日志)

    1.优点:将 Lodash 和 Underscore 放在一起,因为它们提供了数百个功能性的 JavaScript 实用程序来补充原生字符串,数字,数组和其他原始对象的方法。二者有一些功能性的重叠,所以你不太可能在一个项目中同事需要这两个库。

    它在客户端使用率似乎很低,但是可以在服务器端的 Node.js 应用程序中使用这两个库。

    • 小而简单
    • 拥有优质文档,易于学习
    • 与大多数库和框架兼容
    • 不扩展内置对象
    • 可以在客户端或服务器上使用

    2.缺点:

    • 有些方法只适用于ES2015及更高版本的 JavaScript

    实例:

    import * as _ from 'lodash'
    import * as _s from 'underscore'
    
    //数组去重对比
    _.uniq([1,1,3])
    // => [1,3]
    
    _s.uniq([1, 2, 1, 4, 1, 3]);
    => [1, 2, 4, 3]

    二、asyncmd5moment

    分别是:异步请求、加密、日期转换

    实例:

    import * as _async from 'async'
    import * as _moment from 'moment'
    import * as _md5 from 'md5'
    
    shunXuAsync(){
      // 异步 顺序执行
      let task1 = function (callback) {
    
        console.log("task1");
        callback(null, "task1")
      }
    
      let task2 = function (callback) {
    
        console.log("task2");
        callback(null, "task2")
        // callback("err","task2") // null改为err ,如果中途发生错误,则将错误传递到回调函数,并停止执行后面的函数
      }
    
      let task3 = function (callback) {
    
        console.log("task3");
        callback(null, "task3")
      }
    
      _async.series([task1, task2, task3], function (err, result) {
        console.log("series");
        if (err) {
          console.log(err);
        }
        console.log(result);
      }
      )
    }
    
    console.log(_moment().format('MMMM Do YYYY, h:mm:ss a')) //当前时间+格式
    
    console.log(md5('message'));//78e731027d8fd50ed642340b7c9a63b3

    附:常用的几个Lodash函数

    【浅拷贝】:

    var objects = [{ 'a': 1 }, { 'b': 2 }];
     
    var shallow = _.clone(objects);
    console.log(shallow[0] === objects[0]);
    // => true

    如图:

     

      【深拷贝】:

      

    import _ from 'lodash'
    
    // 1.深拷贝
    var objects = [{ 'a': 1 }, { 'b': 2 }];
    var deep = _.cloneDeep(objects);
    console.log(deep[0] === objects[0]);
    // => false
    
    //2.分组
    _.groupBy([6.1, 4.2, 6.3], Math.floor);
    // => { '4': [4.2], '6': [6.1, 6.3] }
     
    // The `_.property` iteratee shorthand.
    _.groupBy(['one', 'two', 'three'], 'length');
    // => { '3': ['one', 'two'], '5': ['three'] }
    
    //3. 合并key、value
    _.zipObject(['a', 'b'], [1, 2]);
    // => { 'a': 1, 'b': 2 }
    
    //4.深比较不同
    var object = { 'a': 1 };
    var other = { 'a': 1 };
     
    _.isEqual(object, other);
    // => true
     
    object === other;
    // => false
    
    //5.数组对象去重
    var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 2 }];
     
    _.uniqWith(objects, _.isEqual);
    // => [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }]
    
    //6.是否为空:对象、数组、布尔值、数值
    _.isEmpty(null);
    // => true
     
    _.isEmpty(true);
    // => true
     
    _.isEmpty(1);
    // => true
     
    _.isEmpty([1, 2, 3]);
    // => false
     
    _.isEmpty({ 'a': 1 });
    // => false

    //7.多维数组合并为一维数组
     _.flattenDeep([1, [2, [3, [4]]5]]);
    
    
     // => [1, 2, 3, 4, 5]
    //8.根据某个字段排序

    const users = [
    { 'user': 'fred', 'age': 48 },
    { 'user': 'barney', 'age': 34 },
    { 'user': 'fred', 'age': 42 },
    { 'user': 'barney', 'age': 36 }
    ];

    // 以 `user` 升序排序 再 以 `age` 降序排序。
    _.orderBy(users, ['user', 'age'], ['asc', 'desc']);

    // 9 .简单数组的排序

    _.sortBy(arr, function(item) {
      return item.createTime;
    });

    _.sortBy(arr, function(item) {
    return -item.createTime;
    });

    // 10.判断是否有重复

    _.uniq([2, 1, 2])
    // => [2, 1]

    if(_.uniq(arr) = arr.lengh){ // 判断去重后的数组长度

    }

    //补:其他引用写法:

    import {flattenDeep} from 'lodash' // 多维数组转换为一维数组
    
    

     对比一下,自己写一个异步请求函数需要怎么做:

    // 1.分布打包请求
    jarBuildChange(params) {
      this.buildResult = ''
      this.loadingBuild = true
      this.loadingBuildResult = true
      const stepCallback = (res) => {
        console.log(res, 'stepCallback')
        if (res.err_code === '0') {
          this.loadingBuildResult = false
          this.$message.success('日志已更新,正在打包中......')
          this.buildResult += `
    ${res.info}`
        } else {
          this.loadingBuild = false
          this.loadingBuildResult = false
          this.$message.error(res.err_desc)
          this.buildResult = `打包失败:
    错误码:${res.error_code}
    ${res.err_desc}`
        }
      }
      const lastCallback = (res) => {
        console.log(res, 'lastCallback')
        if (res.err_code === '0') {
          this.loadingBuild = false
          this.buildResult += `${res.err_desc}`
          this.getBuildHistory()
          this.custom.jarname = res.jarname
          this.$message.success('打包结束,打包记录已更新!')
        } else {
          this.loadingBuild = false
          this.loadingBuildResult = false
          this.$message.error(res.err_desc)
          this.buildResult = `打包失败:
    错误码:${res.error_code}
    ${res.err_desc}`
        }
      }
      const timeoutCallback = (res) => {
        console.log(res, 'timeoutCallback')
        this.loadingBuild = false
        this.loadingBuildResult = false
        this.getBuildHistory()
        this.$message.error(res.err_desc)
        this.buildResult = `打包超时:
    错误码:${res.error_code}
    ${res.err_desc}`
      }
      jarBuildResult(params, stepCallback, lastCallback, timeoutCallback)
    }
    
    //2.打包结果持续拉取:参数+开始返回+最后返回+超时返回
    export function jarBuildResult(params, stepCallback, lastCallback, timeoutCallback) {
      stepRequest(_config.jarpackage_build, params || {}, stepCallback, lastCallback, 3600000, timeoutCallback)
    }
    
    //3.分布执行
    export const stepRequest = (
      url, // 要封装调用接口路径
      data, // 封装调用接口请求数据
      stepCallback, // 中间步骤response回调,参数为response json
      lastCallback, // 调用最后response回调,参数为response json
      timeoutMs, // 执行超时时间
      timeoutCallback // 超时回调,无参数
    ) => {
      let nextSeqid = 0
      let isSuccDone = false
      let timeoutChecker = new TimeoutChecker(timeoutMs)
    
      let uuid = makeUuid()
    
      data['step_track_uuid'] = uuid
    
      const doMainRequest = () => axios({
        url: url,
        method: 'post',
        data: data,
        timeout: 3600000
      }).then(function (response) {
        return response
      }).catch(function (error) {
        console.log(error)
      })
    
      const handleResponseList = (stepRes) => {
        for (let response of stepRes.data.response_list) {
          // eslint-disable-next-line
          stepCallback(eval('(' + response + ')'))
        }
      }
    
      const handleTimeout = () => {
        if (timeoutCallback) {
          let func = timeoutCallback
          timeoutCallback = null
          func()
        }
      }
    
      let interval = setInterval(() => {
        if (isSuccDone) {
          clearInterval(interval)
          handleTimeout()
        } else {
          if (timeoutChecker.isTimeout()) {
            clearInterval(interval)
            handleTimeout()
          } else {
            getResponseStepList(uuid, nextSeqid).then((stepRes) => {
              if (isSuccDone) {
                clearInterval(interval)
              } else {
                nextSeqid = stepRes.data.next_seqid
                handleResponseList(stepRes)
              }
            })
          }
        }
      }, 2000)
    
      doMainRequest().then(res => {
        if (!timeoutChecker.isTimeout()) {
          isSuccDone = true
          clearInterval(interval)
          getResponseStepList(uuid, nextSeqid).then((stepRes) => {
            handleResponseList(stepRes)
            lastCallback(res.data)
          })
        } else {
          handleTimeout()
        }
      })
    }

     生成随机数uuid:

    function makeUuid () {
      var s = []
      var hexDigits = '0123456789abcdef'
      for (var i = 0; i < 36; i++) {
        s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1)
      }
      // bits 12-15 of the time_hi_and_version field to 0010
      s[14] = '4'
      // bits 6-7 of the clock_seq_hi_and_reserved to 01
      s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1)
      s[8] = s[13] = s[18] = s[23] = '-'
    
      var uuid = s.join('')
      return uuid
    }

    获取分布列表:

    export const getResponseStepList = (uuid, seqid) =>
      axios.post(CONFIG_PREFIX + '/response/steplist', {
        step_track_uuid: uuid,
        step_next_seqid: seqid
      }).then(function (response) {
        return response
      }).catch(function (error) {
        console.log(error)
      })

    axios异步封装:

    import { CONFIG_PREFIX } from './config'
    // import axios from 'axios';
    import axios from './axios.js'
    
    /**
     * axios 配置
     * 请求超时
     * 登录携带cookie
     */
    import axios from 'axios'// 让ajax携带cookie
    import { doLogin } from './login'
    import { Message } from 'element-ui'
    import queryString from 'query-string'
    import router from '../../src/router'
    // import Vue from 'vue'
    axios.defaults.timeout = 3600000 // 请求超时
    axios.defaults.withCredentials = true
    function goNotAllowedPage () {
      // document.body.innerHTML = "<div id='app'></div>"
      // let app = new Vue({
      //   el: '#app',
      //   template: '<div style="margin-top: 100px"><p style="text-align: center">暂无此模块权限,如需访问,请联系XXX申请权限。</p></div>'
      // })
    
      // app.$destroy()
      router.push({ path: '/web/notallowed' })
    }
    //* ************************************************ http request 请求拦截器
    const parsed = queryString.parse(location.search)
    axios.interceptors.request.use(
      config => {
        if (parsed.__test) {
          // e2e测试
          config.url = config.url.trim() + '?v__test=' + parsed.__test
        }
        return config
      },
      err => {
        return Promise.reject(err)
      }
    )
    
    //* ******************************************** http response 返回拦截器
    axios.interceptors.response.use(
      response => {
        if (response.status === 200 && response.data) {
          const res = response.data
          // 接口4001统一处理,4001意思是没有登录状态,需要重新登录
          if (res.err_code === '4001') {
            doLogin()
            // eslint-disable-next-line
            return Promise.reject("error");
          } else if (res.err_code === '4003') {
            goNotAllowedPage()
            res.err_code = '0'
            return response
          } else if (res.err_code === '4005') {
            Message.error('请联系owner,无法进行修改')
            // eslint-disable-next-line
            return response;
          } else if (res.app_config || res.err_code === '0') {
            // proj/app/get此请求无返回err_code=0
            return response
          } else {
            // const desc = res.err_desc ? '操作失败错误,错误码:' + res.err_code + ',错误信息:' + res.err_desc : '返回值错误!';   //暂时注释
            // Message.warning(desc);
            return response
          }
        }
      },
      error => {
        // console.log(error, '跨越问题无法获取状态码')
        if (error && error.response) {
          if (error.response.status === 400) {
            Message.error('请求错误(400)')
          } else if (error.response.status === 404) {
            Message.error('请求出错(404)')
          } else if (error.response.status === 408) {
            Message.error('请求超时(408)')
          } else if (error.response.status === 500) {
            Message.error('服务器错误(500)')
          } else if (error.response.status === 501) {
            Message.error('服务未实现(501)')
          } else if (error.response.status === 502) {
            Message.error('网络错误(502)')
          } else if (error.response.status === 503) {
            Message.error('服务不可用(503)')
          } else if (error.response.status === 504) {
            Message.error('网络超时(504)')
          } else {
            Message.error(`连接出错(${error.response.status})!`)
          }
        } else {
          Message.error(error)
        }
        return Promise.reject(error)
      }
    )
    
    export default axios
    
    
    //配置参数:
    /** 【统一配置url入口】
     * 统一URL:cgi_domain_prefix
     * 代理URL(正式/测试)
     * 获取ticket:getPar(par)
     */
    /* eslint-disable */
    let cgi_domain_prefix = MY_HOST
    
    // let cgi_domain_prefix='http://localhost:8080';
    
    // if (process.env.NODE_ENV === 'production') {
    //     cgi_domain_prefix = "http://xxx:8080";
    // } else {
    //     cgi_domain_prefix = "http://xxx:8000";
    // }
    
    export const CONFIG_PREFIX = cgi_domain_prefix
    //* **********************************************************************************************************
    // 获取登录后的ticket:这里可以使用js第三方库【cookie.js】
    function getPar (par) {
      // 获取当前URL
      // console.log("document.location.href", document.location.href)
    
      let local_url = document.location.href
      // 获取要取得的get参数位置
      let get = local_url.indexOf(par + '=')
      // console.log("document.location.href 2", document.location.href)
    
      if (get === -1) {
        return false
      }
      // 截取字符串
      let get_par = local_url.slice(par.length + get + 1)
      // 判断截取后的字符串是否还有其他get参数
      let nextPar = get_par.indexOf('&')
      if (nextPar !== -1) {
        get_par = get_par.slice(0, nextPar)
      }
    
      return get_par
    }
    const REAL_TICKER = getPar('ticket') ? '?ticket=' + getPar('ticket') : ''
    
    // 实例:
    // 1.1.登录
    export const GET_USER = cgi_domain_prefix + '/user/rtxname' + REAL_TICKER
    // 1.2.注销
    export const GET_LOGOUT = cgi_domain_prefix + '/user/logout'
  • 相关阅读:
    LeetCode 811. Subdomain Visit Count (子域名访问计数)
    LeetCode 884. Uncommon Words from Two Sentences (两句话中的不常见单词)
    LeetCode 939. Minimum Area Rectangle (最小面积矩形)
    LeetCode 781. Rabbits in Forest (森林中的兔子)
    LeetCode 739. Daily Temperatures (每日温度)
    三种方式实现按钮的点击事件
    239. Sliding Window Maximum
    14.TCP的坚持定时器和保活定时器
    13.TCP的超时与重传
    12.TCP的成块数据流
  • 原文地址:https://www.cnblogs.com/wheatCatcher/p/11345141.html
Copyright © 2011-2022 走看看