摘要:老板查崗時(shí),一個(gè)快捷鍵,立即關(guān)閉所有賴皮頁(yè)面。上傳,發(fā)布插件。從零開(kāi)始,開(kāi)發(fā)簡(jiǎn)單的一鍵賴皮插件的上班族都在使用瀏覽器賴皮,所以我們選擇采用插件來(lái)實(shí)現(xiàn)功能。
很多人介紹過(guò)Chrome插件,但必須要說(shuō),插件開(kāi)發(fā)就是擺弄一個(gè)小玩具,第一要素是實(shí)用,其次是好玩。 單純羅列各種功能是非常無(wú)趣的。 所以把一篇舊文拿出來(lái)與大家分享。
人,活著就是為了賴皮。
作為一個(gè)合格的開(kāi)發(fā)人員,把30%的時(shí)間用來(lái)賴皮(上班偷懶)是值得推薦的。
因?yàn)?,如果你工作時(shí)間無(wú)法賴皮,并不能說(shuō)明你工作認(rèn)真,只能說(shuō)明你的工作自動(dòng)化程度不夠。
賴皮狗,一般會(huì)在上班時(shí)間瀏覽:SGamer論壇、虎撲論壇、斗魚(yú)、BiliBili這一類的網(wǎng)站。
但在瀏覽過(guò)程中會(huì)遇到以下痛點(diǎn):
老板查崗,貼子或直播間打開(kāi)太多,不能及時(shí)關(guān)閉全部的賴皮站點(diǎn)。
老板走了重新賴皮,不記得之前打開(kāi)的貼子或直播間在哪里。
每次在瀏覽器里輸入賴皮網(wǎng)址,打字真的很麻煩!
工作時(shí)打開(kāi)了太多標(biāo)簽頁(yè),休息時(shí)很難找到想要的賴皮頁(yè)面。
所以,我們需要:
簡(jiǎn)單的一鍵賴皮插件需要怎樣的體驗(yàn):打開(kāi)瀏覽器后,一個(gè)快捷鍵,立即打開(kāi)賴皮頁(yè)面,喜滋滋開(kāi)始賴皮的一天。
老板/leader查崗時(shí),一個(gè)快捷鍵,立即關(guān)閉所有賴皮頁(yè)面。
老板走后,或工作一段時(shí)間后,一個(gè)快捷鍵,立即打開(kāi)原來(lái)的賴皮貼子和直播間。
簡(jiǎn)單的一鍵賴皮插件功能:包含簡(jiǎn)單的一鍵賴皮站點(diǎn)功能
能自定義配置賴皮網(wǎng)站。
上傳Google,發(fā)布插件。
從零開(kāi)始,開(kāi)發(fā)簡(jiǎn)單的一鍵賴皮插件90%的上班族都在使用Chrome瀏覽器賴皮,所以我們選擇采用Chrome插件來(lái)實(shí)現(xiàn)功能。
Chrome插件沒(méi)什么大不了的,依然還是采用HTMLCSSJS的組合。
在這里,我將手把手帶你從零開(kāi)始制作插件。
mainfest.json就像node.js的package.json一樣,每一個(gè)插件必須有一個(gè)manifest.json,作為最初配置文件。
我們創(chuàng)建一個(gè)新的項(xiàng)目,在根目錄下創(chuàng)建manifest.json,填入以下代碼
==mainfest.json==
{ "name": "上班一鍵賴皮工具", "version": "0.1", "description": "windows:按Alt+S開(kāi)啟、關(guān)閉賴皮網(wǎng)站 mac:按Control+S開(kāi)啟、關(guān)閉賴皮網(wǎng)站", "manifest_version": 2 }
解釋一下:
name: 插件的名字
version: 插件的版本
description: 插件簡(jiǎn)介欄
manifest_version: 這個(gè)是寫死的,每個(gè)文件必須有
接下來(lái)請(qǐng)右鍵保存虎撲logo到根目錄,名字還是如apple-touch-icon.png就行吧。
也可以點(diǎn)擊
https://b1.hoopchina.com.cn/c... 保存圖片
修改mainfest.json,設(shè)置四個(gè)尺寸的icon都變成apple-touch-icon.png,以及插件欄也顯示apple-touch-icon.png。
==mainfest.json==
{ "name": "上班一鍵賴皮工具", "version": "0.1", "description": "windows:按Alt+S開(kāi)啟、關(guān)閉賴皮網(wǎng)站 mac:按Control+S開(kāi)啟、關(guān)閉賴皮網(wǎng)站", "icons": { "16": "apple-touch-icon.png", "32": "apple-touch-icon.png", "48": "apple-touch-icon.png", "128": "apple-touch-icon.png" }, "browser_action": { "default_icon": "apple-touch-icon.png", "default_popup": "popup.html" }, "commands": { "toggle-tags": { "suggested_key": { "default": "Alt+S", "mac": "MacCtrl+S" }, "description": "Toggle Tags" } }, "manifest_version": 2 }
解釋一下:
icons: 配置了顯示在不同地方的圖標(biāo)
browser_action: 即右上角插件,browser_action > default_icon即右上角插件圖標(biāo)
commands:一般用于快捷鍵命令。 commands > toggle-tags > suggested_key之下,設(shè)置了快捷鍵,只要按下快捷鍵,即會(huì)向Chrome就會(huì)向后臺(tái)發(fā)布一個(gè)command,值為toggle-tags。
在windows環(huán)境下,我們將快捷鍵設(shè)置成Alt+S,在mac環(huán)境下,我們將快捷鍵設(shè)置成Control+S,配置文件中寫作MacCtrl+S
現(xiàn)在我們有了命令,就需要后臺(tái)腳本接收,在mainfest.json中添加后臺(tái)腳本:
==mainfest.json==
... "background": { "scripts": [ "background.js" ] } ...
并在根目錄下創(chuàng)建background.js.
==background.js==
chrome.commands.onCommand.addListener(function(command) { alert(command) console.log(command) })
現(xiàn)在我們的目錄結(jié)構(gòu)如下:
├── manifest.json └── background.js └── sgamers.png在chrome內(nèi)加載插件
點(diǎn)擊Chorme右上角的三個(gè)點(diǎn)按鈕...> More Tools > Extensions
在右上角把Developer mode打開(kāi)
再找到頂部的LOAD UNPACKED,把項(xiàng)目的根目錄導(dǎo)入進(jìn)去
項(xiàng)目導(dǎo)入后會(huì)出現(xiàn)一個(gè)新的卡片,是這個(gè)效果:
這時(shí),你如果在Windows中按下Alt+S就會(huì)彈出消息,消息為toggle-tags,正好是我們?cè)趍ainfest.json中定義好的。
同時(shí)我們可以點(diǎn)擊上圖藍(lán)色鍵頭所指示的background page,打開(kāi)一個(gè)調(diào)試工具,可以看到toggle-tags的輸出。
我們?cè)谥蟊镜鼐庉嫴寮螅梢园椿疑I頭所指的刷新,新功能就能立即刷新加載了!
有了這些工作,意味著你可以進(jìn)入下一步了!
標(biāo)簽頁(yè)配置一鍵打開(kāi)/關(guān)閉賴皮網(wǎng)站,實(shí)現(xiàn)原理其實(shí)就是chrome的標(biāo)簽頁(yè)功能。
標(biāo)簽頁(yè)功能訪問(wèn)需要在manifest.json中添加權(quán)限
==mainfest.json==
... "permissions": ["tabs"] ...
接下來(lái),我們寫一下background.js實(shí)現(xiàn)通過(guò)快捷鍵(windows的Alt+S,或mac的Ctrl+S)創(chuàng)建新的主頁(yè):
==background.js==
// 輸入你想要的網(wǎng)站主頁(yè) const MainPageUrl = "http://https://bbs.hupu.com/all-gambia" chrome.commands.onCommand.addListener(function (command) { if (command === "toggle-tags") { chrome.tabs.create({"url": MainPageUrl, "selected": true}); } })
其實(shí)實(shí)現(xiàn)很簡(jiǎn)單,就是調(diào)用chrome.tabs.create接口,就創(chuàng)建了一個(gè)新的標(biāo)簽頁(yè)。
刷新一下插件,再試一試快捷鍵功能————是不是已經(jīng)能控制瀏覽器彈出標(biāo)簽 頁(yè)了!
稍顯復(fù)雜的地方是標(biāo)簽頁(yè)isOpen狀態(tài)的處理,
下圖主要關(guān)注isOpen狀態(tài)的變化,以及tabCache的值變化。
具體后臺(tái)邏輯如下,可以跟據(jù)備注、對(duì)照流程圖進(jìn)行理解:
//初始化isOpen和tabCache狀態(tài) let isOpen = false let tabCache = [] //新標(biāo)簽打開(kāi)的主頁(yè) const mainPageUrl = "https://bbs.hupu.com/all-gambia" //四個(gè)賴皮網(wǎng)站的正則匹配表達(dá)式 const myPattern = "sgamer.com/|douyu.com|hupu.com|bilibili.com" //當(dāng)前頁(yè)面的Url let currentPageUrl = "" /** * 開(kāi)始步驟: 判斷isOpen狀態(tài) * 情形一:isOpen為true,則移除頁(yè)面 * 情形二:isOpen為false,則重載頁(yè)面 */ chrome.commands.onCommand.addListener(function (command) { if (command === "toggle-tags") { if (isOpen) { //情形一:isOpen為true removePages(myPattern) //情形二:isOpen為false } else { reloadPages(myPattern, mainPageUrl) } } }) /** * 情形1:移除頁(yè)面 * 1、清空tabCache緩存 * 2、關(guān)閉所有域名內(nèi)標(biāo)簽 * 3、將關(guān)閉的標(biāo)簽存入tabCache緩存數(shù)組 * 4、將isOpen狀態(tài)改為false */ function removePages(patternStr) { tabCache = [] chrome.tabs.query({active: true}, function (tab) { currentPageUrl = tab[0].url }) let pattern = new RegExp(patternStr) walkEveryTab(function (tab) { if (pattern.test(tab.url)) { chrome.tabs.remove(tab.id,function(){ tabCache.push(tab.url) }) } },function(){ isOpen = false }) } /** * 情形2:重載頁(yè)面 * 判斷有沒(méi)有緩存: * 情形2-1無(wú)緩存:開(kāi)啟新標(biāo)簽或定位到域名內(nèi)的標(biāo)簽 * 情形2-2有緩存:打開(kāi)全部緩存內(nèi)的頁(yè)面 */ function reloadPages(patternStr, mainPageUrl) { if (tabCache.length === 0) { focusOrCreateTab(patternStr, mainPageUrl) } else { openAllCachedTab(tabCache) } } /** * 情形2-1:開(kāi)啟新標(biāo)簽或定位到域名內(nèi)的標(biāo)簽 * 1、遍歷全部標(biāo)簽,記錄符合域名的標(biāo)簽的url,以及最后一個(gè)標(biāo)簽頁(yè) * 2、如果沒(méi)有符合域名的標(biāo)簽,則創(chuàng)建主頁(yè),并將isOpen狀態(tài)改為true * 3、如果有符合域名的標(biāo)簽: * 1、獲取當(dāng)前的頁(yè)面url * 2、如果當(dāng)前頁(yè)面url不符合域名,則定位到這個(gè)標(biāo)簽頁(yè),將isOpen狀態(tài)改為true * 3、如果當(dāng)前頁(yè)面url符合域名,則關(guān)閉所有標(biāo)簽頁(yè)(按情形1處理),將isOpen狀態(tài)改為false */ function focusOrCreateTab(patternStr, url) { let pattern = new RegExp(patternStr) let theTabs = [] let theLastTab = null walkEveryTab(function (tab) { if (pattern.test(tab.url)) { theTabs.push(tab.url) theLastTab = tab } }, function () { if (theTabs.length > 0) { chrome.tabs.query({active: true}, function (tab) { let currentUrl = tab[0].url if (theTabs.indexOf(currentUrl) > -1) { removePages(patternStr) isOpen = false } else { chrome.tabs.update(theLastTab.id, {"selected": true}); isOpen = true } }) } else { chrome.tabs.create({"url": url, "selected": true}); isOpen = true } } ) } /** * 情形2-2: * 1、把tabCache所有標(biāo)簽頁(yè)重新打開(kāi) * 2、將isOpen狀態(tài)改為true */ function openAllCachedTab(tabCache) { let focusTab = null tabCache.forEach(function (url, index) { chrome.tabs.create({"url": url}, function (tab) { if (tab.url === currentPageUrl) { focusTab = tab.id } if (index === tabCache.length-1 - 1) { if (focusTab) { chrome.tabs.update(focusTab, {"selected": true},function(){ }); } } }) }) isOpen = true } /** * * @param callback * @param lastCallback * 包裝一下遍歷全部標(biāo)簽的函數(shù),創(chuàng)建兩個(gè)回調(diào)。 * 一個(gè)回調(diào)是每一次遍歷的過(guò)程中就執(zhí)行一遍。 * 一個(gè)回調(diào)是全部遍歷完后執(zhí)行一遍。 */ function walkEveryTab(callback, lastCallback) { chrome.windows.getAll({"populate": true}, function (windows) { for (let i in windows) { let tabs = windows[i].tabs; for (let j in tabs) { let tab = tabs[j]; callback(tab) } } if(lastCallback) lastCallback() }) }上傳與發(fā)布插件
我們需要在Chrome的開(kāi)發(fā)者中心發(fā)布插件,進(jìn)入 Developer Dashboard
好了,一個(gè)簡(jiǎn)單易用的上班賴皮插件做好了!在調(diào)試模式下,你可以用ctrl+s來(lái)快捷尋找、打開(kāi)、關(guān)閉、重新打開(kāi)賴皮頁(yè)面。隨時(shí)隨地、全方位賴皮,從容面對(duì)老板查崗。可配置的高級(jí)賴皮插件
現(xiàn)在我希望我的插件都可以隨時(shí)配置站點(diǎn):
那么就需要用到chrome.storage了。
你需要打開(kāi)storage權(quán)限:
manifest.json內(nèi)添加
... "permissions": [ "tabs","storage" ], ...
然后使用
chrome.storage.local.set({ "value1":theValue1, "value2",theValue2 })
這種形式來(lái)存放storage。
這后使用
chrome.storage.local.get(["value1"],(res)=>{ const theValue1 = res.value1 })
這樣得到存放的value值。
開(kāi)始改寫background.js我們把mainPageUrl和myPattern改成從storage中獲取。
const INIT_SITES_LIST = ["bilibili.com","douyu.com","sgamer.com","hupu.com"] const INIT_MAIN_PAGE = "https://bbs.hupu.com/all-gambia" // 在安裝時(shí)即設(shè)置好storage chrome.runtime.onInstalled.addListener(function() { chrome.storage.local.set({ sites: INIT_SITES_LIST, mainPage:INIT_MAIN_PAGE }) }); //初始化isOpen和tabCache狀態(tài) let isOpen = false let tabCache = [] let currentPageUrl = "" /** * 開(kāi)始步驟: 判斷isOpen狀態(tài) * 情形一:isOpen為true,則移除頁(yè)面 * 情形二:isOpen為false,則重載頁(yè)面 */ chrome.commands.onCommand.addListener(function (command) { if (command === "toggle-tags") { chrome.storage.local.get(["sites","mainPage"],function(res){ let sites = res.sites let mainPageUrl = res.mainPage let myPattern = sites.map(item=>item.replace(".",".")).join("|") console.log(myPattern) if (isOpen) { //情形一:isOpen為true removePages(myPattern) //情形二:isOpen為false } else { reloadPages(myPattern, mainPageUrl) } }) } }) // ======================== 下面的部分不需要改動(dòng),看到這里就夠了) /** * 情形1:移除頁(yè)面 * 1、清空tabCache緩存 * 2、關(guān)閉所有域名內(nèi)標(biāo)簽 * 3、將關(guān)閉的標(biāo)簽存入tabCache緩存數(shù)組 * 4、將isOpen狀態(tài)改為false */ function removePages(patternStr) { tabCache = [] chrome.tabs.query({active: true}, function (tab) { currentPageUrl = tab[0].url }) let pattern = new RegExp(patternStr) walkEveryTab(function (tab) { if (pattern.test(tab.url)) { chrome.tabs.remove(tab.id,function(){ tabCache.push(tab.url) }) } },function(){ isOpen = false }) } /** * 情形2:重載頁(yè)面 * 判斷有沒(méi)有緩存: * 情形2-1無(wú)緩存:開(kāi)啟新標(biāo)簽或定位到域名內(nèi)的標(biāo)簽 * 情形2-2有緩存:打開(kāi)全部緩存內(nèi)的頁(yè)面 */ function reloadPages(patternStr, mainPageUrl) { if (tabCache.length === 0) { focusOrCreateTab(patternStr, mainPageUrl) } else { openAllCachedTab(tabCache) } } /** * 情形2-1:開(kāi)啟新標(biāo)簽或定位到域名內(nèi)的標(biāo)簽 * 1、遍歷全部標(biāo)簽,記錄符合域名的標(biāo)簽的url,以及最后一個(gè)標(biāo)簽頁(yè) * 2、如果沒(méi)有符合域名的標(biāo)簽,則創(chuàng)建主頁(yè),并將isOpen狀態(tài)改為true * 3、如果有符合域名的標(biāo)簽: * 1、獲取當(dāng)前的頁(yè)面url * 2、如果當(dāng)前頁(yè)面url不符合域名,則定位到這個(gè)標(biāo)簽頁(yè),將isOpen狀態(tài)改為true * 3、如果當(dāng)前頁(yè)面url符合域名,則關(guān)閉所有標(biāo)簽頁(yè)(按情形1處理),將isOpen狀態(tài)改為false */ function focusOrCreateTab(patternStr, url) { let pattern = new RegExp(patternStr) let theTabs = [] let theLastTab = null walkEveryTab(function (tab) { if (pattern.test(tab.url)) { theTabs.push(tab.url) theLastTab = tab } }, function () { if (theTabs.length > 0) { chrome.tabs.query({active: true}, function (tab) { let currentUrl = tab[0].url if (theTabs.indexOf(currentUrl) > -1) { removePages(patternStr) isOpen = false } else { chrome.tabs.update(theLastTab.id, {"selected": true}); isOpen = true } }) } else { chrome.tabs.create({"url": url, "selected": true}); isOpen = true } } ) } /** * 情形2-2: * 1、把tabCache所有標(biāo)簽頁(yè)重新打開(kāi) * 2、將isOpen狀態(tài)改為true */ function openAllCachedTab(tabCache) { let focusTab = null tabCache.forEach(function (url, index) { chrome.tabs.create({"url": url}, function (tab) { if (tab.url === currentPageUrl) { focusTab = tab.id } if (index === tabCache.length-1 - 1) { if (focusTab) { chrome.tabs.update(focusTab, {"selected": true},function(){ }); } } }) }) isOpen = true } /** * * @param callback * @param lastCallback * 包裝一下遍歷全部標(biāo)簽的函數(shù),創(chuàng)建兩個(gè)回調(diào)。 * 一個(gè)回調(diào)是每一次遍歷的過(guò)程中就執(zhí)行一遍。 * 一個(gè)回調(diào)是全部遍歷完后執(zhí)行一遍。 */ function walkEveryTab(callback, lastCallback) { chrome.windows.getAll({"populate": true}, function (windows) { for (let i in windows) { let tabs = windows[i].tabs; for (let j in tabs) { let tab = tabs[j]; callback(tab) } } if(lastCallback) lastCallback() }) }
那么我們可以寫一個(gè)popup頁(yè)面,如果點(diǎn)擊圖標(biāo)就會(huì)顯示,如圖:
下面我們完善一下popup.html和popup.css和pupup.js頁(yè)面
所有js文件都可以直接調(diào)用chrome.storage.local.get
只需要特別注意一下js文件的chrome.storage調(diào)用部分
其它的拷貝即可,我們不是來(lái)學(xué)頁(yè)面布局的
popup.html
常用網(wǎng)站配置頁(yè)面 常用賴皮站點(diǎn)域名
我的賴皮主頁(yè)
按Alt+S快速開(kāi)啟/關(guān)閉賴皮站點(diǎn)
popup.css
* { margin: 0; padding: 0; color:#6a6f77; } input, button, select, textarea { outline: none; -webkit-appearance: none; border-radius: 0; border: none; } input:focus{ list-style: none; box-shadow: none; } ol, ul { list-style: none; } li{ margin: 5px 0; } .container { width: 200px; padding: 10px; } .container h2{ margin: 10px; text-align: center; display: block; } .lapi-content li{ transition: opacity 1s; } .site{ cursor: pointer; color: #00b0ff; } .add, .main-page{ box-sizing: border-box; text-align:center; font-size:14px; /*height:27px;*/ border-radius:3px; border:1px solid #c8cccf; color:#6a6f77; outline:0; padding:0 10px; text-decoration:none; width: 170px; } #main-page{ font-size: 12px; text-align: left; width: 166px; margin: 0; padding: 2px; } .add{ height: 27px; } .main-page{ width: 170px; outline: none; resize: none; } .main-page-inactive{ width: 160px; line-break: auto; word-break: break-word; overflow: hidden; display: inline-block; cursor: pointer; color: #00b0ff; margin: 3px; } .button{ font-size: 16px; /*border: 1px solid #c8cccf;*/ color: #c8cccf; /*border: none;*/ padding: 0 4px 1px 3px; border-radius: 3px; } .close-button{ transition: all 1s; } .button:hover{ background: #E27575; color: #FFF; } .add-button{ transition:all 1s; font-size: 20px; padding: 0 6px 1px 5px; } .change-button{ position: absolute; transition:all 1s; font-size: 20px; padding: 0 6px 1px 5px; } .change-button:hover{ background: #f9a825; color: #FFF; } #change-check{ color: #f9a825; } #change-check:hover{ color: #fff; } .add-button:hover{ background: #B8DDFF; color: #FFF; } .submit{ transition: all 1s; margin: 10px; padding: 5px 10px; font-size: 16px; border-radius: 4px; background: #B8DDFF; border: 1px solid #B8DDFF; color: #FFF; } .submit:hover{ border: 1px solid #B8DDFF; background: #fff; color: #B8DDFF; } .fade{ opacity: 0; } .add-wrong,.add-wrong:focus{ border: #e91e63 1px solid; box-shadow:0 0 5px rgba(233,30,99,.3); } .lapi-tip{ margin-top: 20px; border-top: 1px solid #c8cccf; padding-top: 8px; color: #c8cccf; text-align: center; } .lapi-key{ color: #B8DDFF; }
重點(diǎn)關(guān)注:chrome.storage部分
popup.js
let sites = [] let mainPage = "" const isMac = /Macintosh/.test(navigator.userAgent) let $lapiKey = $(".lapi-key") isMac? $lapiKey.text("Control+S"):$lapiKey.text("Alt+S") // 從storage中取出site和mainPage字段,并設(shè)置在頁(yè)面上。 chrome.storage.local.get(["sites","mainPage"], function (res) { if (res.sites) { sites = res.sites mainPage = res.mainPage sites.forEach(function (item) { let appendEl = "
好了,一個(gè)優(yōu)雅的賴皮插件就做好了,大家可以查看
https://github.com/wanthering...
祝大家上班賴得開(kāi)心。 別老躲廁所玩手機(jī)了,不健康!賴皮插件用起來(lái)!
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/100067.html
摘要:引子的引子第一次寫博客,本文是寫給和我一樣的小白的,大牛請(qǐng)謹(jǐn)慎食用,歡迎拍磚。是稱霸全平臺(tái)的偉大的一步,當(dāng)然對(duì)于我們普通前端小白并不太關(guān)心會(huì)怎么發(fā)展,我們只想高高興興上班平平安安回家,所謂老板和我談理想,我說(shuō)我的理想是不上班。 引子的引子 第一次寫博客,本文是寫給和我一樣的小白的,大牛請(qǐng)謹(jǐn)慎食用,歡迎拍磚。 引子 隨著Node.js 4.0 的發(fā)布,這次是nodejs和iojs合并后的...
摘要:引子的引子第一次寫博客,本文是寫給和我一樣的小白的,大牛請(qǐng)謹(jǐn)慎食用,歡迎拍磚。是稱霸全平臺(tái)的偉大的一步,當(dāng)然對(duì)于我們普通前端小白并不太關(guān)心會(huì)怎么發(fā)展,我們只想高高興興上班平平安安回家,所謂老板和我談理想,我說(shuō)我的理想是不上班。 引子的引子 第一次寫博客,本文是寫給和我一樣的小白的,大牛請(qǐng)謹(jǐn)慎食用,歡迎拍磚。 引子 隨著Node.js 4.0 的發(fā)布,這次是nodejs和iojs合并后的...
摘要:更多資源請(qǐng)文章轉(zhuǎn)自月份前端資源分享的作用數(shù)組元素隨機(jī)化排序算法實(shí)現(xiàn)學(xué)習(xí)筆記數(shù)組隨機(jī)排序個(gè)變態(tài)題解析上個(gè)變態(tài)題解析下中的數(shù)字前端開(kāi)發(fā)筆記本過(guò)目不忘正則表達(dá)式聊一聊前端存儲(chǔ)那些事兒一鍵分享到各種寫給剛?cè)腴T的前端工程師的前后端交互指南物聯(lián)網(wǎng)世界的 更多資源請(qǐng)Star:https://github.com/maidishike... 文章轉(zhuǎn)自:https://github.com/jsfr...
閱讀 1995·2023-04-26 01:59
閱讀 3295·2021-10-11 11:07
閱讀 3332·2021-09-22 15:43
閱讀 3414·2021-09-02 15:21
閱讀 2607·2021-09-01 10:49
閱讀 929·2019-08-29 15:15
閱讀 3121·2019-08-29 13:59
閱讀 2858·2019-08-26 13:36