记录一下本人从零开始构建一个插件的过程~
我这个插件的逻辑次要是向指标页面注入一个icon,

1. 创立一个react我的项目

首先官网更新,不再提供全局装置的create-react-app,应用

npx create-react-app@latest yourProjectName --template typescript --use-npm

装置胜利后,将不须要的文件给删除掉,更改manifest.json这个配置文件,过后应用的是V2版本,后续将其更新为V3版本,次要是chrome浏览器在2023年会将V2版本的给废除掉,:(
我的配置文件如下,其中具体的介绍能够查阅这篇文章:chrome插件开发攻略

{    // 清单文件的版本,这个必须写,而且必须是2    "manifest_version": 2,    // 插件的名称    "name": "demo",    // 插件的版本    "version": "1.0.0",    // 插件形容    "description": "简略的Chrome扩大demo",    // 图标,个别偷懒全副用一个尺寸的也没问题    "icons":    {        "16": "img/icon.png",        "48": "img/icon.png",        "128": "img/icon.png"    },    // 会始终常驻的后盾JS或后盾页面    "background":    {        // 2种指定形式,如果指定JS,那么会主动生成一个背景页        //"page": "background.html"        "scripts": ["background.js"]    },    // 须要间接注入页面的JS    "content_scripts":     [        {            //"matches": ["http://*/*", "https://*/*"],            // "<all_urls>" 示意匹配所有地址            "matches": ["<all_urls>"],            // 多个JS按程序注入            "js": ["content-script.js",icon.js],            // JS的注入能够轻易一点,然而CSS的留神就要千万小心了,因为一不小心就可能影响全局款式            "css": ["icon.css"],            // 代码注入的工夫,可选值: "document_start", "document_end", or "document_idle",最初一个示意页面闲暇时,默认document_idle            "run_at": "document_end"        },    ],    // 权限申请    "permissions":    [        "contextMenus", // 右键菜单        "tabs", // 标签        "activeTab",        "http://*/*", // 能够通过executeScript或者insertCSS拜访的网站        "https://*/*" // 能够通过executeScript或者insertCSS拜访的网站    ],    "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'",    "externally_connectable":{      "matches":[        "*://*.italent-inc.cn/*",        "*://*.italent.link/*",        "*://*.italent.cn/*"      ]  }}

先大抵晓得有这么些配置属性就行,上面一步一步的细化
首先梳理一下backgound与content之间的关系:

  • backgound,它随着浏览器的关上而关上,随着浏览器的敞开而敞开,所以通常把须要始终运行的、启动就运行的、全局的代码放在background外面,而且它能够有限跨域
  • Chrome插件中向页面注入脚本的一种模式,特定无法访问页面中的JS,尽管它能够操作DOM,然而DOM却不能调用它,侥幸的是能够通过注入脚本的模式实现向页面增加dom元素
  • 因为content scripts运行在Web页面的上下文中,属于Web页面的组成部分,而不是Google Chrome扩大程序。然而content scripts又往往须要与Google Chrome扩大程序的其余局部通信以共享数据,这通过消息传递实现

    2. 创立backgound.js文件

    我的逻辑就次要是监听页面刷新,就在content去掉用我的办法

//监听页面刷新chrome.tabs.onUpdated.addListener(async function (tabId, message, option) {  //避免加载中和加载实现执行2次  if(message.status !== 'complete' || option.status !== 'complete'){    return  }  chrome.tabs.sendMessage(tabId,{    type:"initIcon",    taburl:option.url // 配置信息须要减少activeTab的权限  })})

3. 创立content.js

//content承受bg音讯chrome.runtime.onMessage.addListener(async function (msg) {  if (msg.type === "initIcon") {    const urlStorage = window.localStorage.getItem('iframeUrl')    // 以下是我的业务逻辑,就不多形容了    if(urlStorage){      //间接应用域名判断      if(isShow()){        render()      }else{        console.log('remove');        window.localStorage.removeItem('iframeUrl')        destroy()      }    }else{      destroy()    }  }});

值得一提的就是render函数就是下面我提到的通过注入脚本的模式实现向页面增加dom元素

const render = function (){  const url = window.localStorage.getItem('iframeUrl')  const script = document.createElement('script');  script.src = chrome.runtime.getURL('icon.js');  document.documentElement.appendChild(script);}

这里也看到了我引入icon.js这个脚本,

const App = () => {    // 省略掉不重要的  return (    <div className={"lm-chrome-guide-icon"} onClick={()=>setDrawerVisible(true)} onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave}>      <img src={imgSrcObj[imgSrc]}/>    </div>  );};// 在Chrome扩大程序中将React Component注入页面const mountNode = document.createElement("div");mountNode.id = 'mapNode'document.body.appendChild(mountNode);ReactDom.render(<App />, mountNode);

值得一提的是,通过这个办法注入的icon,会在主页面的底部,能够在chrome控制台查看搜寻相干内容查看是否注入胜利,注入胜利后却没有显示,应用了position: fixed;
z-index: 9999这两行代码来显示。
PS我感觉初学chrome插件最难的就是各种门路....:(
此时我的我的项目 目录构造是这样的

值得一提的是 我在package.json加了这条命令

"scripts": {    "start": "react-scripts start",    "build": "react-scripts build",    "test": "react-scripts test",    "eject": "react-scripts eject",    // 这条命令    "build-chrome-ext": "react-scripts build && cp src/js/background.js build/background.js && cp src/js/content.js build/content.js && cp build/static/js/main.***.js build/icon.js && cp build/static/css/main.***.css build/icon.css "  },

其中index.js文件就是该项目标入口文件,因而此时的命令就是将编译后的文件打包build/static/js/main.*.js

此时执行npm run build-chrome-ext, 而后chrome浏览器加载解压的build文件,一个插件就实现啦

优化:

  1. V2 --> V3
    此时将配置文件更改为
    V2到V3的扩大迁徙,能够参考此篇文章chrome插件从V2到V3的迁徙
{  "short_name": "施行地图",  "name": "施行地图插件",  "manifest_version": 3,  "content_security_policy": {    "extension_pages": "script-src 'self' ; object-src 'self'"   },  "version": "1.0",  "background":{    "service_worker": "background.js"  },  "icons":    {        "16": "img/icon.png",        "48": "img/icon.png",        "128": "img/icon.png"    },  "content_scripts": [ {    "js": [ "content.js","icon.js" ],    "css": ["icon.css"],    "matches": [       "*://*.italent-inc.cn/*",      "*://*.italent.link/*",      "*://*.italent.cn/*" ],    "run_at": "document_end"  } ],  "permissions":[    "tabs",    "activeTab",    "storage"  ],  "host_permissions":[    "http://*/*",    "https://*/*"  ],  "externally_connectable":{    "matches":[      "*://*.italent-inc.cn/*",      "*://*.italent.link/*",      "*://*.italent.cn/*"    ]  }}
  1. 打包命令优化
    那一长串打包命令确实有些俊俏,能够做成这种: "react-scripts build && node xxx.js",而后把文件操作写到xxx.js里,就是写个shell脚本来专门做文件的挪动和拷贝,不肯定是shell,喜爱哪个用哪个

当然 我的这个插件性能很简略,还有很多没有考虑周到的中央,后续有机会持续跟进学习~