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. Navicat连接阿里云服务器MSQL数据库

    新买了台服务器Navicat老是连接不上数据库,查找一圈找到解决办法.So Easy 一.shell远程登录,进入数据库 mysql -uroot -p 二.输入以下命令进行授权: GRANT ALL ...

  2. 闲着没事写点啥之springmvc梳理

    用户通过浏览器向服务器发送请求,请求被springmvc的前端控制器dispatcherservlet拦截. dispatcherservlet拦截到请求后,会调用handlermapping处理器映 ...

  3. Mac下Sublime text3无法安装Package Control及中文乱码问题

    sublime text3是一款轻量级的代码编辑器,我曾在Windows下配置过,但时间久了就忘了.这次是在mac上配置,在网上查了一些帖子,有的叙述不是很清楚,故记录一下详细过程. 在线安装: ht ...

  4. darknet-yolov3模型预测框size不正确的原因

    问题描述:预测框的中心位置正常,但是预测的框的width和height不正常. 解决方法:使得训练的配置cfg和测试中cfg的输入width, height, anchorbox保持一致! 问题是我在 ...

  5. mysql AND运算符 语法

    mysql AND运算符 语法 作用:在 WHERE 子语句中把两个或多个条件结合起来.佛山大理石方尺 语法:SELECT * FROM 表名 WHERE 字段1 运算符 值 AND 字段2 运算符 ...

  6. mysql SELECT语句 语法

    mysql SELECT语句 语法,苏州大理石方箱 作用:用于从表中选取数据.结果被存储在一个结果表中(称为结果集). 语法:SELECT 列名称 FROM 表名称 以及 SELECT * FROM ...

  7. C#删除文件夹的文件

    using System.IO; //判断文件是不是存在if(File.Exists(@"文件路径")){//如果存在则删除File.Delete(@"文件路径" ...

  8. JDK,JRE与JVM浅析

    JAVA的两个特性: 1, 开源-指的是源代码免费 2,跨平台(可移植性好) 跨平台:是指跨操作系统 JVM(java virtual machine,java虚拟机) JVM就像是两国谈判时的使者充 ...

  9. 【bzoj3195】【 [Jxoi2012]奇怪的道路】另类压缩的状压dp好题

    (上不了p站我要死了) 啊啊,其实想清楚了还是挺简单的. Description 小宇从历史书上了解到一个古老的文明.这个文明在各个方面高度发达,交通方面也不例外.考古学家已经知道,这个文明在全盛时期 ...

  10. 【FJ省队训练&&NOIP夏令营】酱油&&滚粗记

    FJOI2016省队训练滚粗记 2016.07.03~2016.07.06(Day1~5) 在学校期末考.因为才省选二试too young too simple爆蛋了所以下半个学期只能滚回去读文化课, ...