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

编写 Chrome 扩展——contextMenus 的快捷创建的更多相关文章

  1. 编写Chrome扩展程序

    Chrome的扩展程序很多,也很容易入门,可以来简单实现一下 看看,慢慢就能实现出一个扩展程序来 每个扩展程序应用一般会包含: 一个manifest清单文件 html文件 js文件 其他文件等 可以看 ...

  2. 【转】编写Chrome扩展程序

    Chrome的扩展程序很多,也很容易入门,可以来简单实现一下 看看,慢慢就能实现出一个扩展程序来 每个扩展程序应用一般会包含: 一个manifest清单文件 html文件 js文件 其他文件等 可以看 ...

  3. BlazeMeter发布chrome扩展插件,支持JMeter脚本创建

    BlazeMeter发布chrome扩展插件,支持JMeter脚本创建http://www.automationqa.com/forum.php?mod=viewthread&tid=3898 ...

  4. 了解Chrome扩展程序开发--摘抄

    了解Chrome扩展程序开发 2018-01-11 边城到此莫若 鸡蛋君说前端 首先,我尝试来用简单几句话描述一下Chrome扩展程序: Chrome扩展主要用于对浏览器功能的增强,它强调与浏览器相结 ...

  5. 清除页面广告?身为前端,自己做一款简易的chrome扩展吧

    大家肯定有这样的经历,浏览网页的时候,左右两端广告,诸如“屠龙宝刀,点击就送”,以及最近火的不行的林子聪37传奇霸业什么“霸业面具,霸业吊坠”的魔性广告总是充斥我们的眼球. 当然有现成的扩展程序或者插 ...

  6. Chrome扩展程序的二次开发:把它改得更适合自己使用

    我当然知道未经作者允许修改别人程序是不道德的了,但作为学习研究之用还是无可厚非,这里仅供交流. 一切都是需求驱动的 话说某天我在网上猎奇的时候无意间发现这么一款神奇的谷歌浏览器插件:Extension ...

  7. Web 开发人员必备的12款 Chrome 扩展程序

    之前已经分享过一些帮助 Web 开发人员和设计师的 Chrome 扩展,这次我们继续展示一组很有用的 Chrome 应用程序.这些免费的 Chrome 应用程序可以简化您的工作流程,为了加快您的工作流 ...

  8. chrome扩展程序----域名助手

    音乐分享: Future Islands - <Aladdin> 中年大叔的抖腿新专辑<The Far Field> ————————————————————————————— ...

  9. 一起来做chrome扩展《页面右键菜单》

    本文主要内容 contextMenus的设置 打开权限 创建菜单 点击菜单 background script向content script发送消息 1. contextMenus的设置 1.1 打开 ...

随机推荐

  1. 03python面向对象编程4

    http://c.biancheng.net/view/2287.html 1.1定义类和对象 在面向对象的程序设计过程中有两个重要概念:类(class)和对象(object,也被称为实例,insta ...

  2. bzoj2600 [Ioi2011]ricehub 双指针

    题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=2600 题解 随便写一个比较简单的 two pointers 练习题. 首先答案肯定是一个原序列 ...

  3. 查看PL/SQL编译时的错误信息

    编译无效对象是DBA与数据库开发人员常见的工作之一.对于编译过程中的错误该如何去捕获,下面给出两种捕获错误的方法. 一.当前数据库版本信息及无效对象 1.查看当前数据库版本 [sql] view pl ...

  4. B1016. 部分 A+B

    题目描述 正整数A的"D(为1位整数)部分"定义由A中所有D组成的新整数P,例如给定A=3862767,D=6,则A的"6部分" P是66,因为A中有2个6,现 ...

  5. [每日一讲] Python系列:字符串(上)

    字符串作为人类最常处理的内容,在计算中决定了其占有重要的地位.在 Python 中,字符串的操作和处理往往需要根据实际问题,结合其他操作才可以完成目标.在复杂世界仅仅是字符串 API 还无法完成工作. ...

  6. Java的基本使用

    1.如何运行一个Java源码 打开文本编辑器,输入以下代码: public class Hello { public static void main(String[] args) { System. ...

  7. 向上取整&向下取整

    使用floor函数. floor(x)返回的是小于或等于x的最大整数.eg.      floor(1.5) = 1 floor(-2.5) = -3 使用ceil函数. ceil(x)返回的是大于x ...

  8. [思路题][LOJ2290][THUWC2017]随机二分图:状压DP+期望DP

    分析 考虑状压DP,令\(f[sta]\)表示已匹配状态是\(sta\)(\(0\)代表已匹配)时完美匹配的期望数量,显然\(f[0]=1\). 一条边出现了不代表它一定在完美匹配内,这也导致很难去直 ...

  9. D - Cheerleaders(第三周)

    D - Cheerleaders 题目链接:https://vjudge.net/contest/154063#problem/D 题目大意: 给你一个 n∗m 的方格,现在有 k 个相同石子,我们要 ...

  10. es之java插入优化(批量插入)

    插入文档操作的一种优化,因为每次插入单条文档,都会向es中发送请求.然后es执行在返回结果: 如果有大批量的文档数据需要插入,这个时候单挑插入操作显然是不合理的: 之前学习的命令行批量执行方式: PO ...