Статьи

Начало работы с предохранителем

В этом уроке мы создадим приложение Hacker News Reader с Fuse, инструментом для разработки кроссплатформенных приложений, работающих на устройствах Android и iOS. Вы можете найти исходный код проекта на Github . Вот как будет выглядеть окончательный результат:

Fuse hacker news reader

Что такое предохранитель?

Короче говоря, Fuse — это инструмент для создания кроссплатформенных приложений с использованием JavaScript. Если вы знакомы с React Native и NativeScript, идея Fuse довольно схожа. Он использует виртуальную машину JavaScript для запуска JavaScript, компоненты пользовательского интерфейса преобразуются в собственный пользовательский интерфейс и предоставляют доступ к API различных платформ. Мотивация — создать инструмент, удобный как для разработчиков, так и для дизайнеров. И из того, что я видел до сих пор, я могу сказать, что они действительно на правильном пути. Некоторые из особенностей Fuse включают в себя производительность пользовательского интерфейса, мощную и выразительную анимацию и мгновенную перезагрузку в реальном времени на нескольких устройствах.

Установка предохранителя

Чтобы начать работу с Fuse, сначала необходимо установить Android SDK для Android и XCode для iOS. Вот две ссылки, которые помогут вам с этим:

Если ваш компьютер уже был настроен для разработки под Android или iOS, вы можете пропустить этот шаг.

После установки Android SDK или XCode загрузите установщик Fuse со страницы загрузок . Просто введите свой адрес электронной почты, примите лицензионное соглашение и нажмите кнопку «Let’s Fuse». Как только вы это сделаете, он должен показать вам кнопки для загрузки установщика для каждой платформы разработки. Загрузите тот, который применим к вам. Но для этого урока мы будем использовать версию для Windows.

После того, как вы загрузили установщик Fuse, щелкните по нему, чтобы начать установку. Установщик должен также выполнить установку зависимостей, так что это может занять некоторое время в зависимости от вашего интернет-соединения.

Создание нового проекта

Вы можете создать новый проект, запустив панель управления Fuse и нажав кнопку «New Project». Будет запрошено имя проекта и путь к месту сохранения проекта.

new project

После того, как проект создан, он должен появиться в списке «Последние проекты» на панели инструментов. Нажмите на это и нажмите «Открыть в возвышенном тексте 3». Fuse имеет несколько приятных интеграций с Sublime Text, поэтому я рекомендую этот текстовый редактор при работе с проектами Fuse. Но для того, чтобы эти интеграции работали, сначала нужно установить плагин Fuse, используя управление пакетами . После установки он должен добавить такие функции, как завершение кода и отображение результата сборки, чтобы вы точно знали, что не так с вашим кодом. Если вы новичок в Sublime Text, вы можете скачать его здесь . Инструкции по установке пакета управления можно найти здесь . Как только это будет сделано, вы можете установить пакет Fuse, нажав ctrl + shift + P на клавиатуре и выбрав Package Control: Install Package . Оттуда вы можете найти «Предохранитель» и выбрать первый результат, который появляется.

Инструменты разработки

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

Начните с открытия файла MainView.ux . Он должен содержать некоторый код по умолчанию, который вы могли бы запустить и поиграть. Вы можете сделать это, перейдя на панель инструментов Fuse и, выбрав свой проект, нажмите кнопку «Предварительный просмотр» и выберите «Local». Это должно открыть новое окно командной строки, которое запустит инструмент предварительного просмотра.

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

После запуска предварительного просмотра вы должны увидеть приложение по умолчанию. Вы можете внести в него изменения, нажав « Предохранитель» -> «Режим проектирования», а затем « Предохранитель» -> «Открыть инспектор» . Откроется новое окно, в котором можно внести изменения в стиль элемента, выбранного в предварительном просмотре. Например, вы можете изменить ширину, высоту, цвет фона и размер шрифта.

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

inspector

Еще одним инструментом, который вы можете использовать, является инструмент мониторинга. Нажмите на Предохранитель -> Открыть монитор, чтобы открыть его. Это должно показать новое окно, которое регистрирует все ошибки, которые произошли в приложении. Вы также можете войти в систему самостоятельно, вызвав debug_log() из своего кода. Эта функция очень похожа на console.log() которой вы привыкли в браузере. Хотя вы можете только выводить строки с ним. Поэтому вы должны использовать JSON.stringify() если вы хотите проверить содержимое объекта.

monitor

Еще один инструмент, с которым вы должны ознакомиться, это Build Result. Это интегрировано с Sublime Text, и вы можете найти его, щелкнув по значку поля в левой нижней части экрана. Он должен открыть контекстное меню, выбрать «Вывод: FuseBuildResults» и откроет консоль журнала, которая показывает результаты сборки каждый раз, когда вы сохраняете файл. Если в вашем коде есть ошибка, она должна отображаться там. Таким образом, вы знаете, что не так с вашим кодом, и какая конкретная строка является проблемой, так что вы можете это исправить. Хороший способ проверить, что последнее внесенное вами изменение вызвало ошибку, — это если пользовательский интерфейс не обновляется даже после добавления нового элемента.

Результат сборки

Распространенные ошибки включают неправильно заданные имена атрибутов для компонентов, попытки включить пакет, который недоступен, и синтаксические ошибки в вашем коде JavaScript.

Сборка приложения

Теперь, когда вы знакомы с инструментами, которые предлагает Fuse, пришло время создать приложение. Как упоминалось ранее, мы будем создавать приложение Hacker News Reader. В нем будут перечислены первые десять новостей из API Hacker News, и пользователь сможет просматривать веб-страницу, на которую ссылается каждый элемент новостей.

Оставив MainView.ux открытым, очистите весь код по умолчанию. Начните с добавления элемента <app> . Каждая страница в Fuse начинается с этого элемента.

 </ app > < app > </ app > 

Создайте ссылку на файл JavaScript всего приложения:

 < javascript File = "js/App.js" > </ javascript > 

Этот файл находится в каталоге js/ в корне проекта и содержит следующий код:

 const app_title = 'HN Reader' ; const Observable = require ( "FuseJS/Observable" ); var current_page = Observable( "news_items" ); var current_url = Observable( "" ); var title = Observable(app_title); function navigatePage (context) { if (context.data.url){ current_url.value = context.data.url; title.value = context.data.title.substring( 0 , 20 ) + '...' ; current_page.value = 'web_page' ; } else { title.value = app_title; current_page.value = 'news_items' ; } } module.exports = { title: title, navigatePage: navigatePage, current_page: current_page, current_url: current_url }; 

Разбивая код выше, сначала мы определяем некоторые значения по умолчанию, которые будут использоваться приложением. Это включает заголовок, который будет отображаться в заголовке, текущую страницу, которую пользователь просматривает в данный момент, и URL-адрес веб-страницы, используемой WebView. Все эти значения устанавливаются как наблюдаемые значения, используя FuseJS/Observable , встроенную библиотеку в Fuse, которая позволяет использовать двустороннюю привязку данных. Это означает, что когда вы изменяете значение каждой из этих наблюдаемых переменных в коде, оно также отражается в пользовательском интерфейсе и наоборот.

 // js/App.js const app_title = 'HN Reader' ; const Observable = require ( "FuseJS/Observable" ); var current_page = Observable( "news_items" ); var current_url = Observable( "" ); var title = Observable(app_title); 

Определите функцию для навигации между страницей новостей и реальной страницей новостей (WebView). Здесь мы обновляем значение current_url если context.data.url присутствует в аргументе, который был передан в функцию. Мы также берем первые 20 символов из заголовка новости и устанавливаем для current_page значение web_page . Это позволяет нам перейти на страницу, которая отображает WebView. В противном случае установите current_page на страницу news_items и установите заголовок обратно в заголовок приложения.

 // js/App.js function navigatePage (context) { if (context.data.url){ current_url.value = context.data.url; title.value = context.data.title.substring( 0 , 20 ) + '...' ; current_page.value = 'web_page' ; } else { title.value = app_title; current_page.value = 'news_items' ; } } 

Чтобы вы могли ссылаться на переменные в вашем пользовательском интерфейсе, вам нужно использовать module.exports чтобы сделать их доступными:

 // js/App.js module.exports = { title: title, navigatePage: navigatePage, current_page: current_page, current_url: current_url }; 

Возвращаясь к файлу MainView.ux , добавьте следующее прямо под ссылкой на файл js/App.js Этот компонент автоматически обрабатывает эффекты перехода при навигации между страницами.

 < pagecontrol Active = "{current_page}" > </ pagecontrol > 

Внутри определите все страницы, используемые в вашем приложении. Здесь у нас только два: news_items и web_page . Ранее в компоненте <pagecontrol> мы добавили атрибут Active , значением которого является текущее значение переменной current_page . В файле js/App.js мы установили для этого параметра значение news_items что означает, что он будет использовать страницу новостей в качестве страницы по умолчанию. Если вы хотите перейти на страницу web_page , вы можете просто обновить значение current_page до web_page . Предохранитель автоматически обработает эффекты перехода для вас. По умолчанию используется скользящая анимация.

 < page Name = "news_items" > </ page > < page Name = "web_page" > </ page > 

Страница новостей

Давайте сначала посмотрим на содержимое страницы новостей. Здесь мы <dockpanel> все в <dockpanel> . Это позволяет нам закреплять элементы в определенной позиции. По умолчанию установлено значение « Fill которое занимает весь экран.

 </ dockpanel > < dockpanel > </ dockpanel > 

Далее, ссылка на файл js/NewsItems.js . Это JavaScript, используемый только на странице новостей.

 < javascript File = "js/NewsItems.js" > </ javascript > 

Вот содержимое файла:

 const Observable = require ( "FuseJS/Observable" ); var loader_opacity = Observable( '1' ); var news_items = Observable(); const TOP_STORIES_URL = 'https://hacker-news.firebaseio.com/v0/topstories.json' ; var story_promises = []; fetch(TOP_STORIES_URL) .then( function (response) { return response.json(); }) .then( function (top_stories) { for ( var x = 0 ; x < = 10 ; x++){ const story_url = "https://hacker-news.firebaseio.com/v0/item/" + top_stories[x] + ".json" ; const p = fetch(story_url) .then( function (response) { return response.json(); }) .then( function (news) { news_items.add({ title: news.title, url: news.url, time: news.time }); }); story_promises.push(p); } Promise.all(story_promises).then( function () { loader_opacity.value = 0 ; }); }) .catch( function (error) { console.log( 'There has been a problem with your fetch operation: ' + error.message); }); module.exports = { news_items: news_items, loader_opacity: loader_opacity }; 

Разбивая код выше, мы снова используем библиотеку Observable. Обратите внимание, что каждый экземпляр компонента <JavaScript> в Fuse имеет свой собственный контекст, это означает, что даже если мы использовали Observable ранее, он не будет доступен здесь. То же самое верно для любой наблюдаемой переменной, которую мы определили.

 // js/NewsItems.js const Observable = require ( "FuseJS/Observable" ); 

Установите значение для непрозрачности загрузчика. Это непрозрачность для анимации загрузки, которую мы покажем пользователю, когда он откроет приложение. Мы должны установить его в 1 по умолчанию, потому что новости должны быть загружены из API в первую очередь. Поэтому, пока приложение загружает данные, мы показываем анимацию пользователю. Позже мы обновим его значение до 0 чтобы скрыть его от пользователя.

 // js/NewsItems.js var loader_opacity = Observable( '1' ); 

Инициализируйте наблюдаемый список:

 // js/NewsItems.js var news_items = Observable(); 

Добавьте URL-адрес для конечной точки главных новостей API Hacker News и массив для хранения обещаний, которые будут возвращаться при каждом вызове fetch() .

 // js/NewsItems.js const TOP_STORIES_URL = 'https://hacker-news.firebaseio.com/v0/topstories.json' ; var story_promises = []; 

Сделайте запрос к конечной точке топа новостей, используя API выборки :

 // js/NewsItems.js fetch(TOP_STORIES_URL) .then( function (response) { return response.json(); }) .then( function (top_stories) { }) .catch( function (error) { console.log( 'There has been a problem with your fetch operation: ' + error.message); }); 

Это вернет массив, содержащий идентификаторы главных новостей из Hacker News. Нам нужны только первые десять, поэтому мы используем цикл for чтобы перебрать первые десять элементов. На каждой итерации создайте URL-адрес для конечной точки, который возвращает подробности для каждого отдельного элемента новостей. После того, как URL создан, сделайте отдельный запрос для каждого элемента, а затем добавьте его в список наблюдаемых, который мы создали ранее. Каждое обещание также story_promises массив story_promises .

 // js/NewsItems.js for ( var x = 0 ; x < = 10 ; x++){ const story_url = "https://hacker-news.firebaseio.com/v0/item/" + top_stories[x] + ".json" ; const p = fetch(story_url) .then( function (response) { return response.json(); }) .then( function (news) { news_items.add({ title: news.title, url: news.url }); }) .catch( function (error) { console.log( 'There has been a problem with your fetch operation: ' + error.message); }); story_promises.push(p); } 

Это позволяет нам определить, когда именно мы должны скрывать загрузочную анимацию. Promise.all принимает массив обещаний, и если все обещания разрешаются, выполняется функция, которую вы передали then() . Здесь мы скрываем загрузчик, устанавливая его непрозрачность равной 0 .

 // js/NewsItems.js Promise.all(story_promises).then( function () { loader_opacity.value = 0 ; }); 

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

 // js/NewsItems.js module.exports = { news_items: news_items, loader_opacity: loader_opacity }; 

Возвращаясь к файлу MainView.ux , добавьте заполнитель для строки состояния. Это потому, что Fuse автоматически занимает весь экран. Если вы не добавите это, строка состояния не будет видна.

 < StatusBarBackground Dock = "Top" /> 

Добавьте заголовок, используя <stackpanel> . В Fuse используйте </stackpanel><stackpanel> в качестве контейнера, если вы хотите разместить его дочерние </stackpanel><stackpanel> друг над другом (для вертикальной ориентации). Установите для атрибута Dock значение Top чтобы оно располагалось прямо под строкой состояния.

 </ stackpanel > < stackpanel Dock = "Top" Color = "#FF6600" > < text FontSize = "18" Margin = "0,10,0,10" Alignment = "VerticalCenter" TextAlignment = "Center" TextColor = "#FFF" Value = "{title}" > </ text > </ stackpanel > 

Используйте <scrollview> чтобы вертикальная полоса прокрутки автоматически генерировалась, если новости <scrollview> за пределы доступного пространства. В Fuse вы используете компонент <each> для создания списка. Для каждого элемента списка мы создаем <panel> которая при нажатии выполняет функцию navigatePage которую мы определили ранее. Нам не нужно ничего передавать этой функции, потому что контекст, в котором она вызывается, автоматически передается в качестве первого аргумента. Вот почему мы проверяли значение context.data.url ранее в файле js/App.js

 < scrollview > < stackpanel Alignment = "Top" > < each Items = "{news_items}" > < panel Clicked = "{navigatePage}" Alignment = "VerticalCenter" > < text Margin = "10,20,10,20" TextWrapping = "Wrap" FontSize = "20" Value = "{title}" > </ text > </ panel > < rectangle Height = "1" Fill = "#dcdee3" > </ rectangle > </ each > </ stackpanel > </ scrollview > 

Веб-страница

Теперь перейдем к содержанию веб-страницы. Так же, как страница новостей, мы <dockpanel> все в <dockpanel> и также добавляем <statusbarbackground> . Заголовок немного отличается, потому что на этот раз у нас есть элемент <text> который при нажатии или нажатии выполняет кнопку navigatePage . Так как это выполняется из пользовательского интерфейса, оно также будет передавать context.data в качестве аргумента. Единственное отличие состоит в том, что url не существует, поэтому выполняется условие else функции navigatePage . Это означает, что он действительно вернется на страницу новостей.

 < dockpanel > < statusbarbackground Dock = "Top" > </ statusbarbackground > < wrappanel Dock = "Top" Color = "#FF6600" > < text FontSize = "18" Margin = "10,10,0,10" Alignment = "Left" TextAlignment = "Left" TextColor = "#FFF" Value = "Back" Width = "100" Clicked = "{navigatePage}" > </ text > < text FontSize = "18" Alignment = "VerticalCenter" TextAlignment = "Center" TextColor = "#FFF" Value = "{title}" > </ text > </ wrappanel > </ dockpanel > 

Определите повторно используемый компонент, ux:Class атрибут ux:Class . Значение, которое вы дадите этому, будет именем компонента, который будет наследоваться от этого компонента.

 < panel ux:Class = "LoadingBar" Width = "5%" Height = "10" Color = "#fc0" Alignment = "Left" > </ panel > 

Используйте компонент, который вы только что создали. Все стили, которые вы определили, наследуются этим компонентом. Это будет служить загрузчиком прогресса для веб-страницы. ux:Name чтобы вы могли изменить Width позже.

 < loadingbar Dock = "Top" ux:Name = "_loadingBar" > </ loadingbar > 

Наконец, используйте компонент WebView чтобы отобразить выбранный пользователем элемент новостей. Обратите внимание, что этот компонент должен быть заключен в <nativeviewhost> потому что он использует собственный WebView в устройстве. Это также означает, что вы не можете проверить это с помощью инструмента предварительного просмотра. Также обратите внимание на использование компонента <progressanimation> . Это позволяет нам изменять значение полосы загрузки до 100% в течение загрузки страницы. Это дает пользователю представление о том, сколько еще ждать полной загрузки страницы.

 < panel > < nativeviewhost > < webview Dock = "Fill" Url = "{current_url}" ux:Name = "webpage" > < progressanimation > < change _loadingBar.Width = "100%" > </ change > </ progressanimation > </ webview > </ nativeviewhost > </ panel > 

Одновременные анимации

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

Начните с создания окна загрузчика с использованием компонента <rectangle> . Не смущайтесь его именами, вы можете создать квадрат, установив его Width и Height в одно и то же значение. Внутри находится масштаб и вращение по умолчанию. Factor масштабирования установлен в 1 что означает, что масштаб по умолчанию — это Width вами Width и Height . Degrees вращения установлены на 0 что означает, что он не вращается. Мы добавляем свойство ux:Name к каждому из них, чтобы мы могли контролировать значения с помощью временной шкалы анимации.

 </ rectangle > < rectangle Width = "50" Height = "50" Fill = "#FA983C" Opacity = "{loader_opacity}" > < scaling ux:Name = "loaderScale" Factor = "1" > </ scaling > < rotation ux:Name = "loaderRotate" Degrees = "0" > </ rotation > </ rectangle > 

Далее мы определяем временную шкалу анимации для окна загрузчика. PlayMode="Wrap" означает, что анимация будет выполняться бесконечно. По умолчанию установлено значение « Once что означает, что анимация не будет перезапущена после ее завершения. Чтобы изменить значение масштаба и поворота, используйте оператор « Change . Затем используйте имя ux:Name которое вы добавили ранее в свойства анимации окна загрузчика. Здесь мы устанавливаем масштабный коэффициент на 1,5, чтобы увеличить размер поля до половины его первоначального размера. А затем поверните его на 360 градусов. Эти две анимации выполняются одновременно в течение 1 секунды. Вы также можете указать Easing , но я оставлю вам возможность изучить, что делает QuadraticInOut .

 < timeline PlayMode = "Wrap" > < change loaderScale.Factor = "1.5" Duration = "1" Easing = "QuadraticInOut" > </ change > < change loaderRotate.Degrees = "360" Duration = "1" Easing = "QuadraticInOut" > </ change > </ timeline > 

Запуск приложения

Вы можете запустить приложение на своем устройстве, скомпилировав отладочную версию:

 fuse build --target=Android 

После завершения компиляции вы можете найти файл .apk в каталоге build / Android / Debug . Скопируйте его на свое устройство, установите и запустите.

Вывод

Это оно! В этом уроке вы узнали о Fuse, захватывающем новом инструменте в мире разработки собственных мобильных приложений. Как вы уже видели, Fuse действительно радует разработку мобильных приложений. Это связано со следующими особенностями:

  • Супер быстрая автоматическая перезагрузка. Это помогает в производительности проектировщика и разработчика.
  • Позволяет создавать приложения, которые могут работать на устройствах iOS и Android.
  • Дизайн-режим, который позволяет легко изменить дизайн через графический интерфейс.
  • Монитор инструмент, который помогает разработчикам в отладке своего приложения.
  • Анимации сделаны с использованием декларативной разметки. Это позволяет дизайнерам очень легко реализовывать анимацию.

В настоящее время Fuse находится на стадии бета-тестирования, поэтому он может быть еще не готов к сложным проектам. Хотя, если вам нужно только создать простые приложения, подобные тому, которое мы только что создали, не должно быть проблем с использованием Fuse.