Создание интерактивного расширения Chrome с помощью React
Расширения Chrome - забытые средние дети Интернета. Изо дня в день они выполняют огромный объем закулисной работы, блокируя рекламу, выявляя сделки, настраивая стили и многое другое. Тем не менее, большинство из них не обеспечивает пользователям прямого взаимодействия и взаимодействия, предлагаемого их более популярными веб- и мобильными братьями и сестрами. В результате многие расширения, как и студенты Милфордской академии, остаются незамеченными и не слышными.
Одна из причин, по которой расширения Chrome так задерживаются в разработке, заключается в том, что они часто полностью создаются с использованием необработанного JavaScript и HTML / CSS. Хотя этот минималистский подход может помочь уменьшить перегрузку браузера, это часто происходит за счет привлекательного взаимодействия с пользователем. В этой статье будет рассмотрено использование расширения для рендеринга более интерактивного приложения React в собственном модальном окне. После успешного развертывания нашего приложения мы доработаем его, чтобы получать заголовки, относящиеся к сайту, на котором мы находимся.
Идея
Наше расширение будет использовать приложение Create React в качестве шаблона. Предполагая, что вы установили Node.js версии 6 или выше, вы сможете сгенерировать необходимые файлы с помощью следующих команд терминала:
npx create-react-app headline-fetcher cd headline-fetcher
Файлы в этом каталоге будут вам знакомы, если вы раньше использовали приложение Create React, но если вы этого не сделали, не беспокойтесь - мы будем использовать только некоторые из них. Большая часть нашей работы будет находиться в папке public
, которая будет служить точкой входа для нашего расширения Chrome. В этой папке мы создадим два новых файла, background.js
и content.js
, которые обеспечат необходимый интерфейс для взаимодействия с пользователем.
Разделение задач между этими двумя файлами важно, потому что они служат двум разным целям в Chrome. Фоновый сценарий в background.js
взаимодействует с API браузера, обеспечивая функции, которые будут сохраняться во всех частях Интернета. Напротив, сценарий содержимого в content.js
выполняется всякий раз, когда загружается новая страница, обеспечивая доступ к элементам HTML на определенной странице. Эти различия станут более очевидными, когда мы рассмотрим код для каждого файла ниже.
Фоновый скрипт
Создайте файл background.js
в папке public
и добавьте следующий код:
chrome.contextMenus.create({ id: "HeadlineFetcher", title: "Get Headlines", contexts: ["all"] });
Контекстные меню в расширениях Chrome позволяют нам добавить параметр в контекстное меню браузера. После развертывания пользователи смогут щелкнуть правой кнопкой мыши в любом месте и увидеть такую опцию «Получить заголовки»:
На данный момент наша кнопка ничего не делает, поэтому нам нужно добавить некоторые функции.
chrome.contextMenus.onClicked.addListener(() => { chrome.tabs.query({active: true, currentWindow: true}, tabs => { chrome.tabs.sendMessage(tabs[0].id, {type: "getHeadlines"}); }); });
Это добавляет функцию обратного вызова, которая будет вызываться всякий раз, когда пользователь нажимает на опцию «Получить заголовки». Действие, выполняемое в рамках функции, идентифицирует вкладку, на которой в данный момент находится пользователь, и отправляет на эту вкладку сообщение с указанием получить заголовки.
Скрипт содержимого
Теперь, когда у нас есть некоторые функции в background.js
, мы можем перейти к созданию нашего content.js
файла в той же папке. Как показано выше, расширение отправит сообщение из фонового сценария в сценарий содержимого. Тогда было бы разумно добавить своего рода слушателя для этих сообщений:
chrome.runtime.onMessage.addListener(request => { if (request.type === "getHeadlines") { // DO SOMETHING } });
В случае с нашим приложением после запроса «getHeadlines» мы хотим открыть содержимое нашего приложения React. Итак, как мы это сделаем? Через диалоговые теги! Внедряя HTML <dialog>
, мы можем гарантировать, что наше приложение отображается как отдельное модальное окно, что делает его более отличным от содержимого страницы и более трехмерным для пользователя.
const modal = document.createElement("dialog"); modal.setAttribute("style", "height:40%"); modal.innerHTML = `<iframe id="headlineFetcher" style="height:100%"></iframe> <div style="position:absolute; top:0px; left:5px;"> <button>x</button> </div>`; document.body.appendChild(modal); const dialog = document.querySelector("dialog"); dialog.showModal();
Теперь у нас есть элемент диалога с внутренним iframe, дающий нам возможность получить доступ к ресурсам URL через наше расширение. Стиль этого элемента будет меняться в зависимости от того, на каком сайте мы находимся, поэтому не привязывайтесь слишком к каким-либо конкретным спецификациям CSS.
Теперь перейдем к ключевому элементу соединительной ткани: вставим наше приложение React в iframe. Поскольку index.html
является точкой входа для нашего приложения React, эти строки будут гарантировать, что приложение будет обслуживаться при каждом открытии модального окна:
const iframe = document.getElementById("headlineFetcher"); iframe.src = chrome.extension.getURL("index.html"); iframe.frameBorder = 0;
Наш внедренный HTML также включает кнопку, которая позволит нам закрыть диалоговое окно, когда это необходимо. Хотя кнопка в настоящее время ничего не делает, мы можем добавить функциональность с помощью простого прослушивателя щелчков.
dialog.querySelector("button").addEventListener("click", () => { dialog.close(); });
И теперь мы закончили как фоновые, так и контентные скрипты!
Манифест
Последний файл, который нам нужно будет изменить перед развертыванием нашего расширения, - это манифест. Подобно package.json
проекта Node, manifest.json
- это файл, в котором описаны все необходимые метаданные о расширении Chrome. Наша public
папка уже содержит manifest.json
, поэтому нам не нужно создавать новую. Хотя этот файл был инициализирован для другой цели в приложении Create React, мы можем воспользоваться этим семантическим удобством при разработке нашего расширения.
Удалите существующий код в manifest.json
и добавьте следующее:
{ "name": "Headline Fetcher", "manifest_version": 2, "version": "0.0.1", "description": "Retrieves headlines relevant to the website you are currently on", "content_scripts": [{ "js": [ "content.js"], "matches": [ "<all_urls>"] }], "background": { "scripts": ["background.js"] }, "permissions": ["contextMenus", "tabs"], "web_accessible_resources" : ["*.html"] }
Это дает базовое описание нашего приложения и определяет content.js
и background.js
в качестве сценариев содержимого и фона, соответственно. Он также запрашивает разрешение для частей API Chrome, с которыми мы будем взаимодействовать (контекстные меню и вкладки), и разрешает загрузку ресурсов HTML, что позволит нам внедрить index.html
нашего приложения React.
Развертывание
Чтобы подготовить расширение к развертыванию, мы можем использовать собственный сценарий сборки приложения Create React. Выполнение следующей команды гарантирует, что наше приложение настроено правильно:
npm run build
Это создает папку build
для нашего расширения, которую мы сможем использовать для развертывания. Чтобы загрузить его в Chrome, зайдите в chrome: // extensions в своем браузере, переключите переключатель Режим разработчика в правом верхнем углу, затем нажмите Загрузить распакованный. Если выбрать build
в каталоге приложения, необходимо загрузить расширение сборщика заголовков.
Вот и все - вы официально сделали расширение Chrome! Теперь у вас должна быть возможность протестировать его на большинстве веб-сайтов, щелкнув правой кнопкой мыши и выбрав «Получить заголовки». Если модальное окно не отображается, попробуйте обновить страницу; это обеспечит загрузку сценария содержимого вместе с другими элементами страницы.
Разработка нашего приложения
Теперь, когда мы успешно внедрили наше собственное приложение React на веб-сайт, мы можем дополнить его, чтобы получать заголовки, относящиеся к сайту, на котором мы находимся. Чтобы настроить сборщик заголовков, выполните следующие действия:
- Получите API-ключ из News API
- Установите axios с помощью команды
npm i axios
и импортируйте его вApp.js
- Добавьте
/* global chrome */
в первую строкуApp.js
(это позволит нашему линтеру узнать, что мы обращаемся к методам браузера Chrome)
Теперь добавьте в App.js
конструктор с начальными состояниями для домена и соответствующими заголовками:
/* global chrome */ import React, { Component } from 'react'; import logo from './logo.svg'; import './App.css'; import axios from 'axios'; class App extends Component { constructor(props) { super(props); this.state = { domain: '', headlines: [] } }
Первое, что нужно сделать нашему приложению, - это определить домен URL-адреса, на котором в данный момент находится пользователь. Поскольку это должно происходить всякий раз, когда открывается наше приложение React, мы можем поместить это в componentDidMount()
метод.
componentDidMount() { chrome.tabs.query({active: true, currentWindow: true}, tabs => { const url = new URL(tabs[0].url); const domain = url.hostname; this.setState({ domain }); this.getHeadlines(domain); }); }
Как только у нас будет доступ к домену, наш getHeadlines
метод будет извлекать соответствующие ресурсы из API новостей и использовать первые пять результатов в качестве заголовков.
getHeadlines(query) { axios.get('https://newsapi.org/v2/everything',{ params: { q: query, language: 'en', apiKey: {{ YOUR KEY HERE }} } }).then(results => { this.setState({ headlines: results.data.articles.slice(0,5) }); }).catch(error => { console.log('Error in obtaining headlines', error); }); }
Наконец, нам нужно обновить наш render()
метод, чтобы отображать эти результаты.
render() { return ( <div className="App"> <h1 className="App-title">{this.state.domain}</h1> Top Headlines: {this.state.headlines.map(headline => ( <h4 className="link" onClick={() => { window.open(headline.url)}}>{headline.title}</h4>))} </div> ); }
Вам может быть интересно, почему мы используем здесь функцию onClick
вместо того, чтобы просто заключать заголовки в теги<a>
. Причина в том, что мы хотим, чтобы наши ссылки открывались в окне браузера, а не внутри iframe (что и делают обычные теги <a>
). Хотя это означает, что мы не получим стили HTML-ссылок по умолчанию, мы можем добавить в наш App.css
файл следующий косметический пластырь:
.link { color: blue; text-decoration: underline; cursor: pointer; }
И мы официально создали бета-версию нашего приложения! Запустите npm run build
, чтобы настроить наши новые обновления и обновить нашу версию сборщика заголовков на chrome: // extensions. Затем попробуйте посетить разные веб-сайты и щелкните правой кнопкой мыши, чтобы увидеть, как они освещаются в новостях!
Чтобы сослаться на завершенный код для нашего расширения, вы можете посетить это репо: https://github.com/ctrobins/headline-fetcher.
Заключение
Расширения Chrome предоставляют разработчикам множество способов изменить опыт просмотра, включая сценарии контента, которые могут быть нацелены на определенные элементы веб-сайта. Теоретически это создает уникальные преимущества как перед веб-платформами, так и перед мобильными платформами, поскольку позволяет расширениям повсеместно работать через Интернет. Несмотря на этот потенциал, многие популярные расширения предпочитают завершать свою работу на необработанном JavaScript, избегая взаимодействия с любыми интерфейсными библиотеками или фреймворками. Хотя такой скрытый подход может сделать расширения легкими и ненавязчивыми, он также с большей вероятностью сделает их легко забываемыми.
Внедрение приложения React в его собственное модальное окно может предложить новый уровень интерактивности для пользователей Chrome. Поскольку приложение содержится в собственном iframe, оно может получать доступ к внешним ресурсам, а также использовать определенные функции API браузера Chrome. Хотя в этой статье рассматривается только один пример приложения - простой сборщик заголовков - возможности использования этой технологии поистине безграничны. Опытные разработчики React должны продолжить изучение других альтернатив, чтобы лучше понять, как Chrome и React могут работать в тандеме.
📝 Прочтите этот рассказ позже в Журнале.
🗞 Просыпайтесь каждое воскресное утро и слышите самые интересные истории, мнения и новости недели, ожидающие в вашем почтовом ящике: Получите примечательный информационный бюллетень›