使用plasma 开发谷歌插件

因为公司的所有系统都挂在一个门户网站上,通过token带入登陆。但是在开发的时候就没有办法带过来了,所以开发了一个小插件来解决这个问题。

这边我是使用的plasma + react 来进行开发这个插件

插件组成

chrome 插件通常由以下几部分组成:

  1. manifest.json:相当于插件的 meta 信息,包含插件的名称、版本号、图标、脚本文件名称等,这个文件是每个插件都必须提供的,其他几部分都是可选的。
  2. background script:可以调用全部的 chrome 插件 API,实现跨域请求、网页截屏、弹出 chrome 通知消息等功能。相当于在一个隐藏的浏览器页面内默默运行。
  3. 功能页面:包括点击插件图标弹出的页面(简称 popup)、插件的配置页面(简称 options)。
  4. content script:早期也被称为 injected script,是插件注入到页面的脚本,但是不会体现在页面 DOM 结构里。content script 可以操作 DOM,但是它和页面其他的脚本是隔离的,访问不到其他脚本定义的变量、函数等,相当于运行在单独的沙盒里。content script 可以调用有限的 chrome 插件 API,网络请求收到同源策略限制。

Background Script

在是一个常驻后台,是所有生命周期插件中最长的一个;随着浏览器的打开而打开,随着浏览器的关闭而关闭,将一些一直需要运行,全局代理代码放在backgroud里面。

Content Script

下文简称 content1,它只能使用有限的 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...

官方示例:

github.com/PlasmoHQ/ex...

系统版本需要

  • 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的参数

允许获取的权限

  1. tabs: tab的权限
  2. active:
  3. TabcontextMenus:网页右键菜单,
  4. browser_action 右键菜单
  5. cookies:操作 cookie,和用户登录态相关的功能可能会用到该权限
  6. storage:插件存储,不是 localStorage
  7. 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...

相关推荐
程序员黑豆2 小时前
Java类型推断完全指南:从var到菱形运算符,掌握使用限制与最佳实践
java·前端·ai编程
To_OC2 小时前
踩了个 TS 的坑之后,我终于把 type 和 interface 掰明白了
前端·react.js·typescript
GreenTea3 小时前
深度解读 Anthropic 多智能体报告:更强的模型 ≠ 更好的协调
前端·后端·算法
浮生望3 小时前
前端API工程化:用 Mock 数据与 Axios 配置实现独立于后端的并行开发
前端
万少3 小时前
给 DeepSeek Harness 装个"应用商店":一条命令,595 个插件随你逛
前端·javascript·后端
波波0073 小时前
C# 15 重磅新特性: 带标签 break 与 continue:重新定义嵌套循环控制流
服务器·前端·c#
Brown.alexis4 小时前
es6知识点1-自备使用
前端·ecmascript·es6
小爬的老粉丝5 小时前
JavaScript 纯前端预览 WPS:先识别容器,再路由解析器
前端·javascript·wps
计算机魔术师6 小时前
新兴多智能体系统的模式与问题
前端
用户059540174466 小时前
AI Agent 上下文污染踩坑实录:用 pytest + Redis 揪出 3 类记忆串号 bug,排查 6 小时
前端·css