背景:为了实现某些功能,如:数据排序、分组、筛选、深拷贝等,自己写的函数或网上搜索处理的转换函数质量无法保证,这时直接使用成熟的js第三方库是首选。
*注:“framework(框架)”,“library(库)”和“tool(工具)” 可以根据情境,在不同时期,对不同的人,意味着不同的东西。
一、Lodash 和 Underscore(推荐参考阮一峰的日志)
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]
分别是:异步请求、加密、日期转换
实例:
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.多维数组合并为一维数组
//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){ // 判断去重后的数组长度
}
//补:其他引用写法:
对比一下,自己写一个异步请求函数需要怎么做:
// 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 += `\n${res.info}`
} else {
this.loadingBuild = false
this.loadingBuildResult = false
this.$message.error(res.err_desc)
this.buildResult = `打包失败:\n错误码:${res.error_code}\n${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 = `打包失败:\n错误码:${res.error_code}\n${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 = `打包超时:\n错误码:${res.error_code}\n${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'