Статьи

Создайте свое собственное расширение Chrome, используя Angular 2 и TypeScript

Эта статья была рецензирована Дэном Принцем . Спасибо всем рецензентам SitePoint за то, что сделали контент SitePoint как можно лучше!

Расширения Chrome — это небольшие веб-приложения, которые добавляют функции в браузер Google Chrome. Они могут расширять и настраивать поведение браузера, инструменты разработчика или страницу новых вкладок. Расширения можно загрузить из Интернет-магазина Chrome .

В этом руководстве мы создаем расширение для Chrome, которое позволяет нам сохранять URL-адреса веб-сайтов и отображать их на странице каждой новой вкладки. Конечно, есть собственные закладки Chrome, но мы хотим интегрировать закладки прямо на страницу новой вкладки и контролировать их внешний вид.

Вы можете найти полный код проекта в репозитории GitHub здесь , и можете свободно устанавливать работающую версию расширения (с несколькими дополнительными функциями).

Что мы строим

Давайте начнем с краткого обзора того, что мы хотим развивать. На снимке экрана показано, что мы создадим список, состоящий из переменного количества закладок. Закладки — это ссылки, которые открывают соответствующие URL при нажатии.

Скриншот списков закладок.

Каждая закладка нуждается в двух частях информации: ее название и ее URL. Будет возможность отредактировать эту информацию, а другую — удалить закладку. Для редактирования закладки нам понадобится форма с двумя полями ввода и кнопкой отправки.

Снимок экрана модального редактирования.

Для обработки пользовательского ввода и рендеринга списка мы будем использовать Angular 2 с TypeScript . Angular 2 отлично подходит для создания клиентских приложений и хорошо работает с TypeScript, типизированным супер-набором JavaScript. Если вы хотите начать с введения в Angular 2 и TypeScript, я рекомендую эту статью .

Вам не нужно больше, чем текстовый редактор и менеджер пакетов узлов (npm), чтобы следовать этому руководству. Однако для публикации расширения требуется учетная запись разработчика Google, которую можно создать здесь .

Настройка и структура

Настало время поработать над реальным приложением, поэтому давайте создадим новую папку проекта:

mkdir sitepoint-extension && cd sitepoint-extension 

TypeScript Config

Далее мы добавим файл tsconfig.json в папку проекта. Этот файл инструктирует компилятор TypeScript, как скомпилировать наши .ts файлы.

 { "compilerOptions": { "target": "ES5", "module": "system", "moduleResolution": "node", "sourceMap": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, "removeComments": false, "noImplicitAny": false }, "exclude": [ "node_modules" ] } 

Важными настройками являются compilerOptions . Там мы указываем, что целевой версией ECMAScript должна быть ES5 и что генерация кода модуля должна происходить с SystemJS ( "module": "system" ).

С помощью "sourceMap": true будут генерироваться "sourceMap": true файлы карты источника. Эти файлы .map отлично подходят для отладки, поскольку с их помощью браузер может отобразить скомпилированный код ES5 в код TypeScript.

Для этого урока нам не нужно больше знать о файле tsconfig.json . С полной документацией можно ознакомиться здесь .

Package.json

Мы используем npm для установки необходимых нам пакетов и npm-скрипты для создания некоторых задач разработки и сборки. Для этого мы добавляем package.json в наш основной каталог.

Angular 2 в настоящее время находится в бета-версии. Для этого урока я использовал бета-версию 7. Конечно, вы можете использовать более новую версию, но я не могу гарантировать, что все будет работать гладко, потому что фреймворк все еще может измениться.

 { "name": "SitePointBookmarkExtension", "description": "A Chrome Extension for Bookmarks", "version": "1.0.0", "scripts": { "lite": "lite-server", "tsc": "tsc", "tsc:w": "tsc -w", "start": "concurrently \"npm run tsc:w\" \"npm run lite\"" }, "dependencies": { "angular2": "2.0.0-beta.7", "systemjs": "0.19.22", "es6-promise": "^3.0.2", "es6-shim": "^0.33.3", "reflect-metadata": "0.1.2", "rxjs": "5.0.0-beta.2", "zone.js": "0.5.15" }, "devDependencies": { "concurrently": "^2.0.0", "lite-server": "^2.1.0", "typescript": "^1.7.5" } } 

Теперь давайте установим пакеты с

 npm install 

Обратите внимание, что есть некоторые подготовленные сценарии npm, которые можно выполнить с помощью команды npm run [script name] . На данный момент существует четыре сценария, которые помогут нам скомпилировать наши файлы TypeScript и создать сервер разработки.

manifest.json

Прежде чем мы создадим наше приложение, нам нужно добавить еще один файл .json , manifest.json . Этот файл необходим для каждого расширения Chrome, так как в нем указывается информация о том, как интернет-магазин и браузер должны обрабатывать расширение.

Мы закончим файл позже, но сейчас давайте просто добавим обязательные и рекомендуемые свойства:

 { "manifest_version": 2, "name": "SitePoint Bookmark Extension", "short_name": "Make the most of a new tab", "description": "This extension helps you save your favorite webpages.", "version": "1.0.0", "author": } 

Компонент закладки

Angular 2 — это основанная на компонентах инфраструктура, и наш первый компонент будет отдельной закладкой. Этот компонент позже будет дочерним, так как мы будем создавать родительский компонент списка, который будет содержать закладки.

Скриншот архитектуры компонента.

Давайте создадим новую папку scripts и, внутри, файл с именем bookmark.component.ts .

 // To create a component, we need Angular's "Component" function. // It can be imported from the "angular2/core" module. import { Component } from 'angular2/core'; // A component decorator tells Angular that the "BookmarkComponent" class // is a component and adds its meta data: the selector and the template. @Component({ selector: 'sp-bookmark', template: '<h1>Bookmark</h1>' }) // The "BookmarkComponent" module exports the "BookmarkComponent" class, // because we will need it in other modules, // eg to create the bookmark list. export class BookmarkComponent { } 

Для начальной загрузки компонента BookmarkComponent нам нужно добавить еще один файл, который мы называем boot.ts :

 // We need to reference a type definition (or 'typings') file // to let TypeScript recognize the Angular "promise" function // (we'll need this later on) otherwise we'll get compile errors. /// <reference path="../node_modules/angular2/typings/browser.d.ts" /> // Angular's "bootstrap" function can be imported // from the angular2/platform/browser module. // Since we want to bootstrap the "BookmarkComponent", // we have to import it, too. import { bootstrap } from 'angular2/platform/browser' import { BookmarkComponent } from './bookmark.component' // We can now bootstrap the "BookmarkComponent" as the root component. bootstrap( BookmarkComponent ); 

Другой новый файл, system.config.js , настраивает загрузчик модулей SystemJS. Он загрузит файл boot.ts , который мы только что создали.

 // SystemJS is the module loader for the application. // It loads the libraries and our modules and then catches and logs errors, // that may occur during the app launch. System.config({ packages: { scripts: { format: 'register', defaultExtension: 'js' } } }); System.import('scripts/boot') .then(null, console.error.bind(console)); 

Прежде чем мы сможем увидеть что-либо в браузере, последнее, что нам нужно, это файл index.html . Мы помещаем файл в корень каталога нашего проекта на том же уровне, что и файлы .json .

 <html> <head> <title>SitePoint Bookmark Extension</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- We load the libraries we need directly from the "node_modules" folder. In a more complex project, we would use a different approach here, eg working with a build tool like gulp.js or Angular-CLI. --> <script src="node_modules/angular2/bundles/angular2-polyfills.js"></script> <script src="node_modules/systemjs/dist/system.src.js"></script> <script src="node_modules/rxjs/bundles/Rx.js"></script> <script src="node_modules/angular2/bundles/angular2.dev.js"></script> <!-- Load the SystemJS config --> <script src="scripts/system.config.js"></script> </head> <body> <!-- Here we are using the selector "sp-bookmark", which we defined as component meta data in the "BookmarkComponent" decorator. Everything inside the element tag will only be seen until our application is loaded. --> <sp-bookmark>Loading bookmarks...</sp-bookmark> </body> </html> 

Давайте проверим, что мы сделали, скомпилировав файлы TypeScript и запустив сервер:

 npm run start 

Если все работает правильно, мы должны увидеть, как браузер открывает новую вкладку и показывает параграф «Загрузка закладок…» перед отображением нашего шаблона, заголовок «Закладка».

Скриншот заголовка "Закладка".

Шаблон закладки

На данный момент наш шаблон закладок состоит только из статического заголовка, но это не то, что мы на самом деле хотим. Чтобы отобразить полную разметку закладки, мы будем ссылаться на отдельный HTML-шаблон с именем bookmark.html .

Давайте создадим новые templates папок в корне нашего проекта и в нашем новом шаблоне закладок:

 <div class="bookmark"> <!-- We are using the interpolation template syntax to bind the component properties "bookmark.name" and "bookmark.url" to our template. --> <a href="{{bookmark.url}}" class="bookmark__link">{{bookmark.name}}</a> <!-- Every bookmark has two buttons, to let users edit and delete a bookmark.--> <span class="bookmark__button-wrapper"> <!-- The edit button has an event binding "(click)", which sets the component variable "submitted" to true. It also has a property binding "[hidden]", which hides the button, when the variable "submitted" is true. --> <button class="bookmark__button" (click)="submitted=true" [hidden]="submitted"> Edit </button> <!-- The delete button uses an event binding "(click)", that calls the component function "onDelete()", when a user clicks it. --> <button class="bookmark__button" (click)="onDelete(bookmark)">Delete</button> </span> <!-- To edit a bookmark, we show a form if the value of the property "submitted" is false. --> <div class="bookmark__form-wrapper" [hidden]="!submitted"> <!-- The form has a submit button, which allows us to use the Angular directive "ngSubmit". It calls another component function "onSubmit()". --> <form class="bookmark__form" (ngSubmit)="onSubmit()"> <label class="bookmark__form__label">Name: </label> <!-- There are two input fields for the two properties "bookmark.name" and "bookmark.url". Both use the two-way data binding template syntax, to change the property values. --> <input class="bookmark__form__input" [(ngModel)]="bookmark.name" placeholder="Name"/> <label class="bookmark__form__label">URL: </label> <input class="bookmark__form__input" [(ngModel)]="bookmark.url" placeholder="URL"/> <button class="bookmark__form__button" type="submit">Done</button> </form> </div> </div> 

Ссылка на templateUrl заменяет метаданные template в декораторе BookmarkComponent :

 @Component({ selector: 'sp-bookmark', templateUrl: './templates/bookmark.html' }) 

Скриншот закладки без свойств.

Браузер показывает обе кнопки Edit и Delete вместе с формой. Интерполяции остаются пустыми, потому что свойства bookmark.name и bookmark.url не были объявлены.

Давайте добавим отсутствующие свойства в BookmarkComponent . Мы можем использовать динамические данные из предустановки или localStorage позже, но сейчас давайте придерживаться жестко закодированной закладки.

 import { Component } from 'angular2/core'; // We are using an interface to represent a bookmark. // A single bookmark is now strongly typed: // it has to have two properties "name" and "url", // which both must be a string. interface Bookmark { name : string, url : string } @Component({ selector: 'sp-bookmark', templateUrl: './templates/bookmark.html' }) export class BookmarkComponent { // The bookmark property is of the type "Bookmark", // defined in the interface above. bookmark : Bookmark = { name : 'SitePoint', url : 'https://sitepoint.com' } // Setting the default value for the "submitted" property. submitted = false; } 

При переключении в браузер он должен показать нам работающую гиперссылку с двумя кнопками. Форма в настоящее время скрыта, так как мы устанавливаем значение свойства, submitted в false. Функциональность для редактирования и удаления закладки еще не работает, так как мы еще не реализовали ее.

Скриншот закладки со статическими данными.

Списки закладок

Чтобы создать список закладок и заполнить его некоторыми данными, мы создадим родительский компонент list.component.ts .

 import { Component } from 'angular2/core'; import { Bookmark } from './bookmark.component'; import { BookmarkComponent } from './bookmark.component'; // The ListComponent metadata defines the component's selector, // the url of the template and the directives used in this template. @Component({ selector: 'sp-list', templateUrl: './templates/list.html', directives: [ BookmarkComponent ] }) export class ListComponent { } 

Нам также нужно изменить компонент, упомянутый в файле boot.ts и элемент, используемый в index.html . Мы хотим, чтобы наше приложение загрузило ListComponent , который, в свою очередь, загрузит BookmarkComponent .

 /// <reference path="../node_modules/angular2/typings/browser.d.ts" /> import { bootstrap } from 'angular2/platform/browser'; import { ListComponent } from './list.component'; bootstrap( ListComponent ); 
 <body> <sp-list>Loading bookmarks...</sp-list> </body> 

Данные по умолчанию

В этом состоянии список закладок будет пуст для новых пользователей, так как данные по умолчанию отсутствуют. Впервые пользователи должны увидеть некоторые закладки, поэтому мы создадим некоторые данные закладок по умолчанию в новом файле с именем list.data.constant.ts :

 // We are using a constant here, // because we do not want to change the default data. export const BOOKMARKS = [ { 'name': 'Twitter', 'url': 'https://twitter.com' }, { 'name': 'Github', 'url': 'https://github.com' }, { 'name': 'Sitepoint', 'url': 'https://sitepoint.com' }, { 'name': 'Codepen', 'url': 'https://codepen.com' } ]; 

Служба рассылки

Мы не хотим, чтобы ListComponent решал, следует ли использовать данные по умолчанию или данные, хранящиеся в localStorage , поэтому новый файл с именем list.service.ts будет обрабатывать импорт данных.

 import { BookmarkComponent } from './bookmark.component'; import { BOOKMARKS } from './list.data.constant'; // Importing the "Injectable" function from the angular2/core module // and adding the "@Injectable" decorator lets us use dependency injection // in this service. import { Injectable } from 'angular2/core'; @Injectable() export class ListService { // We create three variables: // one for possible data in the localStorage, // one for our default data and // one for the data our service should return. bookmarksLocalStorage = JSON.parse( localStorage.getItem('sp-bookmarklist') ); bookmarksDefaultData = BOOKMARKS; bookmarksToReturn = this.bookmarksDefaultData; // The "getBookmarks()" function checks if there is data in the local storage. // If there is, we return this data, // if there isn't we return the default data. getBookmarks() { if ( this.bookmarksLocalStorage !== null ) { this.bookmarksToReturn = this.bookmarksLocalStorage; } return Promise.resolve( this.bookmarksToReturn ); } // A "setBookmarks()" function saves new data in the local storage. setBookmarks( bookmarks : Object ) { localStorage.setItem( 'sp-bookmarklist', JSON.stringify( bookmarks ) ); } } 

Теперь давайте воспользуемся сервисом в нашем ListComponent . Мы должны импортировать сервис, добавить его в компонент в качестве поставщика и внедрить его, передав его в закрытую переменную в функции конструктора.

Кроме того, мы должны добавить хук жизненного цикла OnInit , который ListComponent как только активируется ListComponent . Эта функция будет использовать ListService для получения списка закладок. Поскольку мы получим закладки асинхронно, мы используем обещания ES2015 и функции стрелок .

 import { Component } from 'angular2/core'; import { OnInit } from 'angular2/core'; import { Bookmark } from './bookmark.component'; import { BookmarkComponent } from './bookmark.component'; import { ListService } from './list.service'; @Component({ selector: 'sp-list', templateUrl: './templates/list.html', directives: [ BookmarkComponent ], providers: [ ListService ] }) export class ListComponent implements OnInit { public bookmarks : Object; constructor( private listService : ListService ) {} // The function "getBookmarkLists" requests the bookmarks asynchronously. // When the promise is resolved, the callback function assigns // the bookmarks to the component's bookmarks property. getBookmarkLists() { this.listService.getBookmarks().then( bookmarks => this.bookmarks = bookmarks ); } // The "ngOnInit" function gets called, when the component gets activated. ngOnInit() { this.getBookmarkLists(); } } 

Шаблон списка

Чего сейчас не хватает, так это шаблона list.html . Итак, давайте создадим один и поместим его в папку templates . Шаблон содержит только элемент <section> с неупорядоченным списком. Элемент списка в этом списке повторяется со встроенной структурной директивой *ngFor . Внутри элемента списка используется селектор <sp-bookmark> компонента BookmarkComponent .

 <section class="bookmarklist-container bookmarklist-container--blue-dark"> <ul class="bookmarklist__sublist"> <!-- Angular's built-in structural directive "*ngFor" instantiates a list element for each bookmark. The hash prefix means, that the private variables "bookmark" and "i" are created. They can be used on the list element's child elements.--> <li *ngFor="#bookmark of bookmarks; #i = index"> <!-- The template property binding "[bookmark]" sets the value to the component property "bookmark". In addition there are two custom component event bindings "(bookmarkChanged)" and "(bookmarkDeleted)". Whenever one of these events were raised, their respective functions will be executed. --> <sp-bookmark [bookmark]="bookmark" (bookmarkChanged)="setBookmarks()" (bookmarkDeleted)="deleteBookmark(bookmark, i)"></sp-bookmark> </li> </ul> </section> 

Чтобы все работало, нам нужно сделать одно небольшое изменение в файле bookmark.component.ts . Мы используем привязку свойства шаблона [bookmark] . Таким образом, это свойство должно быть объявлено как входное свойство в декораторе компонента:

 @Component({ selector: 'sp-bookmark', templateUrl: './templates/bookmark.html', inputs : ['bookmark'] }) 

Браузер теперь показывает список с данными по умолчанию.

Скриншот закладки с данными по умолчанию.

События

На данный момент нам не хватает только функциональности для редактирования и удаления закладки. Щелчки для редактирования и удаления закладки происходят в закладке дочернего компонента, и родительский компонент должен реагировать на них. Нам нужен способ позволить дочернему компоненту общаться с родительским компонентом. Это может быть достигнуто с помощью пользовательских событий.

Мы уже подготовили обработчики нажатий для обеих кнопок в шаблоне bookmark.html и добавили два прослушивателя событий (bookmarkChanged) и (bookmarkDeleted) для обоих событий в шаблоне list.html . Давайте добавим некоторые источники событий в bookmark.component.ts :

 import { Component } from 'angular2/core'; import { Output } from 'angular2/core'; import { EventEmitter } from 'angular2/core'; // [...] I left some code out of the example to save space. export class BookmarkComponent { bookmark : Bookmark; submitted = false; // Events flow outside the child component and therefor need an output decorator. @Output() bookmarkChanged : EventEmitter<any> = new EventEmitter(); @Output() bookmarkDeleted : EventEmitter<any> = new EventEmitter(); // Whenever a user clicks on "Done" after editing a bookmark, // an event is fired, which indicates that the bookmark was changed. // To hide the form, the "submitted" property is set to false again. onSubmit( bookmark : Bookmark ) { this.submitted = false; this.bookmarkChanged.emit( bookmark ); } // When the "Delete" button is clicked, the event "bookmarkDeleted" // will be fired. onDelete( bookmark : Bookmark ) { this.bookmarkDeleted.emit( bookmark ); } } 

Далее мы должны отреагировать на эти события в ListComponent с помощью двух функций setList() и deleteBookmark() :

 // [...] export class ListComponent implements OnInit { public bookmarks : Array< Object >; constructor( private listService : ListService ) {} getBookmarkLists() { this.listService.getBookmarks().then( bookmarks => this.bookmarks = bookmarks ); } ngOnInit() { this.getBookmarkLists(); } // setList uses the "ListService" to save the complete list. setList() { this.listService.setBookmarks( this.bookmarks ); } // The function deletes the bookmark and saves the complete list. deleteBookmark( bookmark : Bookmark, i : number ) { this.bookmarks.splice( i, 1 ); this.setList(); } } 

Сейчас все работает. Проверьте обе функции, отредактировав или удалив закладку.

стайлинг

Прежде чем добавить функциональность расширения для создания новых закладок с помощью кнопки расширения Chrome, потребуется несколько секунд, чтобы добавить немного CSS. Поскольку мы уже подготовили разметку с помощью классов, нам просто нужно добавить файл CSS в новую папку CSS и сослаться на него в index.html . Вы можете скачать CSS здесь .

 <html> <head> <title>SitePoint Bookmark Extension</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="css/main.css"> <!-- [...] --> </html> 

Приложение теперь выглядит так:

Скриншот стилизованного и рабочего списка.

Особенности расширения Chrome

Мы готовы добавить некоторые функции расширения Chrome. Мы хотим добавить значок на панель инструментов браузера Chrome, который при нажатии будет сохранять текущую открытую страницу в нашем списке закладок. Кроме того, новая вкладка должна открывать список закладок Angular 2 вместо новой страницы Chrome по умолчанию. Давайте начнем с использования Chrome JavaScript API для сохранения текущей открытой веб-страницы в нашем списке.

Страница события

Мы должны добавить дополнительный сценарий в папку scripts , которая будет загружаться, когда пользователь нажимает значок на панели инструментов. Поскольку для этой задачи мы используем страницы событий , мы будем вызывать новый сценарий eventPage.ts . Некоторый код должен быть выполнен, когда пользователь нажимает значок на панели инструментов. Поэтому мы используем слушатель Chrome.browserAction.onClicked . Чтобы получить заголовок и URL открытой в данный момент вкладки, необходим API Chrome.tabs .

 ///<reference path="chrome/chrome.d.ts" /> import { Injectable } from 'angular2/core'; import { ListService } from './list.service'; @Injectable() export class EventPage { // The event listener should be set when the "EventPage" class is initialized. // Therefore we are using the constructor for adding it to the "Chrome.browserAction". // To set and get the bookmarkLists, we are using the "ListService". constructor ( listService : ListService ) { let bookmarkLists : Array< Object >; // The "Chrome.browserAction" object throws an error, // when it is not available in development mode. // This is why we are only logging a message, // if it is undefined. if (typeof chrome.browserAction !== 'undefined') { // The Chrome "browserAction" is responsible for the icon in the Chrome toolbar. // This is when we are get the latest list of bookmarks from the "ListService" // and call the function "getSelectedTab" after the promise is resolved. chrome.browserAction.onClicked.addListener( function ( tab ) { listService.getBookmarks().then( bookmarkLists => { bookmarkLists = bookmarkLists; getSelectedTab( bookmarkLists ); }); }); } else { console.log( 'EventPage initialized' ); } // The Chrome tabs API gives us access to the current tab, // its title, and its url, which we are using to add a new bookmark // and save the list of bookmarks again with the "ListService". function getSelectedTab( bookmarkLists ) { chrome.tabs.getSelected( null, function ( tab ) { let newBookmark : Object = { name : tab.title, url : tab.url }; bookmarkLists.push( newBookmark ); listService.setBookmarks( bookmarkLists ); }); } } } 

Первая строка нуждается в пояснении, потому что нам нужно больше файлов, чтобы скрипт мог быть скомпилирован. Чтобы использовать функции Chrome JavaScript API в TypeScript, мы должны рассказать TypeScript о глобальных объектах API. Для этого нам нужно добавить ссылочный путь ///<reference path="chrome/chrome.d.ts" /> к сценарию, а также файлы определения TypeScript ( .d.ts ) в папку script нашего проекта. Файл, который нам нужен, можно скачать здесь .

manifest.json

Все объединяется в файле manifest.json . Давайте добавим необходимые свойства один за другим. Сначала мы добавим ссылку на значки расширений и значки панели инструментов с помощью всплывающей подсказки:

 "icons": { "19": "Icon-19.png", "38": "Icon-38.png", "48": "Icon-48.png", "128": "Icon-128.png" }, "browser_action": { "default_icon": { "19": "Icon-19.png", "38": "Icon-38.png" }, "default_title": "Open a new tab to view your bookmarks." } 

Страница Chrome Override будет загружать наше приложение Angular всякий раз, когда пользователь открывает новую вкладку:

 "chrome_url_overrides" : { "newtab": "index.html" } 

Свойство background с объектом persistent: false добавляет прослушиватели событий Chrome для сценария страницы события:

 "background": { "page": "index.html", "persistent": false } 

Далее мы должны предоставить настройки для политики безопасности контента (CSP) . Политика безопасности содержимого по умолчанию для "manifest_version": 2 — это script-src 'self'; object-src 'self' script-src 'self'; object-src 'self' . В нашем случае мы должны добавить unsafe-eval потому что одна из наших библиотек зависит от оцененного JavaScript.

 "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'" 

Последнее, что мы должны добавить в manifest.json , это свойство полномочий, которое позволяет нам получить доступ к информации об активной вкладке.

 "permissions": ["activeTab"] 

Проверьте расширение в Chrome

Мы готовы протестировать расширение в действии. Просмотр chrome://extensions/ дает обзор текущих установленных расширений, а также возможность загрузить распакованное расширение. Чтобы загрузить расширение в Интернет-магазин Chrome , его нужно сжать как .zip . Давайте добавим еще два сценария npm, которые помогут нам скопировать нужные нам модули в папку lib и сжать все:

 "copy-lib": "mkdir lib && cp node_modules/{angular2/bundles/angular2-polyfills.js,systemjs/dist/system.src.js,rxjs/bundles/Rx.js,angular2/bundles/angular2.dev.js} lib/", "compress": "zip -r -X $npm_package_name-$npm_package_version.zip ./{templates/*,lib/*,css/*,scripts/*.js,*.html,manifest.json,*.png,*.ico}" 

Не забудьте изменить ссылку на эти файлы в файле index.html :

 <script src="lib/angular2-polyfills.js"></script> <script src="lib/system.src.js"></script> <script src="lib/Rx.js"></script> <script src="lib/angular2.dev.js"></script> 

Мы запускаем сценарии npm с

 npm run copy-lib npm run compress 

И мы сделали! Чтобы проверить расширение в Chrome, зайдите в chrome://extensions/ , активируйте режим разработчика и используйте кнопку «Load unpacked extension» для загрузки распакованной zip папки. На новой вкладке должно отображаться наше приложение для закладок, а нажатие на значок нового расширения на другом веб-сайте на панели инструментов должно добавить новую закладку в этот список. Вкладка расширения должна быть обновлена , чтобы сделать новую закладку видимой.

Примечание . Для отладки сценария eventPage мы можем открыть окно отладки на странице chrome://extensions/ . Существует гиперссылка на фоновые страницы, которая называется «Проверка представлений».

Что делать дальше?

Наше приложение для закладок может быть улучшено:

  • Вы можете добавить дополнительные функции, такие как изменение цветовой схемы и импорт закладок.
  • Есть также улучшения UX, например всплывающее окно обратной связи после того, как пользователь успешно добавил новую закладку.
  • Возможно, мы захотим добавить модульные тесты или сквозные тесты в наше приложение. Руководство разработчика по тестированию приложений Angular 2 можно найти здесь .
  • Лучше будет процесс сборки и некоторые переменные окружения. Вместо сценариев npm мы могли бы использовать Angular-CLI, который имеет дополнительные параметры для создания лесов, локального сервера разработки и сквозных тестов.
  • И, конечно, вы можете опубликовать свое приложение в Интернет-магазине. Это можно сделать из панели инструментов разработчика Chrome .

Я надеюсь, что этот урок дал вам первое впечатление и вдохновение для разработки расширения Chrome с Angular 2 и TypeScript. Если вы хотите углубиться в эти темы, я могу порекомендовать следующие ресурсы: