Pull to refresh

В одной упряжке Polymer’ы, Dart и Firebase

Reading time 8 min
Views 8.8K
Недавно появилась информация о том, что Google приобрел облачный сервис Firebase. На хабре информации о Firebase не особо много, а сервис выглядит очень интересно, как минимум для быстрого прототипирования или использования в качестве буфера для активных данных.

FireBase — это облачная NoSQL БД для real-time приложений. То есть, ваши данные хранятся в облаке, которое готово почти к любым нагрузкам. Ваши данные моментально обновляются на всех клиентах, которые подключены к FireBase и подписаны на информацию об обновлениях. Совершенно отпадает необходимость строить сложную систему с использованием веб-сокетов для обмена данными между сервером и клиентами в реальном времени. Вы просто подключаете JS-файл на страницу и настраиваете CallBack на события изменения данных. Все – дальше FireBase будет самостоятельно следить за данными и отдавать их пользователям. Причем, происходит это почти мгновенно! А еще с FireBase можно создавать веб-приложения, для которых вообще не потребуется собственный сервер. При этом для небольших сервисов использование FireBase совершенно бесплатно. Кроме этого, Firebase предоставляет установку прав доступа к частям данных на базе JS-выражений, транзакции (в отличие от MongoDB) и имеет встроенный функционал авторизации пользователей по электронной почте и паролю, а также авторизации с помощью сторонних поставщиков (OAuth), таких как Facebook, Twitter, GitHub, и Google.

image


Взглянув, как это работает, появилось желание переписать тьюториал на Dart.

Dart SDK включает в себя менеджер пакетов с более чем 1300 пакетами от сообщества. Среди них, например, такие как AngularDart и Polymer.dart, которые служат высокоуровневыми основами для строительства веб-приложений. Есть и пакет для работы с Firebase, заворачивающий функционал firebase.js в Dart классы при помощи dart:js. Точно таким же образом Dart-разработчики могут использовать любые другие библиотеки JavaScript вместе с Dart.

Собственно, именно с этим способом использования JavaScript библиотек в Dart и хотелось разобраться детально.

Тема использования JavaScript библиотек в Dart представлена практически только в виде примеров. В текущих условиях, когда нативные библиотеки под Dart есть еще не под все задачи, непонимание того, как использовать JavaScript библиотеки, может быть основным фактором, удерживающим от перехода на Dart. Все остальное предельно просто и выглядит очень выгодным вложением времени на освоение нового языка.

Hа сайте, посвященном Dart, отличная подборка примеров, экспериментируя с которыми человек с базовыми навыками программирования может полностью освоиться с Dart за пару дней. Причем после этого вы сможете создавать и клиентскую и серверную логику на одном и том же языке, используя один раз написанную логику классов на клиенте и на сервере. Dart был разработан так, чтобы выглядеть очень знакомо для программистов на таких языках, как Java и JavaScript. Для тех, кто предпочитает учиться по бумажным носителям на русском языке, можно прочитать книгу «Dart в действии». А вот русский перевод Dart Up and Running.

В отличие от Java, где на освоение с нуля практики написание нормального GUI (графического пользовательского интерфейса) для практических задач офисного пользователя уйдет в среднем от двух недель, Dart позволяет научиться писать GUI за пару дней. И такой GUI будет прекрасно решать поставленные задачи, работать в любом браузере (через трансляцию в JavaScript) и являться тем инструментом, с которым без проблем сможет работать конечный пользователь на практически любом устройстве где есть браузер.

В отличие от Javascript, начав с быстрого прототипирования, вы, в случае потребности, легко сделаете шаги к простой и логичной системе импорта модулей, строго типизированым данным, сильно упрощающим отладку («Разные языки по-разному могут помогать вам в поиске ошибок. К сожалению, JavaScript находится на конце этой шкалы, обозначенном как «вообще почти не помогает»»), удобной отладке внутри Dart Editor’а, использованию классов встроенных в язык без изобретения собственных велосипедов и освоения костылей фреймворков. Что здорово облегчает использование Dart в сложных проектах, когда большая задача декомпозируется и разделяется между несколькими исполнителями.

Ниже результат переписывания на Dart тьюториала, где видны все нюансы взаимодействия с JavaScript.

firebase_chat.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Sample app</title>
    <script src="packages/web_components/platform.js"></script>
    <script src="packages/web_components/dart_support.js"></script>   
    <script src='https://cdn.firebase.com/js/client/1.1.1/firebase.js'></script>
  </head>
  <body>
    <h1>Firebase chat</h1>  
    <div id='messagesDiv'></div>
    <input type='text' id='nameInput' placeholder='Name'>
    <input type='text' id='messageInput' placeholder='Message'>
    <script type="application/dart" src="firebase_chat.dart"></script>
    <script src="packages/browser/dart.js"></script>
  </body>
</html>


firebase_chat.dart
import 'dart:html';
import 'dart:js';

InputElement messageInput;
InputElement nameInput;
DivElement div;
JsObject myDataRef;

main() {
  myDataRef = new JsObject(context['Firebase'], ['https://ihp8plr13um.firebaseio-demo.com/']); // Создаем JS объект с типом Firebase, взятым из импортированной JS библиотеки
  messageInput = querySelector('#messageInput') as InputElement; //Линкуем элемент из HTML файла с Dart объектом
  nameInput = querySelector('#nameInput') as InputElement; //Линкуем элемент из HTML файла с Dart объектом 
  div = querySelector('#messagesDiv') as DivElement; //Линкуем элемент из HTML файла с Dart объектом 
  messageInput.onKeyPress.listen(onMessageInputKeyPress); //Вешаем обработчик на onKeyPress
   
  myDataRef.callMethod('on', ['child_added', // Вызываем JS функцию .on('child_added',
    //new JsFunction.withThis((jsThis, a, b) { // Оборачиваем в JS ф-цию для регистрации в качестве CallBack
    (a, b) {                          
      var message = a.callMethod('val',[]); // Вызываем JS функцию .val()
      print(message['name'] + ':' + message['text']);
      displayChatMessage(message['name'], message['text']);
    }
  ]);
}

void onMessageInputKeyPress(KeyboardEvent e){
  if (e.keyCode == 13) {
    var name = nameInput.value;
    var text = messageInput.value;
    var jsParams = new JsObject.jsify( {'name' : '$name', 'text' : '$text'}); // Заворачиваем параметры в JS объект
    myDataRef.callMethod( 'push', [jsParams]); // Вызываем JS функцию .push(
    messageInput.value ='';
  };
}

void displayChatMessage(String name, String text) {
  div.appendHtml('<em>$name:</em> $text<br />'); 
}


Еще одно дополнение на тему интероперабильности Dart — JS: подход взаимодействия с JS был изменен некоторое время назад. Если где-то на SO встретите в примерах import 'package:js/js.dart' — это уже устарело.

И таки да: Web Components — это единственное светлое будущее веба. Смиритесь.

Вот как выглядит тот же пример с использованием Polymer компонентов.

firebase_chart.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Sample app</title>
    <!-- include the web_components polyfills with support for Dart. -->
    <script src="packages/web_components/platform.js"></script>
    <link rel="import" href="fc_polymer_output_div.html">    
    <link rel="import" href="fc_polymer_input_fields.html">    
  </head>
  <body>
    <h1>Firebase chat</h1>
    <fc-polymer-output-div fb_dblink ='https://ihp8plr13um.firebaseio-demo.com/'></fc-polymer-output-div>    
    <fc-polymer-input-fields fb_dblink ='https://ihp8plr13um.firebaseio-demo.com/'></fc-polymer-input-fields>
    <!-- bootstrap polymer -->
    <script type="application/dart">export 'package:polymer/init.dart';</script>
    <script src="packages/browser/dart.js"></script>   
  </body>
</html>


fc_polymer_output_div.html
<script src='https://cdn.firebase.com/js/client/1.1.1/firebase.js'></script>
<!-- import polymer-element's definition -->
<link rel="import" href="packages/polymer/polymer.html">
<polymer-element name="fc-polymer-output-div" attributes="fb_dblink">
  <template>
    <style>        
    </style>
    <div id=messagesDiv></div>
  </template>
  <script type="application/dart" src="fc_polymer_output_div.dart"></script>
</polymer-element>


fc_polymer_output_div.dart
import 'dart:html';
import 'package:polymer/polymer.dart';
import 'dart:js';

DivElement div;
JsObject myDataRef;

@CustomTag('fc-polymer-output-div')
class OutputDivElement extends PolymerElement {
  @published String fb_dblink; 
  OutputDivElement.created() : super.created() {}
 
  void attached() {
    super.attached();
    myDataRef = new JsObject(context['Firebase'], [fb_dblink]); // Создаем JS объект с типом Firebase взятым из импортированной JS библиотеки
    div = $['messagesDiv'] as DivElement; //Линкуем элемент из HTML файла с Polymer компонентом с Dart объектом
    
    myDataRef.callMethod('on', ['child_added', // Вызываем JS функцию .on('child_added',
      new JsFunction.withThis((jsThis, a, b) { // Для разнообразия оборачиваем в JS ф-цию для регистрации в качестве CallBack                       
        var message = a.callMethod('val',[]); // Вызываем JS функцию .val()
        print(message['name'] + ':' + message['text']);
        displayChatMessage(message['name'], message['text']);
      })
    ]);
  }
  
  void displayChatMessage(String name, String text) {
    div.appendHtml('<em>$name:</em> $text<br />'); 
  }
}


fc_polymer_input_fields.html
<script src='https://cdn.firebase.com/js/client/1.1.1/firebase.js'></script>
<!-- import polymer-element's definition -->
<link rel="import" href="packages/polymer/polymer.html">
<polymer-element name="fc-polymer-input-fields" attributes="fb_dblink">
  <template>
    <style>        
    </style>
    <div>
      <input type='text' id='nameInput' placeholder='Name'>
      <input type='text' id='messageInput' placeholder='Message'>
    </div>
  </template>
  <script type="application/dart" src="fc_polymer_input_fields.dart"></script>
</polymer-element>


fc_polymer_input_fields.dart
import 'dart:html';
import 'package:polymer/polymer.dart';
import 'dart:js';

InputElement messageInput;
InputElement nameInput;
JsObject myDataRef;

@CustomTag('fc-polymer-input-fields')
class InputFieldsElement extends PolymerElement {
  @published String fb_dblink;
  
  InputFieldsElement.created() : super.created() {}
 
  void attached() {
    super.attached();
    myDataRef = new JsObject(context['Firebase'], [fb_dblink]); // Создаем JS объект с типом Firebase взятым из импортированной JS библиотеки
    messageInput = $['messageInput'] as InputElement; //Линкуем элемент из HTML файла с Polymer компонентом с Dart объектом
    nameInput = $['nameInput'] as InputElement; //Линкуем элемент из HTML файла с Polymer компонентом с Dart объектом
    messageInput.onKeyPress.listen(onMessageInputKeyPress); //Вешаем обработчик на onKeyPress
  }
  
  void onMessageInputKeyPress(KeyboardEvent e){
    if (e.keyCode == 13) {
      var name = nameInput.value;
      var text = messageInput.value;
      var jsParams = new JsObject.jsify( {'name' : '$name', 'text' : '$text'}); // Заворачиваем параметры в JS объект
      myDataRef.callMethod( 'push', [jsParams]); // Вызываем JS функцию .push(
      messageInput.value ='';
    };
  }  
}


pubspec.yaml
name: Firebase_chat
description: A sample Polymer Firebase app
dependencies:
  browser: any
  polymer: any
transformers:
- polymer:
    entry_points: web/firebase_chat.html


В данном случае Polymer-компоненты использованы для демонстрации подхода, но в реальных проектах такая декомпозиция позволяет эффективно распараллелить разработку, раздав подзадачи разным исполнителям. Что самое замечательное, Polymer-компоненты гарантированно не будут конфликтовать друг с другом при верстке. Shadow DOM, однако. И в большинстве проектов GUI можно будет не писать, а собирать из уже готовых компонентов, а верстку можно будет делать мышкой, связывая атрибуты компонентов друг с другом и получая сгенерированный HTML (левый верхний угол).

Посмотреть про дизайнера и его возможности можно на видео ниже:



К чему я это все. Довольно интересная рисуется мозаика того, куда Google двигает web. Все больше логики живет под браузером («Привет, Chrome OS!» — в предельном случае) и может быть оформлено в виде веб-компонентов (в том числе и Polymer на Dart). На сервере вполне шустро может жить Dart VM — удачная замена Node.js с учетом того, что Dart в Dart VM нативно работает на треть быстрее чем JS, избавляет от CallBack Hell присущего ПО на Node.js и существенно упрощает отладку. Или вместо серверной логики вообще может быть Firebase.

А теперь давайте порассуждаем: как вам такое будущее настоящее и, как вы думаете, что из текущих навыков JS+HTML+CSS+PHP будет востребовано в нем?
Tags:
Hubs:
+3
Comments 5
Comments Comments 5

Articles