背景:為了實現某些功能,如:數據排序、分組、篩選、深拷貝等,自己寫的函數或網上搜索處理的轉換函數質量無法保證,這時直接使用成熟的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'