大家好,我是若川。最近組織了源碼共讀活動,感興趣的可以點此加我微信ruochuan12?進群參與,每周大家一起學習200行左右的源碼,共同進步。已進行4個月了,很多小伙伴表示收獲頗豐。
前言
在我們的工作過程中,每當需要排查問題、跑冒煙用例、看測試環境的效果時,經常需要在瀏覽器環境中切換登錄賬號,另外,在開發的過程中,也需要在編輯器 VS Code 里切換代理登錄的賬號。
以政采云的業務開發為例:訪問測試、預發等不同環境要切賬號,切換不同角色身份、不同地理區劃、甚至查看有特殊數據時也要切賬號……這讓我們的工作中充斥了大量的輸入賬號密碼的無效時間,也需要我們額外維護賬號文檔,非常苦惱。
關于在 VS Code 編輯器里快捷切換賬號的工具,我們已經有同學設計開發過,在后續的文章中會向大家展示。

本文將講述一下如何在瀏覽器環境,擴展 Chrome 瀏覽器原有的“記住密碼”功能,實現快捷登錄、隔離賬號信息以及備注標簽等方便使用的功能,同時分享給測試、后端、產品等其他的伙伴,提高大家的效率,希望這次探索能給更多的人帶來啟發。
需求分析
支持賬號錄入和一鍵登錄,節約輸入時間
對賬號進行個性化的 tag 標記,支持增刪改查
隔離不同環境下的賬號,解決混用的干擾
方便查看和數據維護
友好的 UI 界面
最終效果預覽
主要演示一下插件的位置,其中,刪除和置頂是常見功能,就不在這里演示了
一鍵登錄

賬號錄入

Tag 標記和搜索

彈層里的傳送門
傳送門編寫在 popup/index.html
目錄下,用于提供快捷進入不同環境登錄頁的入口,用顏色清晰地區別開測試、預發等環境,以及記錄輔助系統魯班的地址。

前期設計
Chrome 擴展程序
既然是代替用戶進行瀏覽器頁面的登錄,我們當然可以選擇 Chrome Extension (擴展程序)(https://developer.chrome.com/docs/extensions/) 來解決這一難題。
擴展是基于 Web 技術構建的,例如 HTML、JavaScript 和 CSS。它們在單獨的沙盒執行環境中運行,并與 Chrome 瀏覽器交互。
擴展允許您通過使用 API 修改瀏覽器行為和訪問 Web 內容來“擴展”瀏覽器。
Chrome 瀏覽器將會識別包含 manifest.json
文件的目錄為擴展文件,所以我們可以開發一個 Chrome Extension 項目來解決這一問題。
前端技術棧
本次 Chrome 插件選用 React 框架開發,其他開發者也可以根據自己的偏好進行技術選型。
第一版本的插件能力暫時不接入后端,數據都存在本地。
優點
天然實現隔離不同域名環境下的數據,避免了測試、預發等環境混用的缺陷。
如果不手動刪除數據,可支持前端長久保存,并隨時可以在控制臺中查看,分享給其他合作者。
缺點
統一使用者針對不同瀏覽器訪客角色無法實現賬號打通的能力,這一缺陷將在下次接入后端時彌補。
清除本地緩存時,會誤刪數據。
美觀的 UI 選型
由于原政采云登錄頁面是用內部基于 AntD 開發的組件庫,為了保持視覺風格的統一,我選擇了繼續使用我們內部的組件庫,每個團隊也可以根據自己情況選擇自己的組件庫,或者開源的組件庫,如 ant design,element ui 等。
更便捷的交互設計
既然可以訪問 Web 內容,那么最簡便的操作就是不用觸發任何其他的按鈕打開彈層,直接 識別登錄頁面,在原有登錄頁面的空白處中 插入我們的組件 DOM 元素,就可以實現最便捷的操作。我們得到一個登錄賬號列表,不必透出密碼,根據我們自己打的標簽判斷當前需要登錄的賬號,一鍵登錄,代替手動操作。

項目搭建
我們建一個空項目,配置必要的 .babelrc 、.gitignore、webpack.config.js 文件,使得文件可以支持 Babel、Git、Webpack 的正常使用,安裝 Less 以及相關的 loader 方便我們的開發,目錄結構大致如下:
目錄結構
.├──?README.md?├──?package-lock.json├──?package.json├──?src│???├──?assets?#?存放擴展程序的標志圖片│???├──?contentScript?#?對?Web?文件的操作│???├──?manifest.json?#?Chrome?Extension?的清單文件│???└──?popup?#?用于存放彈出層└──?webpack.config.js
清單文件 manifest.json
這里是用來配置擴展程序的基礎信息的文件
name:擴展名,顯示在我的擴展文件中
manifest_version:標記 manifest.json 文件的版本號。從 Chrome 18 版本起, manifest_version 需不小于 2, 并且,由于 manifest_version 為 3 的部分語法僅在 Chorme 88 以上支持,Edge、Firefox等其他瀏覽器都不支持,所以 manifest_version 為 2 是更多擴展程序的選擇。
icons:擴展程序顯示在右上角的圖標,需要配置不同規格的圖片,適應不同的顯示需要。
{"name":?"Account?Saver","description"?:?"zcy?賬號管理小精靈~",?"version":?"1.0",?"manifest_version":?2,?"icons":?{?"16":?"./assets/icon.png",?"48":?"./assets/icon.png","96":?"./assets/icon.png","128":?"./assets/icon.png"},"browser_action":?{"default_icon":?"./assets/icon.png",?//?插件加載在瀏覽器右上角時的圖標"default_title":?"賬號管理小精靈~",?//?hover?圖標的提示文字"default_popup":"/popup.html"?//?默認點擊圖標時彈出的浮層},"permissions":?["tabs","activeTab","storage","notifications"],"background":?{"persistent":?false,"scripts":?["./background.js"]},"content_security_policy":?"script-src?'self'?'unsafe-eval';?object-src?'self'","content_scripts":?[{"matches":?["http://*/*","https://*/*"],"js":?[?//?content?script?文件"/popupListener.js"],"run_at":?"document_idle"}]}
webpack.config.js
如下代碼配置 webpack ,可以幫助我們編譯打包 HTML、JavaScript 和 Less 編寫的樣式文件,打包靜態資源,執行npm run build
獲得打包好的 dist 文件,就可以分享到團隊中了。
const?path?=?require('path');const?webpack?=?require('webpack');const?CopyWebpackPlugin?=?require('copy-webpack-plugin');const?CleanWebpackPlugin?=?require('clean-webpack-plugin');const?HtmlWebpackPlugin?=?require('html-webpack-plugin');module.exports?=?{mode:?'development',context:?path.resolve(__dirname,?'./src'),entry:?{popup:?'./popup/index.js',background:?'./background/index.js',popupListener:?'./contentScript/popupListener.js',},output:?{path:?path.resolve(__dirname,?'./dist'),publicPath:?'/',filename:?'[name].js',},module:?{rules:?[{test:?/\.css$/,use:?['style-loader',?'css-loader'],},{test:?/\.less$/,use:?['style-loader','css-loader','less-loader'],},{test:?/\.(js|jsx)$/,exclude:?/node_modules/,use:?{loader:?'babel-loader',options:?{babelrc:?false,presets:?[//?添加?preset-react?識別?react?代碼?require.resolve('@babel/preset-react'),require.resolve('@babel/preset-env'),{plugins:?['@babel/plugin-proposal-class-properties'],},],cacheDirectory:?true,},},},],},plugins:?[new?HtmlWebpackPlugin({title:?'popup',template:?'./popup/index.html',inject:?true,chunks:?['popup'],filename:?'popup.html',}),new?webpack.HotModuleReplacementPlugin(),new?CleanWebpackPlugin(['./dist/',?'./zip/']),new?CopyWebpackPlugin([{?from:?'assets',?to:?'assets'?},{?from:?'manifest.json',?to:?'manifest.json',?flatten:?true?},]),],};
核心代碼
Content Script
Content Scripts 是運行在Web頁面的上下文的 JavaScript 文件。通過標準的 DOM,Content Scripts 可以操作(讀取并修改)瀏覽器當前訪問的Web頁面的內容,并將信息傳遞給父擴展。
插入浮層
在此我們通過原生 JavaScript 的 createElement()
和 append()
方法向 body 中追加元素,插入浮層。
const?{?domain?}?=?document;const?isZcy?=?domain.indexOf('zcy')?!==?-1;const?userDom?=?document.getElementsByName('username')[0];if?(isZcy?&&?userDom)?{//?域名為政采云域名,且存在?name?=?username?的元素(輸入框)時,在頁面左側插入一個浮層const?body?=?document.getElementsByTagName('body')[0];const?panelWrapper?=?document.createElement('div');ReactDOM.render(<AccountPanel?/>,?panelWrapper);body.append(panelWrapper);}
一鍵登錄
Event()
構造函數,創建一個新的事件對象 Event (https://developer.mozilla.org/zh-CN/docs/Web/API/Event)。該方法 IE 瀏覽器不支持。
event?=?new?Event(typeArg,?eventInit);
// typeArg 是DOMString 類型,表示所創建事件的名稱。
// eventInit 可選,接受以下字段:
// bubbles 是否支持冒泡,cancelable:能否被取消,composed:事件是否會觸發shadow DOM(陰影DOM)根節點之外的事件監聽器
target.dispatchEvent(event)
向一個指定的事件目標派發一個事件,從而觸發監聽函數的執行。該方法返回一個布爾值,只要有一個監聽函數調用了 target.dispatchEvent 則返回 false,否則返回 true。
const?usernameDom?=?document.getElementById('username');const?passwordDom?=?document.getElementById('password');const?{?accountList?}?=?this.state;const?{?username,?password?}?=?accountList.find((item)?=>?item.username?===?handleUsername);//?未來可能會廢棄的寫法//?const?evt?=?document.createEvent('HTMLEvents');//?evt.initEvent('input',?true,?true);//?ie?不支持const?evt?=?new?Event('input',?{?bubbles:?true?});//?將值填入?dom?輸入框里usernameDom.value?=?username;usernameDom.dispatchEvent(evt);passwordDom.value?=?password;passwordDom.dispatchEvent(evt);//?模擬用戶點擊登錄按鈕const?loginBtn?=?document.getElementsByClassName('login-btn')[0];loginBtn.click();
開發輔助
一鍵重載:Extensions Reloader
即使 Webpack 配置了熱更新,插件打包出來的 JavaScript 代碼更新后也是不能熱加載的,我們可以訪問 chrome://extensions/
點擊下圖中的小按鈕重新加載,或者安裝 Extensions Reloader (https://chrome.google.com/webstore/detail/extensions-reloader/fimgfedafeadlieiabdeeaodndnlbhid?hl=zh-CN) 插件,點擊按鈕進行重新加載。


安裝擴展文件
Chrome 允許安裝 Chrome 應用市場和本地文件兩種來源的擴展文件。訪問 chrome://extensions/,打開 開發者模式,點擊 加載已解壓的擴展程序,就可以選中我們本地的文件了,Edge 等瀏覽器也可以用。
下一階段
目標
將數據存儲到后端,避免數據丟失問題。
將數據共享到前端 VSCode 插件上,提供給快速本地代理使用。
新增用戶登錄功能,打通同一使用者訪客身份數據共用問題。
隔離業務小組,避免 Tag 混用、全量賬號查找不便問題。
一鍵打開 Chrome 訪客身份并登錄,同時操作多個賬號,方便測試使用。
設計方向:對插件的使用者增加登錄功能,登錄通過 域賬號-密碼-業務小組 圈定一個范圍,同一個 業務小組共享 測試賬號、綁定的業務標簽、業務小組關聯的應用。前端本地開發時,項目獲得的賬號通過當前應用所屬的業務小組拉取。

E-R 圖設計

參考文檔
Chrome Developers (https://developer.chrome.com/docs/extensions/mv3/getstarted/)
最近組建了一個湖南人的前端交流群,如果你是湖南人可以加我微信?ruochuan12?私信 湖南 拉你進群。
推薦閱讀
整整4個月了,盡全力組織了源碼共讀活動~
我歷時3年才寫了10余篇源碼文章,但收獲了100w+閱讀
老姚淺談:怎么學JavaScript?
我在阿里招前端,該怎么幫你(可進面試群)
·················?若川簡介?·················
你好,我是若川,畢業于江西高校。現在是一名前端開發“工程師”。寫有《學習源碼整體架構系列》10余篇,在知乎、掘金收獲超百萬閱讀。
從2014年起,每年都會寫一篇年度總結,已經寫了7篇,點擊查看年度總結。
同時,最近組織了源碼共讀活動,幫助1000+前端人學會看源碼。公眾號愿景:幫助5年內前端人走向前列。
識別上方二維碼加我微信、拉你進源碼共讀群
今日話題
略。分享、收藏、點贊、在看我的文章就是對我最大的支持~