We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
首发于微信公众号《前端成长记》,写于 2019.10.18
有句老话说的好,好记性不如烂笔头。人生中,总有那么些东西你愿去执笔写下。
本文旨在把整个开发的过程和遇到的问题及解决方案记录下来,希望能够给你带来些许帮助。
安装和源码
在 《干货!从0开始,0成本搭建个人动态博客》 中,已经完成了动态博客的搭建。接下来,将围绕该博客,开发对应的 Chrome拓展,方便使用。
Chrome拓展
本文不需要前期准备,直接跟我做就好了
这里主要分为几个大的功能点:
Ⅰ.必要知识介绍
Chrome 拓展插件 实际上是由 HTML/CSS/JS/图片 等资源组成的一个 .crx 的拓展包,解压出来即可得到真正内容。
Chrome 拓展插件
HTML/CSS/JS/图片
.crx
Chrome 拓展插件 对项目结构没有要求,只需要在开发根目录下有一个 mainfest.json 即可。
mainfest.json
进入 Chrome 拓展程序 页面,打开 开发者模式 开始我们的开发之路。
Chrome 拓展程序
开发者模式
Ⅱ.基础配置开发
首先,新建一个 src 目录作为插件的文件目录,然后新建一个 mainfest.json 文件,文件内容如下:
src
// mainfest.json { // 插件名称 "name": "McChen", // 插件版本号 "version": "0.0.1", // 插件描述 "description": "Chrome Extension for McChen.", // 插件主页 "homepage_url": "https://chenjiahao.xyz", // 版本必须指定为2 "manifest_version": 2 }
然后打开 Chrome 拓展程序页面,点击 加载已解压的拓展程序 按钮,选择上面新建的 src 文件,将会看到如下两处变化:
Chrome
你会发现你的拓展插件已经添加到右上角了,点击右键时出现的第一行为 name ,点击跳转链接为 homepage_url 。
name
homepage_url
接下来我们为我们的拓展插件添加图标,在 src 中新建一个名为 icon.png 的图标,然后修改 mainfest.json 文件:
icon.png
// mainfest.json { ... "icons": { "16": "icon.png", "32": "icon.png", "48": "icon.png", "128": "icon.png" } ... }
点击插件开发的更新图标,我们可以看到图标已经加上了:
这里会发现,右上角的图标为什么是置灰的呢?这里就需要聊到 browser_action 和 page_action 。[参考文档]
browser_action
page_action
为了让图标一直可见,我们来修改下 mainfest.json :
{ ... "browser_action": { "default_icon": "icon.png", "default_title": "McChen" }, ... }
此时再次更新查看效果:
到这里,基础的配置开发已经完成了,接下来就是功能部分。
Ⅲ.内容菜单导航开发
[参考文档]
内容导航菜单我用在两个地方:鼠标点击右上角图标的 Popup 和网页中按鼠标右键出现的菜单。
Popup
先看看鼠标点击右上角图标 Popup 的,给 mainfest.json 增加 default_popup 就是 popup 展示的页面内容了。
default_popup
popup
{ ... "browser_action": { "default_icon": "icon.png", "default_title": "McChen", "default_popup": "popup.html" }, ... }
新建一个 popup.html 文件,内容如下:
popup.html
<!DOCTYPE html> <html lang="en"> <head> <title>McChen</title> <meta charset="utf-8"/> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <style type="text/css"> #McChen-container { padding: 4px 0; margin: 0; width: 80px; user-select: none; overflow: hidden; text-align: center; background-color: #f6f8fc;} .McChen-item_a { position: relative; display: block; font-size: 14px; color: #283039; transition: all 0.2s; line-height: 28px; text-decoration: none; white-space: nowrap; text-indent: 16px;} .McChen-item_a:before { position: absolute; top: 50%; margin-top: -14px; font-size: 16px; line-height: 28px;} .McChen-item_a:after { position: absolute; top: 50%; margin-top: -14px; font-size: 16px; line-height: 28px;} .McChen-item_a + .McChen-item_a { border-top: 1px solid #f0f2f5;} .McChen-item_a:hover { color: #0074ff;} .McChen-item_a:nth-child(1):before { content: '·'; left: 4px;} .McChen-item_a:nth-child(2):before { content: '··'; left: 2px;} .McChen-item_a:nth-child(3):before { content: '···'; left: 0;} .McChen-item_a:nth-child(4):before { content: '····'; left: -2px;} .McChen-item_a:nth-child(5):before { content: '····'; margin-top: -16px; left: -2px;} .McChen-item_a:nth-child(5):after { content: '·'; margin-top: -12px; left: -2px;} .McChen-item_a:nth-child(6):before { content: '····'; margin-top: -16px; left: -2px;} .McChen-item_a:nth-child(6):after { content: '··'; margin-top: -12px; left: -2px;} .McChen-item_a:nth-child(7):before { content: '····'; margin-top: -16px; left: -2px;} .McChen-item_a:nth-child(7):after { content: '···'; margin-top: -12px; left: -2px;} </style> </head> <body id="McChen-container"> <a class="McChen-item_a" href="https://chenjiahao.xyz" target="_blank">主页</a> <a class="McChen-item_a" href="https://chenjiahao.xyz/blog/#/archives" target="_blank">博客</a> <a class="McChen-item_a" href="https://chenjiahao.xyz/blog/#/labels" target="_blank">标签</a> <a class="McChen-item_a" href="https://chenjiahao.xyz/blog/#/links" target="_blank">友链</a> <a class="McChen-item_a" href="https://chenjiahao.xyz/blog/#/about" target="_blank">关于</a> <a class="McChen-item_a" href="https://chenjiahao.xyz/blog/#/board" target="_blank">留言</a> <a class="McChen-item_a" href="https://chenjiahao.xyz/blog/#/search" target="_blank">搜索</a> </body> </html>
我们更新后来看看效果,点击右上角图标将会看到如下的内容弹窗:
下一步,我们来实现在网页中按鼠标右键出现的菜单。
首先,你必须要配置对应的权限才能使用这个 API ,还需要配置修改 mainfest.json 内容:
API
[权限参考文档]
... "permissions": [ "contextMenus" ] ...
接下来,需要通过 API 调用去创建对应的菜单,这里需要用到常驻在后台运行的 js 才行,所以还需要修改 mainfest.json 文件:
js
... "background": { "scripts": [ "background.js" ] }, ...
然后我们新建一个 backgroud.js 文件,文件内容如下:
backgroud.js
chrome.contextMenus.create({ id: 'McChen', title: 'McChen', contexts: ['page', 'frame', 'selection', 'link', 'editable', 'image', 'video', 'audio', 'page_action'] }); chrome.contextMenus.create({ id: 'home', title: '主页', parentId: 'McChen', // 右键菜单项的父菜单项ID。指定父菜单项将会使此菜单项成为父菜单项的子菜单 contexts: ['page', 'frame', 'selection', 'link', 'editable', 'image', 'video', 'audio', 'page_action'] }); chrome.contextMenus.create({ id: 'archives', title: '博客', parentId: 'McChen', // 右键菜单项的父菜单项ID。指定父菜单项将会使此菜单项成为父菜单项的子菜单 contexts: ['page', 'frame', 'selection', 'link', 'editable', 'image', 'video', 'audio', 'page_action'] }); chrome.contextMenus.create({ id: 'labels', title: '标签', parentId: 'McChen', // 右键菜单项的父菜单项ID。指定父菜单项将会使此菜单项成为父菜单项的子菜单 contexts: ['page', 'frame', 'selection', 'link', 'editable', 'image', 'video', 'audio', 'page_action'] }); chrome.contextMenus.create({ id: 'links', title: '友链', parentId: 'McChen', // 右键菜单项的父菜单项ID。指定父菜单项将会使此菜单项成为父菜单项的子菜单 contexts: ['page', 'frame', 'selection', 'link', 'editable', 'image', 'video', 'audio', 'page_action'] }); chrome.contextMenus.create({ id: 'about', title: '关于', parentId: 'McChen', // 右键菜单项的父菜单项ID。指定父菜单项将会使此菜单项成为父菜单项的子菜单 contexts: ['page', 'frame', 'selection', 'link', 'editable', 'image', 'video', 'audio', 'page_action'] }); chrome.contextMenus.create({ id: 'board', title: '留言', parentId: 'McChen', // 右键菜单项的父菜单项ID。指定父菜单项将会使此菜单项成为父菜单项的子菜单 contexts: ['page', 'frame', 'selection', 'link', 'editable', 'image', 'video', 'audio', 'page_action'] }); chrome.contextMenus.create({ id: 'search', title: '搜索', parentId: 'McChen', // 右键菜单项的父菜单项ID。指定父菜单项将会使此菜单项成为父菜单项的子菜单 contexts: ['page', 'frame', 'selection', 'link', 'editable', 'image', 'video', 'audio', 'page_action'] }); // 监听菜单点击事件 chrome.contextMenus.onClicked.addListener(function (info, tab) { if (info.menuItemId === 'home') { chrome.tabs.create({url: 'https://chenjiahao.xyz'}); } else { chrome.tabs.create({url: 'https://chenjiahao.xyz/blog/#/' + info.menuItemId}); } })
更新后,点击鼠标右键将查看到如下内容:
至此,内容菜单导航功能已全部完成。
Ⅳ.地址栏搜索开发
地址栏搜索主要是通过 Omnibox 来实现的,我们首先需要设置关键字,在这里我设置成 'mc' ,修改 mainfest.json 文件:
Omnibox
... { "omnibox": { "keyword" : "mc" } } ...
更新后,我们在地址栏输入 mc 按 Tab 或者 Space 键可看到如下内容:
mc
Tab
Space
接下来我们进行接口开发,由于需要进行接口调用,所以需要配置允许请求的地址,修改 mainfest.json 文件:
... { "permissions": [ "contextMenus", // 允许请求全部https "https://*/" ], } ...
然后修改 background.js 文件内容:
background.js
... let timer = ''; chrome.omnibox.onInputChanged.addListener((text, suggest) => { if (timer) { clearTimeout(timer) timer = '' } else { timer = setTimeout(() => { if (text.length > 1) { const xhr = new XMLHttpRequest(); xhr.open("POST", "https://api.artfe.club/transfer/github", true); xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded') xhr.onreadystatechange = function () { if (xhr.readyState === 4) { const list = JSON.parse(xhr.responseText).data.search.nodes; if (list.length) { suggest(list.map(_ => ({content: 'ISSUE_NUMBER:' + _.number, description: '文章 - ' + _.title}))) } else { suggest([ {content: 'none', description: '无相关结果'} ]) } } }; xhr.send('query=' + query); } else { suggest([ {content: 'none', description: '查询中,请稍后...'} ]) } }, 300) } }); // 当选中建议内容时触发 chrome.omnibox.onInputEntered.addListener((text) => { if (text.startsWith('ISSUE_NUMBER:')) { const number = text.substr(13) chrome.tabs.query({active: true, currentWindow: true}, function (tabs) { if (tabs.length) { const tabId = tabs[0].id; const url = 'https://chenjiahao.xyz/blog/#/archives/' + number; chrome.tabs.update(tabId, {url: url}); } }); } }); ...
这里有几个地方需要注意一下:
onInputChanged
Promise
XMLHttpRequest
suggest
content
description
ISSUE_NUMBER:
更新后,在地址栏输入 mc 按 Tab 后,输入 干货 ,将会看到如下内容:
干货
至此,地址栏搜索功能已全部完成。
Ⅴ.新文章推送开发
[存储参考文档]
[推送参考文档]
新文章推送功能,首先我们需要知道之前的最新文章是哪篇,才能做到精准推送,所以这里需要用到 Storage ,也就是存储功能。存下最新文章的 ID ,轮询最新文章,如果有更新,则存下最新文章的 ID 并且调用推送的 API 。所以,我们需要先增加权限配置,修改 mainfest.json 文件:
Storage
ID
... "permissions": [ "storage", "contextMenus", "notifications", "https://*/" ], ...
然后修改 'background.js' 文件内容:
... getLatestNumber(); chrome.storage.sync.get({LATEST_TIMER: 0}, function (items) { if (items.LATEST_TIMER) { clearInterval(items.LATEST_TIMER) } const LATEST_TIMER = setInterval(() => { getLatestNumber() }, 1000 * 60 * 60 *24) chrome.storage.sync.set({LATEST_TIMER: LATEST_TIMER}) }); function getLatestNumber () { const query = `query { repository(owner: "ChenJiaH", name: "blog") { issues(orderBy: {field: CREATED_AT, direction: DESC}, labels: null, first: 1, after: null) { nodes { title number } } } }`; const xhr = new XMLHttpRequest(); xhr.open("POST", "https://api.artfe.club/transfer/github", true); xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); xhr.onreadystatechange = function () { if (xhr.readyState === 4) { const list = JSON.parse(xhr.responseText).data.repository.issues.nodes; if (list.length) { const title = list[0].title; const ISSUE_NUMBER = list[0].number; chrome.storage.sync.get({ISSUE_NUMBER: 0}, function(items) { if (items.ISSUE_NUMBER !== ISSUE_NUMBER) { chrome.storage.sync.set({ISSUE_NUMBER: ISSUE_NUMBER}, function() { chrome.notifications.create('McChen', { type: 'basic', iconUrl: 'icon.png', title: '新文章发布通知', message: title }); chrome.notifications.onClicked.addListener(function (notificationId) { if (notificationId === 'McChen') { chrome.tabs.create({url: 'https://chenjiahao.xyz/blog/#/archives/' + ISSUE_NUMBER}); } }) }); } }); } } }; xhr.send('query=' + query); } ...
注意:由于是后台常驻,所以需要增加轮询来判断是否有更新,我这里设置的是一天一次
更新后,第一次我们会看到浏览器右下角会有推送消息如下:
至此,新文章推送功能也已经开发完成了。
在拓展程序页面点击打包扩展程序,选择 src 作为根目录打包即可。
将会生成 src.crx 和 src.pem 两个文件, .crx 文件就是你提交到拓展商店的资源, .pem 文件是私钥,下次进行打包更新时需要使用。
src.crx
src.pem
.pem
由于打包需要 5$ ,所以我这里就不做演示了,需要的可以自行尝试,[发布地址]
一个基于动态博客的 Chrome 拓展插件 就开发完了,欢迎下载使用。
如有疑问或不对之处,欢迎留言。
(完)
本文为原创文章,可能会更新知识点及修正错误,因此转载请保留原出处,方便溯源,避免陈旧错误知识的误导,同时有更好的阅读体验 如果能给您带去些许帮助,欢迎 ⭐️star 或 ✏️ fork (转载请注明出处:https://chenjiahao.xyz)
The text was updated successfully, but these errors were encountered:
No branches or pull requests
导读
有句老话说的好,好记性不如烂笔头。人生中,总有那么些东西你愿去执笔写下。
本文旨在把整个开发的过程和遇到的问题及解决方案记录下来,希望能够给你带来些许帮助。
安装和源码
安装和源码
背景
在 《干货!从0开始,0成本搭建个人动态博客》 中,已经完成了动态博客的搭建。接下来,将围绕该博客,开发对应的
Chrome拓展
,方便使用。上手开发
功能拆分
这里主要分为几个大的功能点:
Ⅰ.必要知识介绍
Chrome 拓展插件
实际上是由HTML/CSS/JS/图片
等资源组成的一个.crx
的拓展包,解压出来即可得到真正内容。Chrome 拓展插件
对项目结构没有要求,只需要在开发根目录下有一个mainfest.json
即可。进入
Chrome 拓展程序
页面,打开开发者模式
开始我们的开发之路。Ⅱ.基础配置开发
首先,新建一个
src
目录作为插件的文件目录,然后新建一个mainfest.json
文件,文件内容如下:然后打开
Chrome
拓展程序页面,点击 加载已解压的拓展程序 按钮,选择上面新建的src
文件,将会看到如下两处变化:你会发现你的拓展插件已经添加到右上角了,点击右键时出现的第一行为
name
,点击跳转链接为homepage_url
。接下来我们为我们的拓展插件添加图标,在
src
中新建一个名为icon.png
的图标,然后修改mainfest.json
文件:点击插件开发的更新图标,我们可以看到图标已经加上了:
这里会发现,右上角的图标为什么是置灰的呢?这里就需要聊到
browser_action
和page_action
。[参考文档]browser_action
:如果你想让图标一直可见,那么配置该项page_action
:如果你不想让图标一直可见,那么配置该项为了让图标一直可见,我们来修改下
mainfest.json
:此时再次更新查看效果:
到这里,基础的配置开发已经完成了,接下来就是功能部分。
Ⅲ.内容菜单导航开发
[参考文档]
内容导航菜单我用在两个地方:鼠标点击右上角图标的
Popup
和网页中按鼠标右键出现的菜单。先看看鼠标点击右上角图标
Popup
的,给mainfest.json
增加default_popup
就是popup
展示的页面内容了。新建一个
popup.html
文件,内容如下:我们更新后来看看效果,点击右上角图标将会看到如下的内容弹窗:
下一步,我们来实现在网页中按鼠标右键出现的菜单。
首先,你必须要配置对应的权限才能使用这个
API
,还需要配置修改mainfest.json
内容:[权限参考文档]
接下来,需要通过
API
调用去创建对应的菜单,这里需要用到常驻在后台运行的js
才行,所以还需要修改mainfest.json
文件:然后我们新建一个
backgroud.js
文件,文件内容如下:[参考文档]
更新后,点击鼠标右键将查看到如下内容:
至此,内容菜单导航功能已全部完成。
Ⅳ.地址栏搜索开发
[参考文档]
地址栏搜索主要是通过
Omnibox
来实现的,我们首先需要设置关键字,在这里我设置成 'mc' ,修改mainfest.json
文件:更新后,我们在地址栏输入
mc
按Tab
或者Space
键可看到如下内容:接下来我们进行接口开发,由于需要进行接口调用,所以需要配置允许请求的地址,修改
mainfest.json
文件:然后修改
background.js
文件内容:这里有几个地方需要注意一下:
onInputChanged
这方法触发频率高,和正常开发一样,需要做一次函数防抖,要不然请求频率会特别高。Promise
,所以我使用的XMLHttpRequest
suggest
中content
和description
字段都不允许为空,但是在事件回调里需要识别,所以我这里特意增加了一个前缀ISSUE_NUMBER:
更新后,在地址栏输入
mc
按Tab
后,输入干货
,将会看到如下内容:至此,地址栏搜索功能已全部完成。
Ⅴ.新文章推送开发
[存储参考文档]
[推送参考文档]
新文章推送功能,首先我们需要知道之前的最新文章是哪篇,才能做到精准推送,所以这里需要用到
Storage
,也就是存储功能。存下最新文章的ID
,轮询最新文章,如果有更新,则存下最新文章的ID
并且调用推送的API
。所以,我们需要先增加权限配置,修改mainfest.json
文件:然后修改 'background.js' 文件内容:
更新后,第一次我们会看到浏览器右下角会有推送消息如下:
至此,新文章推送功能也已经开发完成了。
打包发布
在拓展程序页面点击打包扩展程序,选择
src
作为根目录打包即可。将会生成
src.crx
和src.pem
两个文件,.crx
文件就是你提交到拓展商店的资源,.pem
文件是私钥,下次进行打包更新时需要使用。由于打包需要 5$ ,所以我这里就不做演示了,需要的可以自行尝试,[发布地址]
结尾
一个基于动态博客的
Chrome 拓展插件
就开发完了,欢迎下载使用。如有疑问或不对之处,欢迎留言。
(完)
The text was updated successfully, but these errors were encountered: