Skip to content
On this page

image-20220211114010280

前言

本文以实现一个简单的浏览器插件为例,带你入门插件开发。插件功能:一键复制标题以及链接,生成 Markdown 格式链接并给到剪切板。源代码在my-extension仓库

背景

近期在写周刊,要复制文章标题以及链接,之后转换为 Markdown 格式粘贴到文档编辑器中。

痛点

每次都要重复「复制标题 ➡️ 复制链接 ➡️ 结合成 Markdown 」这些操作,文章一多就有些麻烦且枯燥。

目标

要是能一键复制为 Markdown 链接格式(包含标题以及对应链接),接着直接到文档编辑器中粘贴,那该有多美好。🤩

刚好最近学了插件开发的内容,可以实现如上想法,正好借此机会实践一下(其实这里的插件应该叫扩展,也就是 extension ,但现在普遍都叫插件,故本文也使用此名称)。

写插件

我常看的文章都是来自掘金与微信公众号,通过分析HTML代码发现,文章标题都是在h1标签内的,所以只要获取到h1标签的innerText内容再加上window.location.href即可完成目标。

首先新建目录my-extension,创建manifest.json文件:

json
{
    "name": "复制标题链接",
    "description": "使用 Markdown 格式复制标题链接",
    "version": "1.0",
    "manifest_version": 3
}

只需要一个简单的manifest.json文件,我们就已经编写了一个插件,这时在浏览器地址栏中输入chrome://extensions即可进入到插件管理页面,打开开发者模式并加载扩展:

image-20220204080203473

加载完成后,可看到插件如下:

image-20220205221158858

你可以理解为我们只是写了一个插件声明,但是这个插件什么都没有做,接下来我们给它添加功能,在manifest.json中增加commands字段、permissions字段和background字段,字段定义文档在Manifest file format中查看:

json
    "background": {
        "service_worker": "background.js"
    },
    "permissions": ["activeTab", "scripting"],
    "commands": {
        "run-copy": {
            "suggested_key": {
                "default": "Ctrl+Shift+Y",
                "mac": "Command+Shift+Y"
            },
            "description": "执行复制标题链接"
        }
    }

commands字段可以监听到快捷键的触发,上述代表使用Command+Shift+Y快捷键就能触发run-copy事件,这时新增background.js文件,并使用chrome.commands.onCommand.addListener即可添加事件监听器,但这里还需要获取当前 tab 以及注入脚本的权限,所以还需要permissions字段,最终background.js文件代码如下:

js
const copyTitleAndLink = async () => {
    const fixedMessageId = '__fixed-message-id';
    const tipStyle = `
#__fixed-message-id {
    position: fixed;
    right: 32px;
    top: 32px;
    background-color: white;
    z-index: 9999;

    padding: 8px 16px;
    min-width: 200px;
    border-radius: 8px;
    font-size: 12px;
    box-shadow: rgba(0, 0, 0, 0.35) 0px 5px 15px;
}

#__fixed-message-id.success {
    border: 2px solid #2ecc71;
}

#__fixed-message-id.fail {
    border: 2px solid #e74c3c;
}
`;
    // 把提示信息插入到网页中
    const tipMessage = isOk => {
        const styleSheet = document.createElement('style');
        styleSheet.innerText = tipStyle;
        document.head.appendChild(styleSheet);

        let div = document.querySelector(`#${fixedMessageId}`);
        if (!div) {
            div = document.createElement('div');
            div.setAttribute('id', fixedMessageId);
        }
        if (isOk) {
            div.innerText = '复制成功 🎉';
            div.setAttribute('class', 'success');
        } else {
            div.innerText = '复制失败 😞';
            div.setAttribute('class', 'fail');
        }

        document.body.append(div);

        setTimeout(() => {
            div.remove();
        }, 2000);
    };
    try {
        const h1Element = document.querySelector('h1');
        const title = h1Element.innerText;
        // 获取到需要的 Markdown 格式
        const titleAndLink = `[${title}](${window.location.href})`;
        // 执行剪切板操作
        // 可参考 https://www.ruanyifeng.com/blog/2021/01/clipboard-api.html
        await navigator.clipboard.writeText(titleAndLink);
        tipMessage(true);
    } catch (err) {
        console.error('Failed to copy: ', err);
        tipMessage(false);
    }
};

const runCopy = async () => {
    // 找到当前活跃的浏览器 tab 标签
    let [tab] = await chrome.tabs.query({ active: true, currentWindow: true });

    // 给活跃的 tab 标签执行 copyTitleAndLink 函数
    chrome.scripting.executeScript({
        target: { tabId: tab.id },
        function: copyTitleAndLink,
    });
};

chrome.commands.onCommand.addListener(command => {
    if (command === 'run-copy') {
        // 在监听到 run-copy command 时执行 runCopy 函数
        runCopy();
    }
});

以上代码先是监听了run-copy事件,之后找到当前活跃的 tab 页(也就是你正在浏览的 tab 页),接着执行了copyTitleAndLink函数,此函数的作用是在成功或者失败时在页面上给出提醒,如果按了快捷键后并没有效果,那么要到快捷键页面重新设置一下:

image-20220206001112017

在开发插件过程中,点击插件卡片上的service worker链接,即可打开DevTools调试background.js代码,如果产生错误,那么Remove按钮旁边有Error按钮,点击即可查看错误,直接打开活跃的 tab 页DevTools可调试注入进的 js 代码。

最后我们找一篇文章测试一下,发现可以成功复制内容。🎉

image-20220206003006212

总结

本文简单介绍了manifest.json文件和一些插件 api,想了解更多内容可参考开发文档,想要从零入门可参考快速入门

实现一些功能简单的插件还是很容易的,为了提升你使用浏览器的效率,大家快去实践起来吧。😜