因为公司的所有系统都挂在一个门户网站上,通过token带入登陆。但是在开发的时候就没有办法带过来了,所以开发了一个小插件来解决这个问题。
这边我是使用的plasma + react 来进行开发这个插件
插件组成
chrome 插件通常由以下几部分组成:
- manifest.json:相当于插件的 meta 信息,包含插件的名称、版本号、图标、脚本文件名称等,这个文件是每个插件都必须提供的,其他几部分都是可选的。
- background script:可以调用全部的 chrome 插件 API,实现跨域请求、网页截屏、弹出 chrome 通知消息等功能。相当于在一个隐藏的浏览器页面内默默运行。
- 功能页面:包括点击插件图标弹出的页面(简称 popup)、插件的配置页面(简称 options)。
- content script:早期也被称为 injected script,是插件注入到页面的脚本,但是不会体现在页面 DOM 结构里。content script 可以操作 DOM,但是它和页面其他的脚本是隔离的,访问不到其他脚本定义的变量、函数等,相当于运行在单独的沙盒里。content script 可以调用有限的 chrome 插件 API,网络请求收到同源策略限制。
Background Script
在是一个常驻后台,是所有生命周期插件中最长的一个;随着浏览器的打开而打开,随着浏览器的关闭而关闭,将一些一直需要运行,全局代理代码放在backgroud里面。
Content Script
下文简称 content[1],它只能使用有限的 chrome API。
由于 content 可以访问 DOM,可以用它来选择、修改、删除、增加网页元素。
但是 content 是运行在隔离的空间(类似沙盒),所以如果需要和页面的其他脚本通信,需要采用 window.postMessage 的方式。
可以获取window下面的方法。
javascript
export const config = {
matches: ["http://127.0.0.1/*","http://localhost/*"],
world: "MAIN"
}
if (window) {
window.addEventListener('beforeunload', function (e) {
if (e.currentTarget.performance.navigation.type === 1) {
// 页面刷新
} else {
e.preventDefault();
}
});
}
plasma
是一个插件开发的框架
docs.plasmo.com/framework#h...
官方示例:
系统版本需要
- Node.js 16.14.x or later
- macOS, Windows, or Linux
- 建议使用 pnpm
如何开始
lua
pnpm create plasmo
# OR
yarn create plasmo
# OR
npm create plasmo
使用pnpm 和 npm 我这边一直请求超时,所以我用的cnpm也是可以运行的
目录结构
文件名 | 描述 |
---|---|
popup.tsx | 该文件导出默认的 React 组件,该组件会渲染到您弹出的页面中。这就是您在扩展弹出窗口上工作所需的全部内容 |
assets | Plasmo 会自动生成一些小图标并将它们从'icon512.png' 文件配置到manifest |
package.json | 常用的 Node.js 项目描述符 |
.prettierrc.cjs | 配置代码格式化 |
.gitignore | git 忽略文件 |
readme.md | READEME 文件 |
tsconfig.json | TypeScript 配置文件 |
开发构建
arduino
// 构建开发环境 有热更新
pnpm dev
生成构建
arduino
// 生成环境构建
pnpm build
构建的包
如何本地加载调试
package.json的配置
perl
{
"name": "login",
"displayName": "Login",
"version": "0.0.1",
"description": "login fjf admin",
"author": "hemoo",
"scripts": {
"dev": "plasmo dev",
"build": "plasmo build",
"test": "plasmo test"
},
"dependencies": {
"@radix-ui/themes": "^2.0.0",
"plasmo": "0.83.0",
"react": "18.2.0",
"react-dom": "18.2.0"
},
"devDependencies": {
"@ianvs/prettier-plugin-sort-imports": "4.1.0",
"@types/chrome": "0.0.245",
"@types/node": "20.5.9",
"@types/react": "18.2.21",
"@types/react-dom": "18.2.7",
"autoprefixer": "^10.4.16",
"postcss": "^8.4.31",
"prettier": "3.0.3",
"tailwindcss": "^3.3.5",
"typescript": "5.2.2"
},
"manifest": {
"host_permissions": [
"https://*/*", // 允许在host的权限
"http://*/*" // 本地要用这个,不要问为什么 问就是泪和脑子的水
],
"permissions": [ // 需要的权限
"cookies", // 需要cookie
"tabs" // 为了获取当前地址需要得到这权限
]
}
}
permissions的参数
允许获取的权限
- tabs: tab的权限
- active:
- TabcontextMenus:网页右键菜单,
- browser_action 右键菜单
- cookies:操作 cookie,和用户登录态相关的功能可能会用到该权限
- storage:插件存储,不是 localStorage
- web_accessible_resources:网页能访问的插件内部资源,比如插件提供 SDK 给页面使用,如 ethereum 的 metamask 钱包插件。或者是修改 DOM 结构用到了插件的样式、图片、字体等资源。
安装Tailwindcss
自动安装
csharp
// 创建的时候就自带tailwindcss
pnpm create plasmo --with-tailwindcss
手动安装
添加依赖项
csharp
// 安装依赖性
pnpm i -D tailwindcss postcss autoprefixer
// 初始化tailwindcss
pnpm tailwindcss init
定义postcss
css
/**
* @type {import('postcss').ProcessOptions}
*/
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {}
}
}
tailwind.config.js
java
/** @type {import('tailwindcss').Config} */
module.exports = {
mode: "jit",
darkMode: "class",
content: ["./**/*.tsx"],
plugins: []
}
style.css
less
@tailwind base;
@tailwind components;
@tailwind utilities;
在扩展中的使用
popup.tsx
javascript
import { useReducer } from "react"
import "./style.css"
function IndexPopup() {
const [count, increase] = useReducer((c) => c + 1, 0)
return (
<button
onClick={() => increase()}
type="button"
className="inline-flex items-center px-5 py-2.5 text-sm font-medium text-center text-white bg-blue-700 rounded-lg hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800">
Count:
<span className="inline-flex items-center justify-center w-8 h-4 ml-2 text-xs font-semibold text-blue-800 bg-blue-200 rounded-full">
{count}
</span>
</button>
)
}
export default IndexPopup
前置知识结束
实现代码,代码很简单就是一行
javascript
import { useState } from "react"
import { Button } from "@radix-ui/themes"
import '@radix-ui/themes/styles.css';
import "./style.css"
function IndexPopup() {
function login() {
chrome.permissions.getAll(
(res)=>{
console.log('res: ', res);
}
)
fetch('host', {
method: 'POST',
headers: {
'content-type': 'application/json'
},
body: JSON.stringify({
"mobile": 13666666666,
"password": "123456",
"code": "123456"
})
}).then(async (response: any) => {
response.json().then((res) => {
if (res.code === 0) {
// 获取当前tab的网址
chrome.tabs.query({
active: true,
currentWindow: true
}, function (tabs) {
if (tabs.length > 0) {
const currentTab = tabs[0];
// 获取当前url的cookies权限
chrome.permissions.request({
permissions: ['cookies'],
origins: [currentTab.url]
}, function (granted) {
console.log('granted: ', granted);
if (granted) {
// 权限已被授予,可以执行访问 Cookie 的操作
const cookie = {
url: currentTab.url,
name: "token",
value: res.data
};
chrome.cookies.set(cookie, function (cookie) {
alert("添加cookie成功")
});
} else {
alert("权限被关闭")
// 用户拒绝了权限请求,无法访问 Cookie
}
});
}
});
}
})
})
}
return (
<div
className="flex flex-col p-2 w-24 border-r-4">
<Button className="cursor-pointer" onClick={login}>登录</Button>
</div>
)
}
export default IndexPopup
参考资料
掘金-字节跳动: juejin.cn/post/711495...
plasmo官方文档: docs.plasmo.com/framework#h...