跪拜 Guibai
← All articles
Frontend · Chrome · Browser

Build Your Own Chrome Extension: A Complete Walkthrough for a Text Snipping Tool

By 牛奶 ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

For Western developers, this guide demystifies Chrome extension development with a practical, complete example that solves a real problem. It shows how MV3 works in practice, including the shift from background pages to service workers, and provides patterns for cross-component communication that apply to any extension project.

Summary

This guide walks through building a complete Chrome extension from scratch: a tool that lets users select text on any webpage, click a floating save button, and store the quote along with its source URL and timestamp. The extension uses Manifest V3 and covers all four core components: manifest.json for permissions and configuration, a content script that injects a save button into pages, a popup HTML page that displays the saved list, and a background service worker that adds right-click menu support.

The project evolves from a basic version with just selection-based saving to a more complete tool that adds right-click context menu saving and JSON export functionality. Along the way, the guide explains common debugging pitfalls like the "Extension context invalidated" error, the difference between chrome.storage.local and chrome.storage.sync, and how message passing works between components.

The final section covers packaging for the Chrome Web Store, including the $5 registration fee, the importance of keeping the .pem private key for updates, and the practical alternative of simply sharing the unpacked folder with friends instead of publishing.

Takeaways
Chrome extensions use four core components: manifest.json, Content Script, Popup, and Background (Service Worker in MV3).
Content Scripts run in the page context and can access the DOM but are isolated from the page's own JavaScript variables.
chrome.storage.local provides persistent storage tied to the extension, not to a specific domain, with no quota limit unlike chrome.storage.sync (100KB).
Message passing between components uses chrome.runtime.sendMessage and chrome.tabs.sendMessage, with async error handling via .catch() for pages without content scripts.
Right-click context menus must be registered by the Background script using the contextMenus permission.
After code changes, both the extension (via the extensions page refresh button) and open webpages must be refreshed for Content Script changes to take effect.
The 'Extension context invalidated' error occurs when the extension is reloaded but old page content scripts are still running; refreshing the page fixes it.
To publish on the Chrome Web Store, pay a $5 one-time fee, upload a zip (not crx), and keep the .pem private key for future updates.
Exporting data as JSON uses Blob and URL.createObjectURL to trigger a file download from the popup.
The extension stores a maximum of 100 quotes by default, configurable in the saveQuote function.
Conclusions

The guide's progression from a basic selection-saver to adding right-click and export features mirrors how real-world extension development often evolves—start minimal, then add features based on actual usage pain points.

The decision to use chrome.storage.local over sync is a practical tradeoff that many developers overlook: sync's 100KB limit is surprisingly easy to hit with text-heavy data, making local the safer default for anything beyond simple preferences.

The explicit handling of the async Promise rejection from chrome.tabs.sendMessage (using .catch() instead of try-catch) is a subtle but important MV3 detail that catches many developers off guard, especially those accustomed to synchronous patterns.

The guide's pragmatic advice to skip the Chrome Web Store and just share the unpacked folder reflects a real tension: the $5 fee and review process create friction for small tools, while sideloading works perfectly for personal or small-team use.

The floating button approach using position:fixed with z-index:2147483647 (the maximum) is a clever solution to the problem of overlaying content on arbitrary websites, but the scroll listener that removes the button is a UX compromise—it avoids position drift but means users must reselect after scrolling.

Concepts & terms
Manifest V3 (MV3)
The current version of the Chrome extension manifest specification. It replaces background pages with service workers, enforces stricter security policies, and requires declarative net request rules instead of the older webRequest API for modifying network requests.
Content Script
A JavaScript file that Chrome injects into web pages. It runs in an isolated world, meaning it can access and modify the page's DOM but cannot access the page's own JavaScript variables or functions. It communicates with other extension components via message passing.
Service Worker (Background)
In MV3, the background script runs as a service worker—a special JavaScript context that can run even when the extension's popup is closed. It handles browser-level events like right-click menu clicks, tab updates, and alarms. Unlike MV2 background pages, service workers are event-driven and can be terminated by the browser when idle.
chrome.storage.local vs chrome.storage.sync
Two storage APIs for Chrome extensions. 'local' stores data on the user's device with no size limit (though browsers may impose a practical limit). 'sync' syncs data across the user's Chrome browsers via their Google account but has a 100KB quota. The choice depends on whether cross-device access or storage capacity is more important.
Message Passing
The mechanism by which different parts of a Chrome extension communicate. Content scripts use chrome.runtime.sendMessage to send messages to the background service worker, and chrome.tabs.sendMessage to send messages from the background to content scripts. Messages are asynchronous and return Promises.
Source: juejin.cn ↗ Google Translate ↗ Backup ↗