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();
} }

最新文章

  1. 剑指Offer 两个链表的第一个公共结点
  2. MySQL- -Join语法解析与性能分析
  3. vi, vim 基本使用(1)
  4. 详谈socket请求Web服务器过程
  5. win7 64位下jboss配置
  6. Erp:原料投入产出报表
  7. Compiler options do not specify -mv64+, but configuration is for C64x+
  8. [Tommas] Web测试中,各类web控件测试点总结
  9. BZOJ 3262 陌上花开 CDQ分治
  10. 通过PHP连接MYSQL数据库 创建数据库 创建表
  11. 移动Web学习笔记(第1天)-bootstrap框架的使用
  12. BZOJ_4325_NOIP2015 斗地主_DFS
  13. vue3+typescript引入外部文件
  14. 算法学习之BFS、DFS入门
  15. 跳跳棋[LCA+二分查找]-洛谷1852
  16. 使用Apache JMeter对SQL Server、Mysql、Oracle压力测试(一)
  17. sqlserver in 和 exist 子查询
  18. mac 上安装 redis
  19. 遇到问题---java---@value注解为null
  20. vue 开发中的常见问题

热门文章

  1. 【学习】020 Redis
  2. python time、datetime模块
  3. SQL server 表copy 到别一张表
  4. 超大文件上传方案( Java )
  5. HDU 4758 Walk Through Squares ( Trie图 && 状压DP && 数量限制类型 )
  6. 【bzoj3566】 [SHOI2014]概率充电器
  7. 【FJ省队训练&&NOIP夏令营】酱油&&滚粗记
  8. PHP导出excel文件名中文IE乱码解决
  9. 【BZOJ1132】Tro(叉积)
  10. 走进JavaWeb技术世界14:Mybatis入门