You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Chrome扩展Manifest V3 Service Worker加载jQuery报错与XML解析方案

解决方案

Manifest V3的Service Worker是独立Worker上下文,没有完整的DOM/BOM环境,jQuery(尤其是你用的2.x老旧版本)依赖window、document等页面环境对象,就算修正导入语法也无法正常运行,完全没必要强行适配。Chrome原生提供的DOMParserAPI可以直接在Service Worker中解析XML,所有jQuery的DOM操作都可以用原生Web API替换,无依赖、启动速度更快。

清理无效依赖

直接删除代码开头错误的jQuery导入语句,本地的jQuery文件也可以一并删除,不需要保留:

// 删除以下无效导入
// import $ as module from 'js/jquery-2.1.4.min.js';

替换Feed解析逻辑

你代码中所有jQuery调用都集中在FeedService模块,直接把原有downloadFeed、parseFeed、getUrlForFeed三个方法替换为下面的原生实现即可,业务逻辑和原代码完全一致,同时修复了原代码未兼容Atom订阅格式的问题:

var FeedService = (function() {
    // 工具方法:获取指定选择器下的元素文本
    function getText(elm, selector) {
        const node = elm.querySelector(selector);
        return node ? node.textContent.trim() : '';
    }
    // 工具方法:获取条目链接,兼容RSS2.0(文本节点)和Atom(href属性)两种格式
    function getEntryLink(elm) {
        const rssLink = elm.querySelector('link');
        if (rssLink && rssLink.textContent.trim()) {
            return rssLink.textContent.trim();
        }
        const atomLink = elm.querySelector('link[href]');
        if (atomLink) {
            return atomLink.getAttribute('href');
        }
        return '';
    }

    return {
        downloadFeed: function(feed) {
            if (!feed.url) return;
            console.log(feed.url)
            fetch(feed.url)
                .then(response => response.text())
                .then(xmlString => {
                    console.log(xmlString)
                    const xmlDoc = new DOMParser().parseFromString(xmlString, 'text/xml');
                    this.parseFeed(xmlDoc, feed);
                })
                .catch(err => console.error('Feed拉取失败:', err))
        },
        parseFeed: function(xmlDoc, existingFeedData) {
            // 兼容Atom的<entry>和RSS2.0的<item>两种条目标签
            let feedEntries = xmlDoc.querySelectorAll("entry");
            if (feedEntries.length === 0) {
                feedEntries = xmlDoc.querySelectorAll('item');
            }

            let lastUpdate = Date.now()
            console.log("Now = " + lastUpdate)
            lastUpdate = (existingFeedData && existingFeedData.lastUpdate) || Date.now()
            console.log("Last Update = " + lastUpdate)
            console.log("existingFeedData = " + JSON.stringify(existingFeedData))
            const pubDates = [];

            Array.from(feedEntries).forEach(function(elm) {
                // 兼容RSS2.0的pubDate和Atom的updated时间字段
                const pubDateStr = getText(elm, "pubDate") || getText(elm, "updated");
                const pubTime = new Date(pubDateStr).getTime();
                pubDates.push(pubTime);

                if (lastUpdate < pubTime) {
                    const title = getText(elm, "title");
                    console.log(title.substring(0, 20));
                    const link = getEntryLink(elm);
                    chrome.notifications.create(
                        link, {
                            type: "basic",
                            iconUrl: "../icons/254.png",
                            title: title,
                            message: "(" + msToTime((Date.now() - pubTime)) + ") ",
                            requireInteraction: true
                        },
                        function() {}
                    );
                }
            }.bind(this));

            if (pubDates.length > 0) {
                const maxPubTime = Math.max.apply(null, pubDates);
                console.log("Max date = " + maxPubTime)
                existingFeedData.lastUpdate = maxPubTime;
                Storage.saveFeedData(existingFeedData);
            }
        },

        getUrlForFeed: function(xmlDoc, feedSettings) {
            if (feedSettings.linkType) {
                const customLink = xmlDoc.querySelector(feedSettings.linkType);
                if (customLink && (customLink.textContent.trim() || customLink.getAttribute('href'))) {
                    return customLink.textContent.trim() || customLink.getAttribute('href');
                }
            }
            const allLinks = xmlDoc.querySelectorAll('link');
            if (allLinks.length == 1) {
                return allLinks[0].textContent.trim() || allLinks[0].getAttribute('href');
            }
            const shortLink = xmlDoc.querySelector('link[rel="shorturl"]');
            if (shortLink) return shortLink.getAttribute('href');
            const altLink = xmlDoc.querySelector('link[rel="alternate"]');
            if (altLink) return altLink.getAttribute('href');
            const relatedLink = xmlDoc.querySelector('link[rel="related"]');
            if (relatedLink) return relatedLink.getAttribute('href');
            return allLinks[0];
        },
        saveFeed: function(existingFeedData, newFeedData) {},
        updateReadStatusOfNewItems: function(oldItems, newItems) {
            if (typeof oldItems === 'undefined' || Object.keys(oldItems).length == 0 || Object.keys(newItems).length == 0) {
                return newItems;
            }
            Object.keys(newItems).forEach(function(url) {
                if (oldItems[url]) {
                    newItems[url].unread = oldItems[url].unread;
                }
            });
            return newItems;
        },
        setNewBadge: function() {
            // MV3已废弃browserAction,统一使用action API
            chrome.action.setBadgeText({
                text: 'NEW'
            });
        }
    };
}());

适配注意点

  • 现有Manifest V3的background配置不需要修改,"type": "module"配置保留即可
  • 注意MV3中chrome.browserActionAPI已被废弃,所有调用都要替换为chrome.action,否则徽章设置、图标点击等逻辑会报错
  • 改造后无任何第三方依赖,Service Worker冷启动速度比加载jQuery快3-5倍,内存占用更低
  • 上述代码额外兼容了Atom格式订阅的时间字段和链接格式,适配范围比原jQuery版本更广

内容的提问来源于stack exchange,提问作者Code Guy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 10:21:19