使用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

下文简称 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...

官方示例:

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...

相关推荐
yuehua_zhang8 分钟前
uni app 写的 小游戏,文字拼图?文字拼写?不知道叫啥
前端·javascript·uni-app
weixin_472183548 分钟前
uniapp使用sm4加密
前端·javascript·uni-app
林涧泣8 分钟前
【Uniapp-Vue3】watch和watchEffect监听的使用
前端·vue.js·uni-app
xinglee17 分钟前
如何实现优雅的删除动画
前端·javascript·面试
远洋录42 分钟前
WebSocket 安全实践:从认证到加密
前端·人工智能·react
贩卖纯净水.1 小时前
JS进阶--JS听到了不灭的回响
java·前端·javascript
番茄小酱0011 小时前
select下拉框,首次进入页面没有显示value的情况
前端·javascript·vue.js·vue
爱上你家菜包1 小时前
我的前端面试笔记(React篇)
前端·react.js
互联网-小阿宇1 小时前
【HTML+CSS+JS+VUE】web前端教程-1-VScode开发者工具快捷键
前端·javascript·html
暗暗那1 小时前
Vue演练场基础知识(六)Props传参+Emits事件
前端·javascript·vue.js