作為戰(zhàn)斗在業(yè)務(wù)一線的前端,要想少加班畔勤,就要想辦法提高工作效率蕾各。這里提一個(gè)小點(diǎn),我們?cè)跇I(yè)務(wù)開發(fā)過程中庆揪,經(jīng)常會(huì)重復(fù)用到日期格式化式曲、url參數(shù)轉(zhuǎn)對(duì)象、瀏覽器類型判斷缸榛、節(jié)流函數(shù)等一類函數(shù)吝羞,這些工具類函數(shù),基本上在每個(gè)項(xiàng)目都會(huì)用到内颗,為避免不同項(xiàng)目多次復(fù)制粘貼的麻煩钧排,我們可以統(tǒng)一封裝,發(fā)布到npm均澳,以提高開發(fā)效率恨溜。
這里,筆者已經(jīng)封裝并發(fā)布了自己的武器庫 outils负懦,如果你對(duì)本項(xiàng)目感興趣筒捺,歡迎 star 本項(xiàng)目。當(dāng)然你也可以在本項(xiàng)目的基礎(chǔ)上封裝自己的武器庫纸厉。
常用函數(shù)匯總
這里先分類整理下,之前項(xiàng)目中多次用到的工具函數(shù)五嫂。
1.Array
1.1 arrayEqual
/**
*
* @desc 判斷兩個(gè)數(shù)組是否相等
* @param {Array} arr1
* @param {Array} arr2
* @return {Boolean}
*/
functionarrayEqual(arr1, arr2) {
if(arr1 === arr2)returntrue;
if(arr1.length != arr2.length)returnfalse;
for(vari =0; i < arr1.length; ++i) {
if(arr1[i] !== arr2[i])returnfalse;
}
returntrue;
}
2.Class
2.1 addClass
/**
*
* @desc ? 為元素添加class
* @param ?{HTMLElement} ele
* @param ?{String} cls
*/
varhasClass =require('./hasClass');
functionaddClass(ele, cls) {
if(!hasClass(ele, cls)) {
ele.className +=' '+ cls;
}
}
2.2 hasClass
/**
*
* @desc 判斷元素是否有某個(gè)class
* @param {HTMLElement} ele
* @param {String} cls
* @return {Boolean}
*/
functionhasClass(ele, cls) {
return(newRegExp('(\\s|^)'+ cls +'(\\s|$)')).test(ele.className);
}
2.3 removeClass
/**
*
* @desc 為元素移除class
* @param {HTMLElement} ele
* @param {String} cls
*/
varhasClass =require('./hasClass');
functionremoveClass(ele, cls) {
if(hasClass(ele, cls)) {
varreg =newRegExp('(\\s|^)'+ cls +'(\\s|$)');
ele.className = ele.className.replace(reg,' ');
}
}
3.Cookie
3.1 getCookie
/**
*
* @desc 根據(jù)name讀取cookie
* @param ?{String} name
* @return {String}
*/
functiongetCookie(name) {
vararr = document.cookie.replace(/\s/g,"").split(';');
for(vari =0; i < arr.length; i++) {
vartempArr = arr[i].split('=');
if(tempArr[0] == name) {
returndecodeURIComponent(tempArr[1]);
}
}
return'';
}
3.2 removeCookie
varsetCookie =require('./setCookie');
/**
*
* @desc 根據(jù)name刪除cookie
* @param ?{String} name
*/
functionremoveCookie(name) {
// 設(shè)置已過期颗品,系統(tǒng)會(huì)立刻刪除cookie
setCookie(name,'1', -1);
}
3.3 setCookie
/**
*
* @desc ?設(shè)置Cookie
* @param {String} name
* @param {String} value
* @param {Number} days
*/
functionsetCookie(name, value, days) {
vardate =newDate();
date.setDate(date.getDate() + days);
document.cookie = name +'='+ value +';expires='+ date;
}
4.Device
4.1 getExplore
/**
*
* @desc 獲取瀏覽器類型和版本
* @return {String}
*/
functiongetExplore() {
varsys = {},
ua = navigator.userAgent.toLowerCase(),
s;
(s = ua.match(/rv:([\d.]+)\) like gecko/)) ? sys.ie = s[1]:
(s = ua.match(/msie ([\d\.]+)/)) ? sys.ie = s[1] :
(s = ua.match(/edge\/([\d\.]+)/)) ? sys.edge = s[1] :
(s = ua.match(/firefox\/([\d\.]+)/)) ? sys.firefox = s[1] :
(s = ua.match(/(?:opera|opr).([\d\.]+)/)) ? sys.opera = s[1] :
(s = ua.match(/chrome\/([\d\.]+)/)) ? sys.chrome = s[1] :
(s = ua.match(/version\/([\d\.]+).*safari/)) ? sys.safari = s[1] :0;
// 根據(jù)關(guān)系進(jìn)行判斷
if(sys.ie)return('IE: '+ sys.ie)
if(sys.edge)return('EDGE: '+ sys.edge)
if(sys.firefox)return('Firefox: '+ sys.firefox)
if(sys.chrome)return('Chrome: '+ sys.chrome)
if(sys.opera)return('Opera: '+ sys.opera)
if(sys.safari)return('Safari: '+ sys.safari)
return'Unkonwn'
}
4.2 getOS
/**
*
* @desc 獲取操作系統(tǒng)類型
* @return {String}
*/
functiongetOS() {
varuserAgent ='navigator'inwindow &&'userAgent'innavigator && navigator.userAgent.toLowerCase() ||'';
varvendor ='navigator'inwindow &&'vendor'innavigator && navigator.vendor.toLowerCase() ||'';
varappVersion ='navigator'inwindow &&'appVersion'innavigator && navigator.appVersion.toLowerCase() ||'';
if(/mac/i.test(appVersion))return'MacOSX'
if(/win/i.test(appVersion))return'windows'
if(/linux/i.test(appVersion))return'linux'
if(/iphone/i.test(userAgent) ||/ipad/i.test(userAgent) ||/ipod/i.test(userAgent))'ios'
if(/android/i.test(userAgent))return'android'
if(/win/i.test(appVersion) &&/phone/i.test(userAgent))return'windowsPhone'
}
5.Dom
5.1 getScrollTop
/**
*
* @desc 獲取滾動(dòng)條距頂部的距離
*/
functiongetScrollTop() {
return(document.documentElement && document.documentElement.scrollTop) || document.body.scrollTop;
}
5.2 offset
/**
*
* @desc ?獲取一個(gè)元素的距離文檔(document)的位置肯尺,類似jQ中的offset()
* @param {HTMLElement} ele
* @returns { {left: number, top: number} }
*/
functionoffset(ele) {
varpos = {
left:0,
top:0
};
while(ele) {
pos.left += ele.offsetLeft;
pos.top += ele.offsetTop;
ele = ele.offsetParent;
};
returnpos;
}
5.3 scrollTo
vargetScrollTop =require('./getScrollTop');
varsetScrollTop =require('./setScrollTop');
varrequestAnimFrame = (function() {
returnwindow.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
function(callback) {
window.setTimeout(callback,1000/60);
};
})();
/**
*
* @desc ?在${duration}時(shí)間內(nèi),滾動(dòng)條平滑滾動(dòng)到${to}指定位置
* @param {Number} to
* @param {Number} duration
*/
functionscrollTo(to, duration) {
if(duration <0) {
setScrollTop(to);
return
}
vardiff = to - getScrollTop();
if(diff ===0)return
varstep = diff / duration *10;
requestAnimationFrame(
function() {
if(Math.abs(step) >Math.abs(diff)) {
setScrollTop(getScrollTop() + diff);
return;
}
setScrollTop(getScrollTop() + step);
if(diff >0&& getScrollTop() >= to || diff <0&& getScrollTop() <= to) {
return;
}
scrollTo(to, duration -16);
});
}
5.4 setScrollTop
/**
*
* @desc 設(shè)置滾動(dòng)條距頂部的距離
*/
functionsetScrollTop(value) {
window.scrollTo(0, value);
returnvalue;
}
6.Keycode
6.1 getKeyName
varkeyCodeMap = {
8:'Backspace',
9:'Tab',
13:'Enter',
16:'Shift',
17:'Ctrl',
18:'Alt',
19:'Pause',
20:'Caps Lock',
27:'Escape',
32:'Space',
33:'Page Up',
34:'Page Down',
35:'End',
36:'Home',
37:'Left',
38:'Up',
39:'Right',
40:'Down',
42:'Print Screen',
45:'Insert',
46:'Delete',
48:'0',
49:'1',
50:'2',
51:'3',
52:'4',
53:'5',
54:'6',
55:'7',
56:'8',
57:'9',
65:'A',
66:'B',
67:'C',
68:'D',
69:'E',
70:'F',
71:'G',
72:'H',
73:'I',
74:'J',
75:'K',
76:'L',
77:'M',
78:'N',
79:'O',
80:'P',
81:'Q',
82:'R',
83:'S',
84:'T',
85:'U',
86:'V',
87:'W',
88:'X',
89:'Y',
90:'Z',
91:'Windows',
93:'Right Click',
96:'Numpad 0',
97:'Numpad 1',
98:'Numpad 2',
99:'Numpad 3',
100:'Numpad 4',
101:'Numpad 5',
102:'Numpad 6',
103:'Numpad 7',
104:'Numpad 8',
105:'Numpad 9',
106:'Numpad *',
107:'Numpad +',
109:'Numpad -',
110:'Numpad .',
111:'Numpad /',
112:'F1',
113:'F2',
114:'F3',
115:'F4',
116:'F5',
117:'F6',
118:'F7',
119:'F8',
120:'F9',
121:'F10',
122:'F11',
123:'F12',
144:'Num Lock',
145:'Scroll Lock',
182:'My Computer',
183:'My Calculator',
186:';',
187:'=',
188:',',
189:'-',
190:'.',
191:'/',
192:'`',
219:'[',
220:'\\',
221:']',
222:'\''
};
/**
* @desc 根據(jù)keycode獲得鍵名
* @param ?{Number} keycode
* @return {String}
*/
functiongetKeyName(keycode) {
if(keyCodeMap[keycode]) {
returnkeyCodeMap[keycode];
}else{
console.log('Unknow Key(Key Code:'+ keycode +')');
return'';
}
};
7.Object
7.1 deepClone
/**
* @desc 深拷貝躯枢,支持常見類型
* @param {Any} values
*/
functiondeepClone(values) {
varcopy;
// Handle the 3 simple types, and null or undefined
if(null== values ||"object"!=typeofvalues)returnvalues;
// Handle Date
if(valuesinstanceofDate) {
copy =newDate();
copy.setTime(values.getTime());
returncopy;
}
// Handle Array
if(valuesinstanceofArray) {
copy = [];
for(vari =0, len = values.length; i < len; i++) {
copy[i] = deepClone(values[i]);
}
returncopy;
}
// Handle Object
if(valuesinstanceofObject) {
copy = {};
for(varattrinvalues) {
if(values.hasOwnProperty(attr)) copy[attr] = deepClone(values[attr]);
}
returncopy;
}
thrownewError("Unable to copy values! Its type isn't supported.");
}
7.2 isEmptyObject
/**
*
* @desc ? 判斷`obj`是否為空
* @param ?{Object} obj
* @return {Boolean}
*/
functionisEmptyObject(obj) {
if(!obj ||typeofobj !=='object'||Array.isArray(obj))
returnfalse
return!Object.keys(obj).length
}
8.Random
8.1 randomColor
/**
*
* @desc 隨機(jī)生成顏色
* @return {String}
*/
functionrandomColor() {
return'#'+ ('00000'+ (Math.random() *0x1000000<<0).toString(16)).slice(-6);
}
8.2 randomNum
/**
*
* @desc 生成指定范圍隨機(jī)數(shù)
* @param ?{Number} min
* @param ?{Number} max
* @return {Number}
*/
functionrandomNum(min, max) {
returnMath.floor(min +Math.random() * (max - min));
}
9.Regexp
9.1 isEmail
/**
*
* @desc ? 判斷是否為郵箱地址
* @param ?{String} ?str
* @return {Boolean}
*/
functionisEmail(str) {
return/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/.test(str);
}
9.2 isIdCard
/**
*
* @desc ?判斷是否為身份證號(hào)
* @param ?{String|Number} str
* @return {Boolean}
*/
functionisIdCard(str) {
return/^(^[1-9]\d{7}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}$)|(^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])((\d{4})|\d{3}[Xx])$)$/.test(str)
}
9.3 isPhoneNum
/**
*
* @desc ? 判斷是否為手機(jī)號(hào)
* @param ?{String|Number} str
* @return {Boolean}
*/
functionisPhoneNum(str) {
return/^(0|86|17951)?(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$/.test(str)
}
9.4 isUrl
/**
*
* @desc ? 判斷是否為URL地址
* @param ?{String} str
* @return {Boolean}
*/
functionisUrl(str) {
return/[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)/i.test(str);
}
10.String
10.1 digitUppercase
/**
*
* @desc ? 現(xiàn)金額轉(zhuǎn)大寫
* @param ?{Number} n
* @return {String}
*/
functiondigitUppercase(n) {
varfraction = ['角','分'];
vardigit = [
'零','壹','貳','叁','肆',
'伍','陸','柒','捌','玖'
];
varunit = [
['元','萬','億'],
['','拾','佰','仟']
];
varhead = n <0?'欠':'';
n =Math.abs(n);
vars ='';
for(vari =0; i < fraction.length; i++) {
s += (digit[Math.floor(n *10*Math.pow(10, i)) %10] + fraction[i]).replace(/零./,'');
}
s = s ||'整';
n =Math.floor(n);
for(vari =0; i < unit[0].length && n >0; i++) {
varp ='';
for(varj =0; j < unit[1].length && n >0; j++) {
p = digit[n %10] + unit[1][j] + p;
n =Math.floor(n /10);
}
s = p.replace(/(零.)*零$/,'').replace(/^$/,'零') + unit[0][i] + s;
}
returnhead + s.replace(/(零.)*零元/,'元')
.replace(/(零.)+/g,'零')
.replace(/^整$/,'零元整');
};
11.Support
11.1 isSupportWebP
/**
*
* @desc 判斷瀏覽器是否支持webP格式圖片
* @return {Boolean}
*/
functionisSupportWebP() {
return!![].map && document.createElement('canvas').toDataURL('image/webp').indexOf('data:image/webp') ==0;
}
12.Time
12.1 formatPassTime
/**
* @desc ? 格式化${startTime}距現(xiàn)在的已過時(shí)間
* @param ?{Date} startTime
* @return {String}
*/
functionformatPassTime(startTime) {
varcurrentTime =Date.parse(newDate()),
time = currentTime - startTime,
day = parseInt(time / (1000*60*60*24)),
hour = parseInt(time / (1000*60*60)),
min = parseInt(time / (1000*60)),
month = parseInt(day /30),
year = parseInt(month /12);
if(year)returnyear +"年前"
if(month)returnmonth +"個(gè)月前"
if(day)returnday +"天前"
if(hour)returnhour +"小時(shí)前"
if(min)returnmin +"分鐘前"
elsereturn'剛剛'
}
12.2 formatRemainTime
/**
*
* @desc ? 格式化現(xiàn)在距${endTime}的剩余時(shí)間
* @param ?{Date} endTime
* @return {String}
*/
functionformatRemainTime(endTime) {
varstartDate =newDate();//開始時(shí)間
varendDate =newDate(endTime);//結(jié)束時(shí)間
vart = endDate.getTime() - startDate.getTime();//時(shí)間差
vard =0,
h =0,
m =0,
s =0;
if(t >=0) {
d =Math.floor(t /1000/3600/24);
h =Math.floor(t /1000/60/60%24);
m =Math.floor(t /1000/60%60);
s =Math.floor(t /1000%60);
}
returnd +"天 "+ h +"小時(shí) "+ m +"分鐘 "+ s +"秒";
}
13.Url
13.1 parseQueryString
/**
*
* @desc ? url參數(shù)轉(zhuǎn)對(duì)象
* @param ?{String} url ?default: window.location.href
* @return {Object}
*/
functionparseQueryString(url) {
url = url ==null? window.location.href : url
varsearch = url.substring(url.lastIndexOf('?') +1)
if(!search) {
return{}
}
returnJSON.parse('{"'+ decodeURIComponent(search).replace(/"/g,'\\"').replace(/&/g,'","').replace(/=/g,'":"') +'"}')
}
13.2 stringfyQueryString
/**
*
* @desc ? 對(duì)象序列化
* @param ?{Object} obj
* @return {String}
*/
functionstringfyQueryString(obj) {
if(!obj)return'';
varpairs = [];
for(varkeyinobj) {
varvalue = obj[key];
if(valueinstanceofArray) {
for(vari =0; i < value.length; ++i) {
pairs.push(encodeURIComponent(key +'['+ i +']') +'='+ encodeURIComponent(value[i]));
}
continue;
}
pairs.push(encodeURIComponent(key) +'='+ encodeURIComponent(obj[key]));
}
returnpairs.join('&');
}
14.Function
14.1 throttle
/**
* @desc ? 函數(shù)節(jié)流则吟。
* 適用于限制`resize`和`scroll`等函數(shù)的調(diào)用頻率
*
* @param ?{Number} ? ?delay ? ? ? ? ?0 或者更大的毫秒數(shù)。 對(duì)于事件回調(diào)锄蹂,大約100或250毫秒(或更高)的延遲是最有用的氓仲。
* @param ?{Boolean} ? noTrailing ? ? 可選,默認(rèn)為false得糜。
* ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?如果noTrailing為true敬扛,當(dāng)節(jié)流函數(shù)被調(diào)用,每過`delay`毫秒`callback`也將執(zhí)行一次朝抖。
* ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?如果noTrailing為false或者未傳入啥箭,`callback`將在最后一次調(diào)用節(jié)流函數(shù)后再執(zhí)行一次.
* ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?(延遲`delay`毫秒之后,節(jié)流函數(shù)沒有被調(diào)用,內(nèi)部計(jì)數(shù)器會(huì)復(fù)位)
* @param ?{Function} ?callback ? ? ? 延遲毫秒后執(zhí)行的函數(shù)治宣。`this`上下文和所有參數(shù)都是按原樣傳遞的急侥,
* ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?執(zhí)行去節(jié)流功能時(shí),調(diào)用`callback`侮邀。
* @param ?{Boolean} ? debounceMode ? 如果`debounceMode`為true坏怪,`clear`在`delay`ms后執(zhí)行。
* ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?如果debounceMode是false绊茧,`callback`在`delay` ms之后執(zhí)行铝宵。
*
* @return {Function} ?新的節(jié)流函數(shù)
*/
functionthrottle(delay, noTrailing, callback, debounceMode) {
// After wrapper has stopped being called, this timeout ensures that
// `callback` is executed at the proper times in `throttle` and `end`
// debounce modes.
vartimeoutID;
// Keep track of the last time `callback` was executed.
varlastExec =0;
// `noTrailing` defaults to falsy.
if(typeofnoTrailing !=='boolean') {
debounceMode = callback;
callback = noTrailing;
noTrailing =undefined;
}
// The `wrapper` function encapsulates all of the throttling / debouncing
// functionality and when executed will limit the rate at which `callback`
// is executed.
functionwrapper() {
varself=this;
varelapsed =Number(newDate()) - lastExec;
varargs = arguments;
// Execute `callback` and update the `lastExec` timestamp.
functionexec() {
lastExec =Number(newDate());
callback.apply(self, args);
}
// If `debounceMode` is true (at begin) this is used to clear the flag
// to allow future `callback` executions.
functionclear() {
timeoutID =undefined;
}
if(debounceMode && !timeoutID) {
// Since `wrapper` is being called for the first time and
// `debounceMode` is true (at begin), execute `callback`.
exec();
}
// Clear any existing timeout.
if(timeoutID) {
clearTimeout(timeoutID);
}
if(debounceMode ===undefined&& elapsed > delay) {
// In throttle mode, if `delay` time has been exceeded, execute
// `callback`.
exec();
}elseif(noTrailing !==true) {
// In trailing throttle mode, since `delay` time has not been
// exceeded, schedule `callback` to execute `delay` ms after most
// recent execution.
//
// If `debounceMode` is true (at begin), schedule `clear` to execute
// after `delay` ms.
//
// If `debounceMode` is false (at end), schedule `callback` to
// execute after `delay` ms.
timeoutID = setTimeout(debounceMode ? clear :exec, debounceMode ===undefined? delay - elapsed : delay);
}
}
// Return the wrapper function.
returnwrapper;
};
14.2 debounce
/**
* @desc 函數(shù)防抖
* 與throttle不同的是,debounce保證一個(gè)函數(shù)在多少毫秒內(nèi)不再被觸發(fā)按傅,只會(huì)執(zhí)行一次捉超,
* 要么在第一次調(diào)用return的防抖函數(shù)時(shí)執(zhí)行,要么在延遲指定毫秒后調(diào)用唯绍。
* @example 適用場(chǎng)景:如在線編輯的自動(dòng)存儲(chǔ)防抖拼岳。
* @param ?{Number} ? delay ? ? ? ? 0或者更大的毫秒數(shù)。 對(duì)于事件回調(diào)况芒,大約100或250毫秒(或更高)的延遲是最有用的惜纸。
* @param ?{Boolean} ?atBegin ? ? ? 可選,默認(rèn)為false绝骚。
* ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?如果`atBegin`為false或未傳入耐版,回調(diào)函數(shù)則在第一次調(diào)用return的防抖函數(shù)后延遲指定毫秒調(diào)用。
如果`atBegin`為true压汪,回調(diào)函數(shù)則在第一次調(diào)用return的防抖函數(shù)時(shí)直接執(zhí)行
* @param ?{Function} callback ? ? ?延遲毫秒后執(zhí)行的函數(shù)粪牲。`this`上下文和所有參數(shù)都是按原樣傳遞的,
* ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?執(zhí)行去抖動(dòng)功能時(shí)止剖,腺阳,調(diào)用`callback`落君。
*
* @return {Function} 新的防抖函數(shù)。
*/
varthrottle =require('./throttle');
functiondebounce(delay, atBegin, callback) {
returncallback ===undefined? throttle(delay, atBegin,false) : throttle(delay, callback, atBegin !==false);
};
封裝
除了對(duì)上面這些常用函數(shù)進(jìn)行封裝亭引, 最重要的是支持合理化的引入绎速,這里我們使用webpack統(tǒng)一打包成UMD通用模塊規(guī)范,支持webpack焙蚓、RequireJS纹冤、SeaJS等模塊加載器,亦或直接通過標(biāo)簽引入购公。
但這樣萌京,還是不能讓人滿意。因?yàn)橥暾胝麄€(gè)庫君丁,略顯浪費(fèi)枫夺,我們不可能用到所有的函數(shù)。那么绘闷,就支持按需引入吧
1.目錄結(jié)構(gòu)說明
│ ?.babelrc
│ ?.gitignore
│ ?.travis.yml
│ ?LICENSE
│package.json
│ ?README.md
│ ?setCookie.js// 拷貝到根路徑的函數(shù)模塊橡庞,方便按需加載
│ ?setScrollTop.js
│ ?stringfyQueryString.js
│ ? ...
│ ? ...
│
├─min
│ ? ? ?outils.min.js// 所有函數(shù)統(tǒng)一打包生成的全量壓縮包
│
├─script// 本項(xiàng)目開發(fā)腳本目錄
│ ? ? ?build.js// 打包構(gòu)建腳本
│ ? ? ?test.js// 測(cè)試腳本
│ ? ? ?webpack.conf.js// webpack打包配置文件
│
├─src// 源碼目錄
│ ?│ ?index.js// webpack入口文件
│ ?│
│ ?├─array
│ ?│
│ ?├─class
│ ?│
│ ?├─cookie
│ ?│
│ ?├─device
│ ?│
│ ?├─dom
│ ?│
│ ?├─keycode
│ ?│
│ ?├─object
│ ?│
│ ?├─random
│ ?│
│ ?├─regexp
│ ?│
│ ?├─string
│ ?│
│ ?├─support
│ ?│
│ ?├─time
│ ?│
│ ?└─url
│
└─test// 測(cè)試用例目錄
│ ?array.test.js
│class.test.js
│ ?cookie.test.js
│ ?device.test.js
│ ?dom.test.js
│ ?index.html
│ ?keycode.test.js
│object.test.js
│ ?random.test.js
│ ?regexp.test.js
│string.test.js
│ ?support.test.js
│ ?time.test.js
│ ?url.test.js
│
└─_lib// 測(cè)試所用到的第三方庫
mocha.css
mocha.js
power-assert.js
2.構(gòu)建腳本
這里主要說明一下項(xiàng)目中 build.js 的構(gòu)建過程 第一步,構(gòu)建全量壓縮包印蔗,先刪除min目錄中之前的outils.min.js扒最,后通過webpack打包并保存新的壓縮包至min目錄中:
......
......
// 刪除舊的全量壓縮包
rm(path.resolve(rootPath,'min',`${pkg.name}.min.js`), err => {
if(err)throw(err)
webpack(config,function(err, stats) {
if(err)throw(err)
building.stop()
process.stdout.write(stats.toString({
colors:true,
modules:false,
children:false,
chunks:false,
chunkModules:false
}) +'\n\n')
resolve()
console.log(chalk.cyan(' ?Build complete.\n'))
})
})
......
......
第二步,拷貝函數(shù)模塊至根目錄华嘹,先刪除根目錄中之前的函數(shù)模塊吧趣,后拷貝src下面一層目錄的所有js文件至根目錄。這么做的目的是耙厚,拷貝到根路徑强挫,在引入的時(shí)候,直接require('outils/<方法名>')即可薛躬,縮短引入的路徑俯渤,也算是提高點(diǎn)效率。
// 替換模塊文件
......
......
// 先刪除根目錄中之前的函數(shù)模塊
rm('*.js', err => {
if(err)throw(err)
letfolderList = fs.readdirSync(path.resolve(rootPath,'src'))
folderList.forEach((item, index) => {
// 拷貝`src`下面一層目錄的所有`js`文件至根目錄
copy(`src/${item}/*.js`, rootPath,function(err, files) {
if(err)throwerr;
if(index === folderList.length -1) {
console.log(chalk.cyan(' ?Copy complete.\n'))
copying.stop()
}
})
})
})
......
......
3.書寫測(cè)試用例
俗話說型宝,不寫測(cè)試用例的前端不是一個(gè)好程序員八匠。那就不能慫,就是干趴酣。
但是因?yàn)闀r(shí)間關(guān)系梨树,本項(xiàng)目暫時(shí)通過項(xiàng)目中的 test.js ,啟動(dòng)了一個(gè)koa靜態(tài)服務(wù)器岖寞,來加載mocha網(wǎng)頁端的測(cè)試頁面抡四,讓筆者書寫項(xiàng)目時(shí),可以在本地對(duì)函數(shù)功能進(jìn)行測(cè)試仗谆。 但是后續(xù)將使用travis-ci配合Github來做持續(xù)化構(gòu)建床嫌,自動(dòng)發(fā)布到npm跨释。改用karma胸私,mocha厌处,power-assert做單元測(cè)試,使用Coverage測(cè)試覆蓋率岁疼。這一部分阔涉,后續(xù)更新。
這里給大家推薦一個(gè)好用的斷言庫 power-assert 捷绒,這個(gè)庫記住assert(value,[message])一個(gè)API就基本無敵瑰排,從此再也不用擔(dān)心記不住斷言庫的API。
本項(xiàng)目的所有測(cè)試用例都在test目錄下暖侨,大家可以作一定參考椭住。
發(fā)布
首先放到Github托管一下,當(dāng)然你也可以直接fork本項(xiàng)目字逗,然后再加入你自己的函數(shù)京郑。 以筆者項(xiàng)目,舉個(gè)栗子:
1.添加自己的函數(shù)
在src目錄下葫掉,新建分類目錄或者選擇一個(gè)分類些举,在子文件夾中添加函數(shù)模塊文件(建議一個(gè)小功能保存為一個(gè)JS文件)。
/**
*
* @desc ? 判斷是否NaN
* @param ?{Any} value
* @return {Boolean}
*/
functionisNaN(value) {
returnvalue !== value;
};
modules.export= isNaN
然后記得在src/index.js文件中暴露isNaN函數(shù)
2.單元測(cè)試
在test文件新建測(cè)試用例
describe('#isNaN()',function() {
it(`outils.isNaN(NaN) should return true`,function() {
assert(outils.isNaN(NaN))
})
it(`outils.isNaN('value') should return false`,function() {
assert.notEqual(outils.isNaN(NaN))
})
})
然后記得在test/index.html中引入之前創(chuàng)建的測(cè)試用例腳本俭厚。
3.測(cè)試并打包
執(zhí)行npm run test户魏,看所有的測(cè)試用例是否通過。如果沒有問題挪挤,執(zhí)行npm run build構(gòu)建叼丑,之后提交到個(gè)人的 github 倉庫即可。
4.發(fā)布到npm
在 www.npmjs.com 注冊(cè)賬號(hào)扛门,修改本地package.json中的name鸠信、version、author等信息尖飞,最后npm publish就大功告成了症副。
注意:向
npm發(fā)包,要把鏡像源切到 www.npmjs.com 政基,使用cnpm等第三方鏡像源會(huì)報(bào)錯(cuò)贞铣。
使用
1.瀏覽器
直接下載min目錄下的 outils.min.js ,通過標(biāo)簽引入沮明。
varOS = outils.getOS()
注意: 本倉庫代碼會(huì)持續(xù)更新辕坝,如果你需要不同版本的增量壓縮包或源碼,請(qǐng)到 github Release 頁面下載對(duì)應(yīng)版本號(hào)的代碼荐健。
2.Webpack酱畅、RequireJS琳袄、SeaJS等模塊加載器
先使用npm安裝outils。
$ npm install --save-dev outils
// 完整引入
constoutils =require('outils')
constOS = outils.getOS()
推薦使用方法
// 按需引入require('outils/<方法名>')
constgetOS =require('outils/getOS')
constOS = getOS()
當(dāng)然纺酸,你的開發(fā)環(huán)境有babel編譯ES6語法的話窖逗,也可以這樣使用:
importgetOSfrom'outils/getOS'
// 或
import{ getOS }from"outils";
總結(jié)
這里只是簡(jiǎn)單封裝,發(fā)布到npm上餐蔬,省去下次復(fù)制粘貼的功夫碎紊,或者直接Goole的時(shí)間。如果筆者的庫中樊诺,沒有你常用的函數(shù)仗考,或者你有更好的建議,歡迎來本項(xiàng)目的 Github Issues 交流词爬,如果覺得不錯(cuò)秃嗜,歡迎 star本項(xiàng)目。
當(dāng)然顿膨,更好的建議是 fork 本項(xiàng)目锅锨,或者直接新建自己的項(xiàng)目,添加自己想要的虽惭、常用的橡类、記不住的函數(shù),甚至是可以抽象出來的功能芽唇,封裝成自己順手顾画、熟悉的庫。 這樣才能打造出你自己的武器庫匆笤,瞬間提高你的單兵作戰(zhàn)(開發(fā))能力研侣。
工欲善其事必先利其器。有了屬于自己的這把利器炮捧,希望加班也會(huì)變成奢望庶诡。O(∩_∩)O哈哈~