Статьи

Создайте браузер с вкладками, используя Node-Webkit и AngularJS

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

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

Затем в 2011 году Роджер Ван представил нечто, называемое Node-Webkit . Node-Webkit (который с тех пор был переименован в NW.js) представляет собой комбинацию Node.js и встроенного браузера WebKit, который позволяет разработчикам использовать веб-технологии (например, HTML, CSS и JavaScript) для разработки собственных приложений. Да все верно! Мы пишем нативные приложения, используя все возможности, которые поддерживаются в наших современных браузерах. Например, CSS3-анимации, WebGL, WebRTC, видео, аудио и многое другое могут быть включены в собственное приложение.

В этом руководстве я собираюсь продемонстрировать, как использовать возможности NW.js для создания браузера с вкладками, который можно развернуть во всех основных операционных системах. Как и всегда, код этого руководства можно найти в нашем репозитории GitHub .

Начальная настройка

Как следует из названия, NW.js основан на Node, поэтому он должен быть установлен в вашей операционной системе. Мы также будем использовать npm (менеджер пакетов узлов). Если вам нужна помощь в настройке любого из этих параметров, ознакомьтесь с нашим руководством: Руководство для начинающих по npm .

Далее нам понадобится папка для нашего проекта:

mkdir sitepoint-browser && cd sitepoint-browser 

Нам также понадобятся некоторые зависимости, которые должны быть установлены глобально (а именно Yeoman, Grunt и Bower):

 npm install -g yo grunt bower 

Из них Yeoman (AKA Yo) — это инструмент для динамического создания повседневных проектов, что позволяет избежать препятствий, связанных с созданием многократно используемых структур проектов вручную. Grunt — это бегун, который использует Yeoman. Он также использует npm и Bower для установки необходимых зависимостей.

Далее мы установим генератор Йо -отученного . Вы можете сделать это глобально или локально. Здесь я сделаю это глобально:

 npm install -g generator-wean 

Сам по себе NW.js имеет несколько генераторов, но генератор отлученных (созданный мной) поставляется вместе с ExpressJS и AngularJS, что облегчает их установку и настройку. WEAN расшифровывается как Webkit, Express, Angular и Node, как и популярный MEAN .

Теперь наше скелетное приложение может быть сгенерировано одной командой:

 yo wean 

Йо — интерактивный парень, и генератор задаст вам несколько вопросов, чтобы помочь в создании проекта, который наилучшим образом соответствует вашим потребностям. Здесь вы можете просто принять значения по умолчанию.

Структура папок

Структура папок будет выглядеть так:

 . ├── app │ ├── app.js │ ├── index.html │ ├── public │ │ ├── css │ │ │ └── app.css │ │ ├── js │ │ │ └── app.js │ │ ├── libs │ │ │ ├── angular │ │ │ ├── bootstrap │ │ │ └── jquery │ │ └── partials │ │ └── header.html │ ├── routes │ │ └── index.js │ └── views │ └── index.ejs ├── node_modules ├── bower.json ├── Gruntfile.js ├── package.json └── README.md 

Для этого проекта нас интересует прежде всего содержимое public каталога. Генератор заполнил эти файлы набором шаблонов (очень простое приложение на Angular), но мы будем решать это по ходу дела.

Теперь мы можем запустить скелетное приложение, используя:

grunt run или просто grunt

Эту команду можно использовать в любой момент разработки приложения для предварительного просмотра изменений. Он выполняет проект NW.js, который, в свою очередь, использует Express для маршрутизации так же, как при создании веб-приложения. Это хороший пример того, как мы можем использовать Node-модули в NW.js, вставляя их в app/index.html после инициализации.

NW.js также имеет инструменты разработчика и панели инструментов, где мы можем найти элементы управления для обновления, отладки, проверки, ведения журнала и т. Д., Как мы делаем при создании веб-приложения в Chrome. Вы можете получить к ним доступ, щелкнув значок гамбургера в приложении скелета.

Работа с пользовательским интерфейсом

Самым важным аспектом этого урока является возможность серфинга в интернете из нашего нативного приложения. Теги webview и iframe — идеальные кандидаты для нашего плана. Тег webview является эффективным, но довольно новым для игры, поскольку он был добавлен только недавно в NW.js. Однако тег iframe существует с HTML 4 и имеет широкую поддержку. Мы будем использовать его, потому что это хорошо известно большинству разработчиков.

Bootstrap будет служить основой для нашего пользовательского интерфейса. Мы будем использовать собственную тему начальной загрузки под названием Slate из Bootswatch . Загрузите Slate и поместите его в app/public/css/bootstrap.css .

Для наших иконок мы будем использовать Font Awesome . Из корневого каталога проекта:

 bower install --save fontawesome 

Это загрузит Font Awesome в нашу папку libs как и другие зависимости bower. Это связано с тем, что мы указываем следующую строку в файле .bowerrc в корне нашего проекта (по умолчанию это bower_components ).

 { "directory" : "app/public/libs" } 

К счастью, Bootstrap справится с большинством задач пользовательского интерфейса, но нам нужно настроить некоторые компоненты и содержимое, чтобы браузер действительно выглядел хорошо. Чтобы достичь этого, мы напишем простой и короткий CSS и app/public/css/app.css его в app/public/css/app.css :

 html, .tab-content, .tab-pane, body > div { height: 100%; } iframe { background: white; } .controls { position: fixed; top: 10px; right: 8px; } .controls > i { padding: 0 3px; } .controls > i:last-child { color: red; } .controls > i:hover { color: white; cursor: pointer; } .close-tab:hover { color: red; cursor: pointer; } 

Высота html , body , tab-content и tab-pane установлена ​​на 100%, чтобы гарантировать, что независимо от размера нашего браузера, контент должен заполнять высоту окна. По умолчанию ширина равна 100%, поэтому нет необходимости явно указывать ее. Мы также даем минимальный стиль для наших элементов управления браузером, которые мы увидим через некоторое время.

Чтобы убедиться, что все наши CSS-файлы загружены, скопируйте следующее в app/views/index.ejs раздел app/views/index.ejs . Это должно заменить две таблицы стилей, которые уже есть.

 <link rel="stylesheet" href="css/bootstrap.css"> <link rel="stylesheet" href="libs/font-awesome/css/font-awesome.css"> <link rel="stylesheet" href="css/app.css"> 

Установка наших зависимостей

Как уже упоминалось, мой генератор WEAN поставляется в комплекте с AngularJS , который мы будем использовать для нашего браузера. Мы также будем использовать компонент вкладок Bootstrap для отображения вкладок нашего браузера. Однако при попытке добавить компоненты Bootstrap JavaScript в проект Angular существует известная проблема , поэтому команда Angular создала пользовательский интерфейс Angular Bootstrap .

Давайте добавим это в наш проект. Из корневого каталога запустите:

 bower install --save angular-bootstrap 

AngularJS хорош в безопасности и накладывает ограничения на вещи, которые считаются потенциально опасными. В какой-то момент в нашем приложении нам придется дезинфицировать наш HTML и запретить Angular генерировать ошибки, когда он сталкивается с введением доверенного HTML в наш код. Модуль, который обрабатывает такую ​​ситуацию, называется ngSanitize (с которым мы скоро столкнемся ). Это тоже должно быть установлено с помощью Bower:

 bower install --save angular-sanitize 

Если Бауэр попросит вас выбрать версию библиотеки, выберите ту, которая соответствует угловому # 1.4.6.

Как и в случае с нашими CSS-файлами, давайте включим зависимости JS перед тегом закрытия body в app/views/index.ejs :

 <html ng-app="browser"> ... <body ng-controller="main"> <script src="libs/jquery/dist/jquery.min.js"></script> <script src="libs/angular/angular.js"></script> <script src="libs/angular-sanitize/angular-sanitize.js"></script> <script src="libs/angular-bootstrap/ui-bootstrap-tpls.js"></script> <script src="js/app.js"></script> </body> </html> 

Обратите внимание, что ng-app атрибута ng-app установлено значение browser а для атрибута ng-controller установлено значение main . Сейчас также самое время очистить остальную часть стандартного кода.

Последнее, что нужно сделать при настройке, — это вставить зависимости, которые мы только что загрузили, в наше приложение Angular app/public/js/app.js :

 angular.module('browser', ['ui.bootstrap', 'ngSanitize']) .controller('main', function(){}); 

На данный момент у нас есть скелет приложения. Наши CSS-файлы (как библиотечные, так и пользовательские) были установлены и / или созданы. Файлы JavaScript вместе с их зависимостями также стали доступны и добавлены в приложение. Если вы запустите grunt в этот момент, вы увидите пустое окно. Волнует, а?

Теперь все, что осталось в процессе разработки, — это выделить оставшуюся часть разметки и создать поведение в app.js

Разметка

NW.js использует HTML для своих просмотров. Мы уже создали один с генератором ( app/views/index.ejs ) и загрузили в него несколько файлов. Теперь нам нужно добавить разметку, которая поможет нам запускать веб-страницы в нашем родном приложении.

 <uib-tabset> <iframe-tab tabs="tabs"> </iframe-tab> </uib-tabset> 

uib-tabset является Angular-директивой, предоставляемой библиотекой Angular Bootstrap UI, чтобы помочь реализовать компонент tabs, в то время как iframe-tab будет создан в нашем приложении для настройки директивы Bootstrap UI tab для нас. Директива iframeTab будет настроена с использованием области tabs которая отображается в виде атрибута тега iframe-tab .

Реализация Директивы

Браузеры с вкладками, такие как Chrome и Firefox, легче ориентируются и повышают производительность пользователей. Наш собственный браузер не должен оставаться в стороне. В файле app/public/js/app.js мы создадим минимальную директиву для реализации табуляции.

 .directive('iframeTab', function () { return { restrict: 'E', scope: { tabs: '=' }, replace: true, template: '<uib-tab ng-repeat="tab in tabs" active="tab.active">' + '<uib-tab-heading>{{tab.url}} <i class="fa fa-times close-tab"' + 'ng-click="closeTab($index)"></i></uib-tab-heading>' + '<iframe src="{{trustSrc(tab.url)}}"' + 'style="width:100%; height:100%" nwdisable nwfaketop></iframe>' + '</uib-tab>' }; }) 

Здесь мы создаем многократно используемый шаблон, который можно создавать динамически с помощью Angular. Метод trustSrc() для атрибута iframe src будет создан в нашем контроллере.

Объяснение того, как директивы работают в Angular, выходит за рамки этой статьи. Если вам нужна переподготовка, ознакомьтесь: Практическое руководство по директивам AngularJS .

Некоторые службы поддержки

Angular использует сервисы для организации кода, повторного использования, взаимодействия с API и обмена логикой между контроллерами. Нам нужно сделать три для себя: одну ( prompt ), чтобы использовать подсказки для получения входных URL-адресов, а две другие ( GUI и Window ) для взаимодействия с GUI и Window API- интерфейсами NW.js, чтобы мы могли создавать настраиваемые полноэкранные минимизации и кнопки закрытия:

 .factory("prompt", function ($window, $q) { function prompt(message, defaultValue) { var defer = $q.defer(); var response = $window.prompt(message, defaultValue); if (response === null) { defer.reject(); } else { defer.resolve(response); } return (defer.promise); } return (prompt); }) .factory('GUI', function () { return require('nw.gui'); }) .factory('Window', function (GUI) { return GUI.Window.get(); }); 

Контроллер наконец

Контроллер, как следует из названия, будет контролировать поток данных в приложении. Мы $sce следующие зависимости: $scope , $sce (сервис, который предоставляет Strict Contextual Escaping services для AngularJS), prompt, Window (два сервиса, которые мы создали выше).

 .controller('main', function ($scope, $sce, prompt, Window) { //implementation here }) 

Сначала мы создадим метод для доверия URL ресурса (который мы уже реализовали в директиве):

 $scope.trustSrc = function (src) { return $sce.trustAsResourceUrl(src); } 

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

 $scope.tabs = [ { url: 'http://www.sitepoint.com/' } ]; 

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

 $scope.newTab = function () { prompt("Please enter a url", "http://www.sitepoint.com") .then(function (url) { var tab = {url: url, active:true} $scope.tabs.push(tab); }, function () { alert("Error opening site!"); }); }; 

Закрытие вкладок предполагает использование функции Array#splice для удаления значений из массива вкладок, как показано ниже:

 $scope.closeTab = function (index) { $scope.tabs.splice(index, 1); }; 

Остальная часть контроллера используется для добавления поведения к элементам управления, которые предназначены для минимизации, включения / отключения полноэкранного режима и закрытия окна:

 $scope.minimize = function () { Window.minimize(); }; $scope.toggleKioskMode = function () { Window.toggleKioskMode(); }; $scope.close = function () { Window.close(); }; 

Нам еще предстоит добавить эти элементы управления в разметку, хотя мы добавили реализацию. Итак, давайте сделаем это сейчас (в app/views/index.ejs ):

 <div class="controls"> <i class="fa fa-plus" tooltip-placement="bottom" uib-tooltip="New tab" ng-click="newTab()"></i> <i class="fa fa-minus" ng-click="minimize()"></i> <i class="fa fa-square-o" ng-click="toggleKioskMode()"></i> <i class="fa fa-times" ng-click="close()"></i> <div> 

Вот и все! Теперь вы можете запустить браузер, используя команду grunt из терминала.

Снимок экрана браузера, отображающего домашнюю страницу SitePoint

Снимок экрана с подсказкой новой вкладки

Здание для платформ

Если вы вернетесь к началу статьи, я упомянул, что можно развернуть приложение NW.js во всех основных операционных системах. На странице проекта NW.js есть подробные инструкции о том, как это сделать , или вы можете использовать предварительно сконфигурированную задачу build Wean (которую я сейчас продемонстрирую).

Запуск grunt build из корня проекта создаст приложение для ОС, на которой он был построен, тогда как grunt build:all будет создано для всех платформ. Команда grunt build:{platform} (например, grunt build:mac ) будет собираться для конкретной ОС. Возможные варианты: win , osx , linux32 , linux64 . Для получения дополнительной информации, пожалуйста, обратитесь к генеральному отредактированному readme .

Например, если вы работаете в 64-битной системе Linux и запускаете:

 grunt build 

Это builds/test/linux64 каталог builds/test/linux64 который содержит исполняемый файл, соответствующий имени вашего проекта.

Вывод

При этом я надеюсь продемонстрировать не только мощь NW.js, но и мощь веб-технологий в создании нативных приложений. Мы не только узнали, как создать собственный браузер, но и увидели NW.js, Yeoman и другие инструменты в игре. Не забывайте, что исходный код этого руководства находится на GitHub — я рекомендую вам скачать его и поэкспериментировать.

Вы используете NW.js? Как вы думаете, это может создать серьезную проблему для нативных приложений? Я хотел бы услышать ваши мысли в комментариях ниже.