В последнее время я много использую VueJS. Это легкая в освоении структура, которая быстро дает результаты. Существует дополнительный пакет Typescript для обеспечения столь необходимой безопасности типов. В целом я очень доволен этим. Однако шаблоны во Vue - это то место, где нарушается безопасность типов. У Typescript просто нет возможности узнать, что там происходит, тогда как он поддерживает JSX / TXS и предоставит вам информацию о типе, например, Компоненты React.

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

Его основатель предлагает начинать с Elm в проектах с малого. Итак, никаких капитальных ремонтов, но исправление глючного винтика где-нибудь с Elm. Я задавался вопросом, могу ли я использовать его в VueJS…

Я создал новый проект Vue с Vue cli

# install vue-cli 
$ npm install --global vue-cli 
# create a new project using the "webpack" template 
$ vue init webpack elm-in-vue 
# install dependencies and go! 
$ cd elm-in-vue 
$ npm install

Когда это будет завершено, вы можете запустить npm run dev, чтобы запустить веб-сервер с вашим новым проектом Vue.

Теперь мы добавим вяз. Сначала мы устанавливаем elm и elm-webpack-loader. В пакете elm есть все инструменты для Elm, тогда как нам нужен загрузчик веб-пакетов, чтобы мы могли связать код Elm, скомпилированный в javascript, в нашем приложении.

npm install --save elm elm-webpack-loader

Конфигурация webpack также должна быть осведомлена о загрузчике Elm.

Добавьте следующие строки в rules в конфигурации вашего веб-пакета. Это в основном указывает веб-пакету использовать компилятор Elm для .elm

// ./build/webpack.base.conf.js 
{ 
   test: /\.elm$/, 
   exclude: [/elm-stuff/, /node_modules/], 
   use: { loader: 'elm-webpack-loader', options: {} } 
},

Компонент Вяза

Итак, теперь мы можем перейти к интеграции Elm в Vue. Сначала я создаю небольшую программу Elm под названием Counter. Если вы знакомы с Elm, его легко понять. Мы используем модуль порта, чтобы иметь возможность взаимодействовать с javascript. У него есть модель, состоящая из одного целого числа, и он отображает две кнопки для увеличения / уменьшения и счетчик. Довольно простой.

-- ./src/Counter.elm 
port module Main exposing (..) 
import Html exposing (Html, button, div, text) 
import Html.Events exposing (onClick) 
main = Html.program { init = init, view = view, update = update, subscriptions = subscriptions } 
-- MODEL 
type alias Model = Int 
init : ( Model, Cmd Msg ) 
init = ( 0, Cmd.none ) 
-- UPDATE 
type Msg = Increment | Decrement | Multiply Int 
update : Msg -> Model -> ( Model, Cmd Msg ) 
update msg model = 
    case msg of 
        Increment -> ( model + 1, watchCounter (toString msg) )    
        Decrement -> ( model - 1, watchCounter (toString msg) )            
        Multiply val -> ( model * val, watchCounter (toString msg) ) 
port counter : (Int -> msg) -> Sub msg 
port watchCounter : String -> Cmd msg 
subscriptions : Model -> Sub Msg 
subscriptions model = counter Multiply 
-- VIEW 
view : Model -> Html Msg 
view model = div [] [ button [ onClick Decrement ] [ text "-" ] , div [] [ text (toString model) ] , button [ onClick Increment ] [ text "+" ] ]

Мост Эльм-Вю

Наша цель - отрендерить эту программу в компоненте VueJs. Для этого мы создаем компонент-оболочку. Это функция, которая принимает модуль Elm и возвращает объект для использования Vue. Объект принимает два свойства: ports для взаимодействия Elm / javascript и flags объект для установки начального состояния программы. Как только вызывается ловушка жизненного цикла объекта mounted, мы находим узел DOM и говорим Elm встроить его; если какие-либо порты установлены, мы используем их для установки моста между Elm и javascript.

// ./src/elm.js 
module.exports = function (elm) {
 return {
    props: {
      ports: {
        type: Function,
        required: false
      },
      flags: {
        type: Object,
        required: false
      }
   },
   
   render: function (createElement, _context) {
      return createElement('div')
   },
   mounted: function () {
      var node = this.$el
      var app = elm.embed(node, this.$props.flags)
      if (this.$props.ports) {
         this.$props.ports(app.ports);
      }
   }
}

Теперь, как мы вызываем это из Vue?

Мы собираемся адаптировать файл App.vue, созданный для нас с помощью инструмента vue-cli.

<!-- ./src/App.vue --> 
<template> 
  <div id="app"> 
    <img src="./assets/logo.png"> 
    <Counter></Counter> 
  </div> 
</template> 
<script> 
import Hello from './components/Hello' 
import * as ElmComponent from './elm' 
export default { 
    name: 'app', 
    components: { 'Counter':     
        ElmComponent(require('./Counter.elm').Main) } } 
</script>

Вы можете видеть, что мы импортируем функцию ElmComponent и вызываем ее с помощью нашего кода Counter.elm. Webpack видит расширение и скомпилирует код. Вызов Main возвращает приложение Elm. Мы регистрируем его локально в Counter, поэтому в шаблоне мы можем назвать его таковым.

Если вы запустите npm run dev сейчас, он должен показать счетчик под логотипом Vue!

Вызов Вяза из javascript

Мы можем использовать порты для отправки сообщений программе Elm. Мы настраиваем порты, используя опору ports при вызове компонента. Это устанавливает свойство ports для нашего объекта Vue. Теперь мы можем использовать обычный синтаксис Vue в кнопке для вызова программы Elm и умножить текущее значение модели на 10.

Свойство counter портов было открыто как порт в приведенном выше коде Elm. Обратите внимание, как было определено принимать только целые числа, если мы отправим что-нибудь еще, будет выдано сообщение об ошибке. См. Здесь о том, как определяется сопоставление между типами Elm и javascript.

<template> 
  <div id="app"> 
    <img src="./assets/logo.png"> 
    <Counter :ports="setupPorts"></Counter> 
    <button @click="ports.counter.send(10)">Multiply by 10</button>    
  </div> 
</template> 
<script> 
import Hello from './components/Hello' 
import * as ElmComponent from './elm' 
export default { 
  name: 'app', components: { 'Counter':   
    ElmComponent(require('./Counter.elm').Main) }, 
  methods: { 
    setupPorts: function (ports) { 
      ports.watchCounter.subscribe(function (message) {    
        console.log(message) 
      }) 
      this.ports = ports 
    } 
  } 
} </script>

Мы также можем прослушивать изменения в модели Elm и уведомлять Vue об этих изменениях. Это делается с помощью порта watchCounter, который мы установили в коде Elm. VueJs может подписаться на этот порт и будет регистрировать все передаваемые сообщения.

Это все, что необходимо для наличия программы Elm в вашем приложении Vue. См. Весь код в этом репо. Также есть пример использования флагов для инициализации вашего приложения elm.

Кроме того, в настройке очень помог эквивалент React.

Первоначально опубликовано на сайте maartenvanvliet.nl 17 августа 2017 г.