大家好,我是若川。最近组织了源码共读活动,感兴趣的可以点此加我微信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年内前端人走向前列。
识别上方二维码加我微信、拉你进源码共读群
今日话题
略。分享、收藏、点赞、在看我的文章就是对我最大的支持~