编写 Chrome 扩展——contextMenus 的快捷创建
2024-09-05 20:57:03
1 写在前面
最近使用 typescript 与 angular 编写 chrome 扩展, 对繁复的 contextMenus 创建步骤进行了提炼,并总结一个类
2 重构思路
2.1 一般方法
在编写 chrome 扩展中的 contextMenu 中,一般的思路是定义一个 JSON,并且遍历这个JSON数据并且以此创建 menu, 如:
const menu = {
"menus": [
{"id": "main", "visible": true, "title": "main"},
{"id": "sub1", "visible": true, "title": "sub1", "parentId": "main"},
{"id": "sub11", "visible": true, "title": "sub11", "parentId": "sub1"},
{"id": "sub12", "visible": true, "title": "sub12", "parentId": "sub1"},
{"id": "sub2", "visible": true, "title": "sub2", "parentId": "main"}
]
}; const createMenu = () => {
menu.menus.forEach(value => {
chrome.contextMenus.create(value);
})
};
createMenu();
2.2 重构
在 menu 的个数很少的情况下,上述的传统方式可能不会构成问题,但在选项多的场景下(如编写工具类扩展),contextMenu 的可读性就变得极差
2.2.1 编写 Menu 类
因此我们将用 typescript 来构建一个层次结构清晰的类以组合编排 父-子 menu 的层次结构, 类的结构如下:
interface Menu {
createProperties: CreateProperties;
children?: Menu[];
}
这个类的结构很简单,但是已经足以代表需要创建 ContextMenu 的数据
现在上边使用过的 json 数据现在可以重写为:
const defaultMenu: Menu = {
createProperties: {
id: 'main',
visible: true,
title: 'main'
},
children: [
{
createProperties: {
id: 'sub1',
visible: true,
title: 'sub1', },
children: [
{
createProperties: {
id: 'sub11',
visible: true,
title: 'sub11',
parentId: 'sub1'
}
},
{
createProperties: {
id: 'sub12',
visible: true,
title: 'sub12',
parentId: 'sub1'
}
}
]
},
{
createProperties: {
id: 'sub2',
visible: true,
title: 'sub2',
parentId: 'main'
}
}
]
};
结构似乎很清晰,但是不够完善,我们不想手动管理 menu 之间的父子关系,现在,删掉所有的 parentId 属性,我们将在接下来的节点来讲述如何动态维护关系
2.2.2 编写 collectMenuCreateProperties 方法
在上一节点中,我们创建了一个足以涵盖创建 contextMenu 的类 Menu,但是 chrome.contextMenus.create(property); 要求我们每次传入一个 CreateProperties 类,因此我们需要一个工具类从 Menu 类中抽取所有的 CreateProperties 信息,并且不要忘记了该方法必须能够动态维护 menu 间的父子关系(设置子menu 的 parentId 为上一层的 menu 的 id)
function collectMenuCreateProperties(parent: Menu): CreateProperties[] {
if (parent.createProperties.id === undefined) {
throw new Error('parent contextMenu must has id');
}
let result: CreateProperties[] = [];
result.push(parent.createProperties);
if (parent.children) {
parent.children.forEach(child => {
// 确保每一层的层级关系
child.createProperties.parentId = parent.createProperties.id;
result = result.concat(collectMenuCreateProperties(child));
});
}
return result;
}
2.2.3 编写 createMenu 方法
一切就绪,我们需要创建一个 contextMenu,这里有一些注意的点:
- 如果在文件manifest.json 中对 background.js 设置了属性 "persistent": false ,可能会出现多次创建同一个menu
- 我们的 menu 只需要创建一次,比如扩展初次安装的时候就很合适
- 当调用者忘记传入构建源 Menu 时,需要确保运行不出错,这一点我们将采用ts中的默认值
function createMenu(menu: Menu = defaultMenu): void {
chrome.runtime.onInstalled.addListener(details => {
const properties: CreateProperties[] = collectMenuCreateProperties(menu);
// alert(JSON.stringify(properties));
properties.forEach(property => {
chrome.contextMenus.create(property);
}); });
}
最后,别忘了在顶层导出
export {Menu, defaultMenu};
export {createMenu};
export {collectMenuCreateProperties};
附:
完整的代码
import CreateProperties = chrome.contextMenus.CreateProperties; export {Menu, defaultMenu};
export {createMenu};
export {collectMenuCreateProperties}; /**
* 一系列创建 {@link contextMenus} 需要的数据
* @see defaultMenu
*/
interface Menu {
createProperties: CreateProperties;
children?: Menu[];
} /**
* 默认的的数据 :
* [{"id":"main","visible":true,"title":"main"},
* {"id":"sub1","visible":true,"title":"sub1","parentId":"main"},
* {"id":"sub11","visible":true,"title":"sub11","parentId":"sub1"},
* {"id":"sub12","visible":true,"title":"sub12","parentId":"sub1"},
* {"id":"sub2","visible":true,"title":"sub2","parentId":"main"}]
*/
const defaultMenu: Menu = {
createProperties: {
id: 'main',
visible: true,
title: 'main'
},
children: [
{
createProperties: {
id: 'sub1',
visible: true,
title: 'sub1', },
children: [
{
createProperties: {
id: 'sub11',
visible: true,
title: 'sub11',
}
},
{
createProperties: {
id: 'sub12',
visible: true,
title: 'sub12',
}
}
]
},
{
createProperties: {
id: 'sub2',
visible: true,
title: 'sub2',
}
}
]
}; /**
* 主要方法
* 创建 contextMenus
* 1 监听 {@link chrome.runtime}事件,事件的实体可能是:("install", "update", "chrome_update", or "shared_module_update")
* 2 移除之前创建的所有 {@link chrome.contextMenus}
* 3 执行创建
*
* @param menu 执行创建的上下文信息
* @see Menu
* @see defaultMenu
*/
function createMenu(menu: Menu = defaultMenu): void {
chrome.runtime.onInstalled.addListener(details => {
const properties: CreateProperties[] = collectMenuCreateProperties(menu);
// alert(JSON.stringify(properties));
properties.forEach(property => {
chrome.contextMenus.create(property);
}); });
} /**
* 递归方式返回 parent 中包含的{@link CreateProperties} 对象,
* 每一层的 {@link CreateProperties.id} 必须不为空
* 并以编码的方式保证: 第二层开始, {@link CreateProperties.parentId} 被正确设置
*
* @param parent 顶层
*/
function collectMenuCreateProperties(parent: Menu): CreateProperties[] {
if (parent.createProperties.id === undefined) {
throw new Error('parent contextMenu must has id');
}
let result: CreateProperties[] = [];
result.push(parent.createProperties);
if (parent.children) {
parent.children.forEach(child => {
// 确保每一层的层级关系
child.createProperties.parentId = parent.createProperties.id;
result = result.concat(collectMenuCreateProperties(child));
});
}
return result;
}
用例:
import {Component, OnInit} from '@angular/core';
import {createMenu} from './contextMenus'; @Component({
selector: 'app-event-page',
templateUrl: './event-page.component.html',
styleUrls: ['./event-page.component.css']
}) /**
* @author siweipancc
* @version 1.0.0
*/
export class EventPageComponent implements OnInit {
ngOnInit() {
createMenu();
} }
最新文章
- 剑指Offer 两个链表的第一个公共结点
- MySQL- -Join语法解析与性能分析
- vi, vim 基本使用(1)
- 详谈socket请求Web服务器过程
- win7 64位下jboss配置
- Erp:原料投入产出报表
- Compiler options do not specify -mv64+, but configuration is for C64x+
- [Tommas] Web测试中,各类web控件测试点总结
- BZOJ 3262 陌上花开 CDQ分治
- 通过PHP连接MYSQL数据库 创建数据库 创建表
- 移动Web学习笔记(第1天)-bootstrap框架的使用
- BZOJ_4325_NOIP2015 斗地主_DFS
- vue3+typescript引入外部文件
- 算法学习之BFS、DFS入门
- 跳跳棋[LCA+二分查找]-洛谷1852
- 使用Apache JMeter对SQL Server、Mysql、Oracle压力测试(一)
- sqlserver in 和 exist 子查询
- mac 上安装 redis
- 遇到问题---java---@value注解为null
- vue 开发中的常见问题
热门文章
- 【学习】020 Redis
- python time、datetime模块
- SQL server 表copy 到别一张表
- 超大文件上传方案( Java )
- HDU 4758 Walk Through Squares ( Trie图 &;&; 状压DP &;&; 数量限制类型 )
- 【bzoj3566】 [SHOI2014]概率充电器
- 【FJ省队训练&;&;NOIP夏令营】酱油&;&;滚粗记
- PHP导出excel文件名中文IE乱码解决
- 【BZOJ1132】Tro(叉积)
- 走进JavaWeb技术世界14:Mybatis入门