Почему Flutter использует язык Dart? Основные преимущества языка Dart.
Сложность: Новичок.
Вступление
Многие лингвисты считают, что естественный язык, на котором говорит человек, влияет на то, как он думает. Применяется ли та же концепция к компьютерным языкам? Программисты, работающие с разными языками программирования, часто предлагают совершенно разные решения проблем. В качестве более радикального примера, ученые-компьютерщики убрали оператор goto, чтобы поощрять более структурированные программы (не совсем тоже самое, что тоталитарные лидеры в романе "1984", удаляющие еретические слова из естественного языка, чтобы устранить мыслепреступления, но вы поняли идею).
Какое это имеет отношение к Flutter и Dart? На самом деле совсем немного. Ранняя команда Flutter оценила более дюжины языков и выбрала Dart, потому что он соответствовал тому, как они создавали пользовательские интерфейсы.
Dart — главная причина, по которой разработчики любят Flutter.
Содержание
Раздел 1: Основные плюсы языка Dart
Вот краткий список функций Dart, которые вместе делают его незаменимым для Flutter:
Dart — это AOT (Ahead Of Time), скомпилированный в быстрый, предсказуемый и нативный код, что позволяет писать почти весь Flutter на Dart. Это не только делает Flutter быстрым, но и позволяет настроить практически все (включая все виджеты).
Dart также может быть скомпилирован JIT (Just In Time) для исключительно быстрых циклов разработки и революционного рабочего процесса (включая популярную во Flutter горячую перезагрузку с сохранением состояния за доли секунды).
Dart упрощает создание плавных анимаций и переходов со скоростью 60 кадров в секунду. Dart может выполнять анализ и сборку мусора (Garbage Collector) без блокировок. Как и JavaScript, Dart избегает упреждающего планирования и разделяемой памяти (и, следовательно, блокировок). Поскольку приложения Flutter скомпилированы в нативный код, им не требуется медленный мост между областями (например, как JavaScript в нативный код). Они также запускаются намного быстрее.
Dart позволяет Flutter избежать необходимости в отдельном языке декларативной компоновки, таком как JSX или XML, или отдельных конструкторов визуальных интерфейсов, поскольку декларативную программную компоновку Dart легко читать и визуализировать. А со всей компоновкой на одном языке и в одном месте Flutter легко предоставляет расширенные инструменты, которые делают компоновку проще простого.
Разработчики обнаружили, что Dart особенно легко освоить, поскольку он имеет функции, знакомые пользователям как статических, так и динамических языков.
Не все эти функции уникальны для Dart, но их комбинация обеспечивает наилучшие результаты, которые и делают Dart уникально мощным инструментом для реализации Flutter. Настолько, что трудно представить, что Flutter может быть таким мощным без Dart.
В оставшейся части этой статьи более подробно рассматриваются многие характеристики Dart (включая его стандартные библиотеки), которые делают его лучшим языком для реализации фреймворка Flutter.
Раздел 2: Компиляция и исполнение
Раздел 2.1: AOT, JIT и Виртуальные Машины
[Вы можете пропустить этот раздел, если вы уже знакомы с такими темами, как статические и динамические языки, компиляция AOT, JIT и виртуальные машины]
Исторически компьютерные языки делились на две группы: статические языки (например, Fortran или C, где переменные имеют статический тип во время компиляции) и динамические языки (например, Smalltalk или JavaScript, где тип переменной может изменяться при запуске). Статические языки обычно компилировались для создания машинного кода (или ассемблерного кода) программ для целевой машины, которые во время выполнения выполнялись непосредственно аппаратным обеспечением. Динамические языки выполнялись интерпретатором без создания кода машинного языка.
Конечно, со временем все стало намного сложнее. Стала популярной концепция виртуальной машины (ВМ), которая на самом деле является просто продвинутым интерпретатором, имитирующим аппаратную машину в программном обеспечении. Виртуальная машина упрощает перенос языка на новые аппаратные платформы. В этом случае язык ввода виртуальной машины часто является промежуточным языком. К примеру, язык программирования (возьмем Java) компилируется в промежуточный язык (байт-код), а затем выполняется на виртуальной машине (JVM).
Кроме того, теперь существуют JIT-компиляторы. Компилятор JIT запускается во время выполнения программы, компилируя ее на лету. Первоначальные компиляторы, которые выполняются во время создания программы (до выполнения), теперь называются опережающими компиляторами (AOT).
В общем, только статические языки поддаются компиляции AOT в собственный машинный код, потому что машинным языкам обычно необходимо знать тип данных, а в динамических языках тип не фиксируется заранее. Следовательно, динамические языки обычно интерпретируются или компилируются JIT.
Когда компиляция AOT выполняется во время разработки, это неизменно приводит к гораздо более медленным циклам разработки (время между внесением изменений в программу и возможностью выполнить программу, чтобы увидеть результат изменения). Но в результате компиляции AOT программы могут выполняться более предсказуемо и без пауз для анализа и компиляции во время выполнения. Программы, скомпилированные AOT, также начинают выполняться быстрее (потому что они уже были скомпилированы).
И наоборот, компиляция JIT обеспечивает гораздо более быстрые циклы разработки, но может привести к более медленному или прерывистому выполнению. В частности, JIT-компиляторы имеют более медленное время запуска, потому что, когда программа запускается, JIT-компилятор должен выполнить анализ и компиляцию, прежде чем код сможет быть выполнен. Исследования показали, что многие люди отказываются от приложения, если его запуск занимает более нескольких секунд.
Это конец справочной информации. Разве не было бы здорово объединить преимущества компиляции AOT и JIT?
Раздел 2.2: Реализация в языке Dart
До работы над языком Dart, его члены команды проделали новаторскую работу над передовыми компиляторами и виртуальными машинами как для динамических языков (таких как движок V8 для JavaScript и Strongtalk для Smalltalk), так и для статических языков (таких как компилятор Hotspot для Java). Они использовали этот опыт, чтобы сделать Dart необычайно гибким в плане компиляции и выполнения.
Dart — один из очень немногих языков (и, возможно, единственный «основной» язык), который хорошо подходит для компиляции как AOT, так и JIT. Поддержка обоих видов компиляции дает существенные преимущества Dart и, особенно, Flutter.
Компиляция JIT используется во время разработки с использованием особенно быстрого компилятора. Затем, когда приложение готово к выпуску, оно компилируется AOT. Следовательно, с помощью передовых инструментов и компиляторов Dart может предоставить лучшее из обоих миров: чрезвычайно быстрые циклы разработки и быстрое время выполнения и запуска.
Гибкость Dart в компиляции и выполнении на этом не заканчивается. Например, Dart можно скомпилировать в JavaScript, чтобы он мог выполняться браузерами. Это позволяет повторно использовать код между мобильными приложениями и веб-приложениями. Разработчики сообщают о 70% повторного использования кода между мобильными и веб-приложениями. Dart также можно использовать на сервере либо путем компиляции в собственный код, либо путем компиляции в JavaScript и использования его с Node.js.
Наконец, Dart также предоставляет автономную виртуальную машину, которая использует сам язык Dart в качестве своего промежуточного языка (по сути, действуя как интерпретатор).
Dart можно эффективно компилировать AOT или JIT, интерпретировать или транспилировать на другие языки. Компиляция и выполнение Dart не только необычайно гибкие, но и особенно быстрые.
В следующем разделе приведен пример того, как сильно скорость компиляции Dart может повлиять на его использование…
Раздел 2.3: Горячая перезагрузка с сохранением состояния
Одной из самых популярных особенностей Flutter является чрезвычайно быстрая горячая перезагрузка. Во время разработки Flutter использует компилятор JIT, который может перезагрузить и продолжить выполнение кода, как правило, менее чем за секунду. Состояние приложения сохраняется при перезагрузке, когда это возможно, поэтому приложение может продолжать работу с того места, где оно было остановлено.
Трудно оценить, насколько важной может быть действительно быстрая и надежная горячая перезагрузка во время разработки, если только вы не испытали ее на себе. Разработчики сообщают, что это меняет то, как они создают свои приложения, описывая, что это похоже на то, как будто они рисуют свое приложение вживую.
Горячая перезагрузка Flutter значительно упрощает реализацию новых идей или экспериментирование с альтернативами, обеспечивая огромный импульс творчеству.
До сих пор мы обсуждали, как Dart помогает разработчику. Следующий раздел посвящен тому, как Dart упрощает создание плавных приложений, которые радуют пользователей.
Раздел 3: Работа с плавностью
Быстрое приложение — это хорошо, но плавное — еще лучше. Даже сверхбыстрая анимация будет выглядеть плохо, если она дергается. Тем не менее, предотвратить рывок может быть сложно, потому что существует очень много разных причин. Dart имеет ряд функций, позволяющих избежать многих распространенных вещей, вызывающих зависание.
Конечно, (как и в любом языке) на Flutter все еще можно написать дерганое приложение. Dart помогает, будучи более предсказуемым и предоставляя разработчику больший контроль над плавностью работы своего приложения, упрощая обеспечение наилучшего пользовательского опыта, без исключения.
Работая со скоростью 60 кадров в секунду, пользовательские интерфейсы, созданные с помощью Flutter, работают намного лучше, чем созданные с помощью других кроссплатформенных сред разработки. И не только лучше, чем кроссплатформенные приложения, но и не хуже лучших нативных приложений.
Раздел 3.1: Компиляция AOT и "мост"
Мы уже обсуждали одну функцию, которая помогает сделать все гладко, и это способность Dart быть AOT-компилированным в собственный машинный код. Предварительно скомпилированный код AOT более предсказуем, чем JIT, поскольку во время выполнения нет пауз для выполнения JIT-анализа или компиляции.
Однако у скомпилированного кода AOT есть еще большее преимущество, заключающееся в том, что он позволяет избежать "моста JavaScript". Когда динамическим языкам (таким как JavaScript) необходимо взаимодействовать с собственным кодом на платформе, они должны взаимодействовать через мост, что вызывает переключение контекста, которое должно сохранять особенно большое количество состояний (возможно, во вторичном хранилище). Эти переключения контекста наносят двойной удар по производительности, потому что они не только замедляют работу, но и могут вызвать серьезные сбои.
Примечание: даже скомпилированному коду может потребоваться интерфейс для взаимодействия с кодом платформы, и это также можно назвать мостом, но обычно он на несколько порядков быстрее, чем мост, требуемый динамическим языком. Кроме того, поскольку Dart позволяет перемещать такие вещи, как виджеты, в приложение, необходимость переходить через мост уменьшается.
Раздел 3.2: Упреждающее планирование, разделение времени и общие ресурсы
Большинство компьютерных языков, поддерживающих несколько параллельных потоков выполнения (включая Java, Kotlin, Objective-C и Swift), используют вытеснение для переключения между потоками. Каждому потоку выделяется "кусочек" времени для выполнения, и если он превышает выделенное время, поток прерывается с помощью переключения контекста. Однако если вытеснение происходит при обновлении ресурса, совместно используемого потоками (например, памяти), это вызывает состояние гонки.
Состояние гонки — это двойной удар по производительности, потому что они могут вызвать серьезные ошибки, включая сбой вашего приложения и потерю данных, и их особенно трудно найти и исправить, потому что они зависят от относительного времени независимых потоков. Слишком часто условия состояния гонки перестают проявляться, когда вы запускаете приложение в отладчике.
Типичный способ исправить состояние гонки — защитить общий ресурс с помощью блокировки, которая препятствует выполнению других потоков, но сами по себе блокировки могут вызвать зависание или даже более серьезные проблемы (включая взаимоблокировку и голодание).
Dart по-иному подошел к этой проблеме. Потоки в Dart, называемые изолятами, не используют общую память, что устраняет необходимость в большинстве блокировок. Изоляты общаются, передавая сообщения по каналам, что похоже на акторов в Erlang или web-workers в JavaScript.
Dart, как и JavaScript, является однопоточным, что означает, что он вообще не допускает вытеснения. Вместо этого потоки явно уступают (используя async/await, Futures или Streams). Это дает разработчику больше контроля над выполнением. Однопоточность помогает разработчику гарантировать, что критически важные функции (включая анимацию и переходы) выполняются полностью, без вытеснения. Это часто является большим преимуществом не только для пользовательских интерфейсов, но и для другого клиент-серверного кода.
Конечно, если разработчик забывает передать управление, это может привести к задержке выполнения другого кода. Однако мы обнаружили, что забывание yield обычно гораздо легче найти и исправить, чем забывание lock (поскольку трудно найти условия состояния гонки).
Раздел 3.3: Распределение и сбор мусора
Еще одна серьезная причина мусора — сбор мусора (Garbage Collection). Действительно, это всего лишь частный случай доступа к общему ресурсу (памяти), который во многих языках требует использования блокировок. Но блокировки могут помешать запуску всего приложения во время сбора свободной памяти. Однако Dart почти всегда может выполнять сборку мусора без блокировок.
Dart использует усовершенствованную схему сборки мусора и распределения, которая особенно быстро распределяет множество недолговечных объектов (идеально подходит для реактивных пользовательских интерфейсов, таких как Flutter, которые перестраивают неизменяемое дерево представления для каждого кадра). Dart может выделять объект одним использованием указателя (блокировка не требуется). Опять же, это приводит к плавной прокрутке и анимации без рывков.
Раздел 4: Унифицированный макет
Еще одно преимущество Dart заключается в том, что Flutter не разделяет макет между вашей программой и дополнительным языком шаблонов или макетов, таким как JSX или XML, а также не требует отдельных инструментов визуального макета. Вот простое представление Flutter, написанное на Dart:
new Center(child:
new Column(children: [
new Text('Hello, World!'),
new Icon(Icons.star, color: Colors.green),
])
)
Обратите внимание, как легко визуализировать вывод этого кода (даже если у вас нет опыта работы с Dart).
Также обратите внимание, что теперь, когда Flutter использует Dart 2, макет стал еще проще и понятнее, поскольку ключевое слово new
является необязательным, поэтому статические макеты могут еще больше выглядеть так, как будто они написаны на декларативном языке макета, например:
Center(child:
Column(children: [
Text('Hello, World!'),
Icon(Icons.star, color: Colors.green),
])
)
Наверняка вы думаете — как можно назвать преимуществом отсутствие специализированных языков верстки? Но на самом деле это меняет правила игры. Вот что написал разработчик в статье под названием "Почему разработчики нативных приложений должны серьезно взглянуть на Flutter":
Во Flutter макеты определяются только с использованием кода Dart. Нет языка XML/шаблонов. Также нет инструмента визуального дизайнера/раскадровки.
Я подозреваю, что, услышав это, некоторые из вас могут даже немного съежиться. На первый взгляд, это была и моя реакция. Не проще ли делать макеты с помощью визуального инструмента. Не усложнит ли код всевозможная логика ограничений.
Ответ для меня оказался нет. Чудо!
Первая часть ответа — это горячая перезагрузка, упомянутая выше.
Я не могу не подчеркнуть, что это на световые годы опережает Android Instant Run или любое подобное решение. Это просто работает, даже в больших нетривиальных приложениях. И это безумно быстро. Невероятная сила Dart.
На практике это делает интерфейс визуального редактора излишним. Я совсем не скучал по довольно приятной автоматической компоновке XCode.
Dart создает краткий и простой для понимания макет, а "безумно быстрая" горячая перезагрузка позволяет мгновенно увидеть результаты. И это включает в себя нестатические части вашего макета.
В результате я гораздо продуктивнее писал макеты во Flutter (язык Dart), чем в Android & XCode. Как только вы освоитесь (для меня это была пара недель), вы значительно сократите накладные расходы из-за того, что происходит очень мало переключений контекста. Не нужно переключаться в режим дизайна, брать мышь и начинать щелкать. А потом задаетесь вопросом, нужно ли что-то делать программно, как этого добиться и т.д. Все программно. И API очень хорошо разработан. Вскоре он станет ещё более интуитивно понятным и намного мощнее, чем конструкции, предлагаемые автоматической и XML разметками.
Например, вот простой макет списка, который добавляет разделитель (горизонтальную линию) между каждым другим элементом, определенным программно:
return new ListView.builder(itemBuilder: (context, i) {
if (i.isOdd) return new Divider();
// ...
});
Во Flutter весь макет находится в одном месте, независимо от того, является ли он статическим или программным макетом. А новые инструменты Dart, в том числе Flutter Inspector и представление структуры (которые используют преимущество того, что все макеты находятся в одном месте), делают сложные и красивые макеты еще проще для понимания.
Раздел 5: Является ли Dart проприетарным языком?
Нет, Dart (как и Flutter) имеет полностью открытый исходный код с чистой лицензией, а также соответствует стандартам ECMA. Dart популярен как внутри Google, так и за его пределами. Внутри Google это один из самых быстрорастущих языков, с другой стороны, репозиторий Dart имеет более 100 внешних редакторов.
Еще лучшим показателем открытости Dart является рост сообщества за пределами Google. Например, мы наблюдаем постоянный поток статей и видео о Dart (включая Flutter и AngularDart) от третьих лиц, некоторые из которых я процитировал в этой статье.
Помимо внешних редакторов самого Dart, в общедоступном репозитории пакетов Dart имеется более 3000 пакетов, включая библиотеки для Firebase, Redux, RxDart, интернационализации, шифрования, баз данных, маршрутизации, коллекций...
Раздел 6: Легко ли будет найти программистов Dart?
Если Dart знает не так много программистов, будет ли сложнее найти квалифицированных программистов? По иронии судьбы, Dart облегчает поиск программистов, потому что это невероятно быстрый язык для изучения. Программисты, которые уже знают такие языки, как Java, JavaScript, Kotlin, C# или Swift, могут начать программировать в Dart почти сразу. Кроме того, горячая перезагрузка побуждает пользователей играть с Dart и пробовать новые вещи, что делает изучение Dart еще быстрее и приятнее.
Вот как один программист написал об этом в статье под названием "Почему Flutter взлетит в 2018 году":
Dart — язык, используемый для разработки приложений Flutter, прост в освоении. У Google есть опыт создания простых, хорошо документированных языков, таких как, например, Go. Пока что мне Dart напоминает Ruby, и изучать его одно удовольствие. И используется он не только для мобильных устройств, но и для веб-приложений.
Из другой статьи о Flutter и Dart под названием "Почему Flutter? А не фреймворк Х? или еще лучше, почему я иду ва-банк":
Flutter использует язык Dart, созданный Google. Если честно, я не фанат строго типизированных языков, таких как C# или Java, но я не знаю, почему способ написания кода в Dart кажется другим. И мне очень удобно ее писать. Может быть, потому, что это очень просто выучить и интуитивно понятно.
Dart был специально разработан для того, чтобы быть знакомым и легким в освоении, обладающим обширными возможностями и тестированием UX. Например, в первой половине 2017 года команда Flutter провела UX-исследование с участием восьми участников. Мы дали им краткое введение во Flutter, а затем отпустили их на час или около того, создав простое представление. Все участники смогли быстро начать программировать, хотя раньше никогда не использовали Dart. Они были сосредоточены на написании реактивных представителей, а не на языке. Dart просто работал.
В конце концов, один участник (который продвинулся особенно далеко в выполнении задания) ничего не упомянул о языке, поэтому мы спросили их, понимают ли они, какой язык они используют. Они не знали. Язык не имел значения, они программировали в Dart за считанные минуты.
Трудная часть изучения новой системы, как правило, заключается не в изучении языка, а в изучении всех библиотек, фреймворков, инструментов, шаблонов и лучших практик для написания хорошего кода. А библиотеки и инструменты Dart исключительно хороши и хорошо документированы. В одной статье говорится: "В качестве бонуса они также очень заботятся о своей кодовой базе и у них лучшая документация, которую я когда-либо видел". Те небольшие усилия, которые тратятся на изучение Dart, легко компенсируются экономией времени на изучение всего остального.
Как прямое доказательство, крупный проект внутри Google хотел перенести свое мобильное приложение на iOS. Они собирались нанять iOS-программистов, но вместо этого решили попробовать Flutter. Они отслеживали, сколько времени потребовалось разработчикам, чтобы освоить Flutter. Их результаты показали, что программист может изучить Dart и Flutter и стать продуктивным за три недели. Это сопоставимо с пятью неделями, которые они ранее наблюдали, чтобы научить программистов работать только на Android (не говоря уже о том, что им пришлось бы нанимать и обучать разработчиков для iOS).
Наконец, статья "Почему мы выбрали Flutter и как он изменил нашу компанию к лучшему" написана компанией, которая перенесла свое крупное корпоративное приложение на Dart на всех трех платформах (iOS, Android и веб). Их выводы:
Нанимать людей гораздо проще. Теперь мы ищем лучшего кандидата, независимо от того, из разработки веб-приложений, iOS или Android.
Теперь у нас в 3 раза больше пропускной способности, поскольку все наши команды объединены на единой кодовой базе.
Обмен знаниями всегда на высоте!
Они смогли утроить свою производительность, используя Dart и Flutter. Это не должно удивлять, учитывая то, что они делали раньше. Они, как и многие компании, создавали отдельные приложения для каждой платформы (веб, iOS и Android), используя разные языки, инструменты и программистов. Переход на Dart означал, что им больше не нужно было нанимать трех разных программистов. И им было легко перевести своих существующих программистов на Dart.
Также они обнаружили, что как только программисты начинают использовать Flutter, они часто влюбляются в Dart. Им нравится лаконичность языка и отсутствие церемоний. Им нравятся языковые функции, такие как каскады, именованные параметры, async/await и Streams. И, прежде всего, им нравятся функции Flutter (например, горячая перезагрузка), которые стали возможными благодаря Dart, и красивые, производительные приложения, которые Dart помогает им создавать.
Раздел 7: Dart 2
На момент публикации этой статьи выходит Dart 2. Dart 2 ориентирован на улучшение опыта создания клиентских приложений, включая скорость разработки, улучшенные инструменты для разработчиков и безопасность типов. Например, Dart 2 имеет систему звуковых типов и вывод типов.
Dart 2 также делает ключевое слово new
необязательным. Это означает, что многие представления Flutter можно описать без использования каких-либо ключевых слов, что делает их менее загроможденными и более удобными для чтения. Например:
Widget build(BuildContext context) =>
Center(child:
Column(children: [
Text('Hello, World!'),
Icon(Icons.star, color: Colors.green),
])
)
Dart 2 также использует вывод типов, чтобы сделать многие варианты использования ключевого слова const
необязательными, не требуя избыточного указания const
внутри контекста const
. Например, заявление:
const breakfast = {
const Doughnuts(): const [const Cruller(), const BostonCream()],
};
Теперь можно заменить на это:
const breakfast = {
Doughnuts(): [Cruller(), BostonCream()],
};
Поскольку breakfast
является const
, предполагается, что все остальное также является const
.
Раздел 8: Секрет во внимании
Улучшения в Dart 2 сосредоточены на оптимизации разработки на стороне клиента. Но Dart по-прежнему будет отличным языком для создания серверных, настольных, встроенных систем или других программ.
Внимание — это хорошая вещь. Почти все устойчивые популярные языки выиграли от того, что были под огромным вниманием людей. Например:
C был языком системного программирования для написания операционных систем и компиляторов. Он стал чем-то большим.
Java был языком, разработанным для встраиваемых систем.
JavaScript был языком сценариев для веб-браузеров.
Даже очень оклеветанный PHP преуспел, потому что он был ориентирован на написание персональных домашних страниц (откуда он и получил свое название, "Personal Home Pages").
С другой стороны, многие языки явно пытались (и потерпели неудачу) быть полностью универсальными, например, PL/1 и Ada, среди множества других. Самая распространенная проблема заключается в том, что без внимания людей эти языки стали бесполезными.
Многие функции, которые делают Dart отличным языком для клиентской части, также делают его лучшим языком для использования на стороне сервера. Например, тот факт, что Dart избегает упреждающей многозадачности, дает ему те же преимущества, что и Node на сервере, но с гораздо лучшим и более безопасным набором текста.
Тоже самое касается написания программного обеспечения для встраиваемых систем. Способность Dart надежно обрабатывать несколько одновременных входных данных является ключевой здесь.
Наконец, успех Dart на клиенте неизбежно вызовет больший интерес к его использованию на сервере — точно так же, как это произошло с JavaScript и Node. Зачем заставлять людей использовать два разных языка для создания клиент-серверного программного обеспечения?
Заключение
Это потрясающее время для языка Dart. Людям, использующим этот язык, он нравится, а новые функции Dart 2 делают его еще более ценным дополнением к вашему арсеналу инструментов. Если вы еще не использовали Dart, я надеюсь, что эта статья предоставила вам ценную информацию о том, на что способен Dart, и что вы попробуете его — и Flutter.