打造自己的 JavaScript 武器庫

作為戰(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哈哈~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市咆课,隨后出現(xiàn)的幾起案子末誓,更是在濱河造成了極大的恐慌,老刑警劉巖书蚪,帶你破解...
    沈念sama閱讀 221,198評(píng)論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件喇澡,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡殊校,警方通過查閱死者的電腦和手機(jī)晴玖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人呕屎,你說我怎么就攤上這事让簿。” “怎么了秀睛?”我有些...
    開封第一講書人閱讀 167,643評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵尔当,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我琅催,道長(zhǎng)居凶,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,495評(píng)論 1 296
  • 正文 為了忘掉前任藤抡,我火速辦了婚禮,結(jié)果婚禮上抹估,老公的妹妹穿的比我還像新娘缠黍。我一直安慰自己,他們只是感情好药蜻,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,502評(píng)論 6 397
  • 文/花漫 我一把揭開白布瓷式。 她就那樣靜靜地躺著,像睡著了一般语泽。 火紅的嫁衣襯著肌膚如雪贸典。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,156評(píng)論 1 308
  • 那天踱卵,我揣著相機(jī)與錄音廊驼,去河邊找鬼。 笑死惋砂,一個(gè)胖子當(dāng)著我的面吹牛妒挎,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播西饵,決...
    沈念sama閱讀 40,743評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼酝掩,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了眷柔?” 一聲冷哼從身側(cè)響起期虾,我...
    開封第一講書人閱讀 39,659評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎驯嘱,沒想到半個(gè)月后镶苞,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,200評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡宙拉,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,282評(píng)論 3 340
  • 正文 我和宋清朗相戀三年宾尚,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,424評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡煌贴,死狀恐怖御板,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情牛郑,我是刑警寧澤怠肋,帶...
    沈念sama閱讀 36,107評(píng)論 5 349
  • 正文 年R本政府宣布,位于F島的核電站淹朋,受9級(jí)特大地震影響笙各,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜础芍,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,789評(píng)論 3 333
  • 文/蒙蒙 一杈抢、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧仑性,春花似錦惶楼、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至晨汹,卻和暖如春豹储,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背淘这。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評(píng)論 1 271
  • 我被黑心中介騙來泰國打工剥扣, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人慨灭。 一個(gè)月前我還...
    沈念sama閱讀 48,798評(píng)論 3 376
  • 正文 我出身青樓朦乏,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親氧骤。 傳聞我的和親對(duì)象是個(gè)殘疾皇子呻疹,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,435評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容

  • 前言 作為戰(zhàn)斗在業(yè)務(wù)一線的前端,要想少加班筹陵,就要想辦法提高工作效率刽锤。這里提一個(gè)小點(diǎn),我們?cè)跇I(yè)務(wù)開發(fā)過程中朦佩,經(jīng)常會(huì)重...
    農(nóng)林臥夫閱讀 380評(píng)論 0 0
  • 自己打造一把趁手的武器并思,高效率完成前端業(yè)務(wù)代碼。 前言 作為戰(zhàn)斗在業(yè)務(wù)一線的前端语稠,要想少加班宋彼,就要想辦法提高工作效...
    SlaneYang閱讀 296評(píng)論 0 2
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理弄砍,服務(wù)發(fā)現(xiàn),斷路器输涕,智...
    卡卡羅2017閱讀 134,693評(píng)論 18 139
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法音婶,類相關(guān)的語法,內(nèi)部類的語法莱坎,繼承相關(guān)的語法衣式,異常的語法,線程的語...
    子非魚_t_閱讀 31,657評(píng)論 18 399