Elm vs Vue/React which one do you prefer to use with Phoenix, and why?

Clearly I haven’t looked in the right places; I never signed up for FunctionalWorks because they didn’t offer evidence on their homepage of anything of interest to me, and its impossible to browse listings without signing up, which is bizarre.

Looking in the usual places like Indeed and StackOverflow I can find jobs that have the word Purescript in them, but almost always in the “gee its cool, but we don’t actually use it here” type reference.

Yes I used to think thoughts like those. Then I actually used it and now I feel differently. YMMV.

I agree that this is usual, but I’d say it applies to most languages that are actually interesting. It’s either that or “You will be working with Java, Scala and [exciting language]” and what the listing doesn’t really spell out is that about 1% of the code is actually in this exciting language and the rest is just JVM boilerplate.

It’s too bad that they have this pattern. I never noticed it because I just signed up right away when I was looking for work at one point. They offer an excellent user experience if you do sign up. It’s very much tailored towards finding you opportunities, with emphasis on you ranking the languages you are interested in, etc…

When I was looking previously I found something like 4-5 positions that explicitly said that they had front-ends written in PureScript and two of those mentioned halogen specifically, though not really as a requirement.

If one is into Scala there are 160 listings on FW right now and in the case of Scala they actually put Scala in the position title, so it’s a good bet that you’d be spending most of your time writing it.

(For less meaningful reference, there are 38 Haskell listings and 9 OCaml listings, though a good portion of the OCaml ones list OCaml as an auxiliary language)

Given your familarity with Vue I suspect you know how you would use it to create the following trivial example.

So I will go forward to attempt to illustrate why I think viewing JSX “as markup” is misguided and limiting.

Personally I’m no fan of JSX but I’m not the first to feel that way.

https://gist.github.com/gaearon/a9bbb73d57b6e4cc17d7b50807b62f9a#file-counter-js
shows your typical introductory React custom class component. This style of component is sometimes referred to as a “vanilla component”. Vanilla components have their use and place but as such they don’t seem to be really representative of the type of components that are used in “sophisticated front-ends”. So I’m going to take this basic component and deliberately over-engineer it to highlight some other aspects that occur in applications with more complicated requirements.

Every component instance tree starts with a root:

// File: src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import ContainerComponent from './ContainerComponent';
import registerServiceWorker from './registerServiceWorker';

ReactDOM.render(
  <ContainerComponent />,
  document.getElementById('root')
);
registerServiceWorker();

<ContainerComponent /> is JSX but it simply references a plain custom component (and it’s not some magical “app component”). It’s nature is such that it “assembles” a tree of lower level component instances, or in DDD terms it is an “Aggregate” of a larger whole. The render can be easily rewritten without JSX as:

ReactDOM.render(
  React.createElement(ContainerComponent),
  document.getElementById('root')
);

Again the name createElement is unfortunate because it evokes an association with the DOM Element object. React Components, Elements, and Instances hints that it actually creates something of a specification object that references a component constructor and a “properties” object (with child “specifications”) for a particular component instance.

So <ContainerComponent /> is simply an XML tag to reference the ContainerComponent constructor function - it’s not markup.

Moving on to the next piece:

// File: src/ViewComponent.js (ES2015 Module)
import React from 'react';

// functional (stateless) component
// is passed a "props" object
const ViewComponent = ({ value, increment, decrement }) =>
  <div>
    {value}
    <button onClick={increment}>+</button>
    <button onClick={decrement}>-</button>
  </div>;

export default ViewComponent;

It’s a simple ES2015 module that contains some JSX that looks a lot more like markup because of the familiar div and button tags. But just as before those are just references to some predefined React DOM components. It can be rewritten as:

import {button, div} from 'react-dom-factories';

const ViewComponent = ({ value, increment, decrement }) =>
  div({},[
    value,
    button({key: 'increment', onClick: increment},['+']),
    button({key: 'decrement', onClick: decrement},['-'])
  ]);
  
export default ViewComponent;

or even

import {createElement} from 'react';

const ViewComponent = ({ value, increment, decrement }) =>
  createElement('div',{},[
    value,
    createElement('button', {key: 'increment', onClick: increment},['+']),
    createElement('button', {key: 'decrement', onClick: decrement},['-'])
  ]);

export default ViewComponent;

Now lets gets some terminology straight:

  • The ViewComponent instance is the Owner of the div and button instances (and of value). Lot’s of sources refer to ViewComponent as the Parent which is incorrect (ViewComponent would be the parent of children which are passed in the props object - but this example ignores those, so if any are passed they simply won’t be rendered).
  • The button instances and value will be handed to the div instance as children (inside the div'sprops object). So [value,button,button] are children of div, while div is the parent of [value,button,button]. So with JSX the owner ViewComponent gets to declare the children of div by placing them in between the opening and closing tags <div></div>.

Now <button onClick={increment}>+</button> clearly has something that looks like an (onClick) attribute. The notation can be changed to <button {...{onClick: increment}}>+</button>, expressing that the properties of the {onClick: increment} object should be spread over the props object intended for the button instance. So these “attributes” are simply key value pairs for the props object.

const ViewComponent = ({ value, increment, decrement }) =>

shows what props look like on the receiving end. ViewComponent expects an incoming value for display and increment and decrement functions that are used to notify the owner instance (or something further up) when state changes are required. The props object essentially describes the runtime interface of the ViewComponent instance. Incoming data is simply delivered as values while function( value)s are provided as a means to move outbound data. In effect the ViewComponent instance acts as a distribution point for the prop-values among its nested component instances.

If ViewComponent was an electronic component the props property names would be equivalent to “contacts” or “wire terminals”, while the property values act like “wires” (or the information on those wires).

Now to the final piece of the puzzle, ContainerComponent:

// File: src/ContainerComponent.js (ES2015 Module)
import React, { Component } from 'react';
import ViewComponent from './ViewComponent';

// Based on:
// https://gist.github.com/gaearon/a9bbb73d57b6e4cc17d7b50807b62f9a#file-counter-js

// ContainerComponent initial state
const initialState = () => ({ value: 0 });

// action types
const INCREMENT = 'INCREMENT';
const DECREMENT = 'DECREMENT';

// action creators
const incrementBy = (n) => ({
  type: INCREMENT,
  payload: n
});
const decrementBy = (n) => ({
  type: DECREMENT,
  payload: n
});

// state updates
const incrementState = (state, n) => {
  const value = state.value + n;

  return { ...state, ...{ value }};
}; // use "spread in object literals" to update "value" in the "state" object.
   // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals

const decrementState = (state, n) => {
  const value = state.value - n;

  return { ...state, ...{ value }};
};

// reducer
const reducer = ({type, payload}, state) => { // takes action and state
  switch (type) {                             // to produce next state
    case INCREMENT:
      return incrementState(state, payload);
    case DECREMENT:
      return decrementState(state, payload);
    default:
      return state;
  }
};

class ContainerComponent extends Component {
  constructor(props) {           // ignores props i.e no communication
    super(props);                // with OWNER component

    this.state = initialState(); // i.e. give React the initial state
  }                              // for this particular instance
                                 // Everwhere else "this.state" is READ-ONLY
                                 // while "this.props" is always READ-ONLY
                                 // i.e. they are managed by React on behalf
                                 // of the instance.

  send(action) {                 // i.e. emulate ReasonReact.reducerComponent
    this.setState(prevState => reducer(action, prevState));
  }                              // setState "requests" a change of state
                                 // for this instance, i.e. state changes
                                 // asynchronously.

  increment = () => {            // function for nested component instance
    this.send(incrementBy(1));   // to initiate an action
  };

  decrement = () => {            // Using "Property Initializers" combined with
    this.send(decrementBy(1));   // "Arrow Functions" to avoid having to bind
  };                             // member functions in constructor
                                 // (if they are passed to nested component instances)
                                 // https://github.com/tc39/proposal-class-fields
                                 // http://2ality.com/2017/07/class-fields.html#initializers
                                 // https://reactjs.org/docs/handling-events.html
  render() {
    const {state: {value}, increment, decrement} = this;

    return <ViewComponent {...{value, increment, decrement}} />;
  } // https://reactjs.org/docs/jsx-in-depth.html#spread-attributes
}

export default ContainerComponent;

// See also:
// https://reactjs.org/docs/lifting-state-up.html#lifting-state-up

A lot of code, yes. But as I said it is deliberately over-engineered. But it’s still a just a plain ES2015 module. Interestingly all of the rendering to the browser’s DOM is delegated to ViewComponent in:

    return <ViewComponent {...{ value, increment, decrement }} />;

Again the JSX can be gotten rid of with:

    return React.createElement(ViewComponent, { value, increment, decrement } , []);

Dan Abramov discusses container components in Presentational and Container Components. So all this code is simply infrastructure to support the operation of the nested, lower-level component instances - it’s a component and while the instance may hold data that gets rendered to the browser’s DOM, it’s not directly involved with any DOM elements in the browser - the components responsible for DOM elements are div and button under ViewComponent.

This example can be pushed even further by removing ViewComponent from ContainerComponent and adding a render prop to ContainerComponent:

// File: src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import ContainerComponent from './ContainerComponent';
import ViewComponent from './ViewComponent';
import registerServiceWorker from './registerServiceWorker';

const render = (props) => <ViewComponent {...props} />;

ReactDOM.render(
  <ContainerComponent {...{render}} />,
  document.getElementById('root')
);
registerServiceWorker();

or

// File: src/index.js
import React, {createElement}  from 'react';
import ReactDOM from 'react-dom';
import ContainerComponent from './ContainerComponent';
import ViewComponent from './ViewComponent';
import registerServiceWorker from './registerServiceWorker';

const render = (props) => createElement(ViewComponent, props, []);

ReactDOM.render(
  createElement(ContainerComponent, {render}, []),
  document.getElementById('root')
);
registerServiceWorker();

and

// File: src/ContainerComponent.js (ES2015 Module)
import { Component } from 'react';

// ...

class ContainerComponent extends Component {
  // ...

  render() {
    const { props: {render}, state: {value}, increment, decrement } = this;

    return render({value, increment, decrement});
  }
}

export default ContainerComponent;

So does <ContainerComponent {...{render}} /> (i.e. JSX) have anything to do with markup?

  • It’s XML used to declare the component instances in an instance tree.
  • The tag name identifies the component constructor function.
  • The collective “attributes” on the tag define the component’s props - the component’s “runtime interface” and defines how that “runtime interface” is “wired” for this particular instance.
  • The tags between the opening and closing components declare the child component instances for this particular component instance.

Simple React Patterns: Dealing With Side-Effects In React explores some other common React patterns in use.

Now container components aren’t a universal thing:

People are reading way too much into Presentational vs Container components divide. Almost regretting I wrote that.

Container components are a good place to put strictly local logic and state to keep it out of the view component(s) but at the same time a view component could easily nest a one or more lower-level container components.

Putting state in container components can by used to avoid introducing Redux into an application.

You don’t need a single source of truth for everything. Just make sure you have a single source of truth for any particular thing.

See also: You might not need Redux.

Reusablity is often used to justify container components - but I think that is just falling into the old OO trap. Good boundaries are always important irrespective of reusability concerns in order to clearly define what exactly passes into and what passes out of the boundaries. Selecting the right boundary is rarely easy - it takes practice, sometimes lots of it.

FYI: David Nolen (ClojureScript, Om Next) talking favourably about React.

Seems some people think containers could be a good idea in Vue: Structure a Vue.js App from Containers and Components.

See also JavaScript Report: How Is React Different from Vue?.

7 Likes

First, thank you for taking the time to write this throughly explanation.
I understand that JSX is not a “templating” language, what I’m arguing is that the overhead of learning how it works is similar to understanding how vue templates work because this is the argument that I hear the most - “it’s just plain old js you don’t need to learn a DSL…”. That’s not true.

Given that Vue itself can render JSX (you simply don’t because, well, it’s way more verbose and arguably harder to understand immediately what is the end result, specially if you have a lot of it) and that all templates are resolved to render functions themselves I would say that they’re pretty similar in that regard, where the “templates” in vue can be seen as “syntactic” sugar for the manual callouts to render - underneath they will do the same they just save me all the boiler plate code and in turn give me a neat organisation for my code. In fact when you instantiate a Vue instance you have a render property, where you use createElement and either pass a dom element specification, or, pass a component.

In a JS file:

import socket from './shared/socket'
import Vue     from 'vue'
import Vuex   from 'vuex'

import main_store  from './store/main_store.js'
import tree_root     from './views/tree_root.vue'

import '../css/app.scss'

Vue.use(Vuex)

const store  = new Vuex.Store(main_store)

new Vue({
        el: '#tree-root',
        store,
        mounted: function() {
            store.dispatch('get_user')
        },
        render: function(createElement) {
            return createElement(tree_root)
        },
        destroyed: function() {
            unsync()
        }
    })

This will render a dom representation of tree_root, replacing everything inside the element #tree-root with the result of that render call. This tree_root component can be a single file component (but it can also be JSX, or a createElement spec), inside this component I have all my code neatly organised in 3 sections (although 99% of the time I don’t use the styling section, but you can, and you can scope it automatically as well), one is the template section, which allows me to have things like:

<template>
 	<div id="tree-root-wrapper">
   		<a-component some_prop="literal_string" :another_prop="any_js_expression"/>
   		<b-component/>
		<h1 class="title" @click="a_filthy_directive" @key.up.enter="another_one">Something</h1>
		<p :class="[ 'a-literal-class', aJSexpression, { 'some-other-class': truthyOrFalseJSexpression } ]">{{a_Prop}}</p>
                       <c-component v-if="truthyEvaluator"/>
                       <d-component v-else>
	</div>
</template>

(although I mentioned props I seldom use them, besides when using some lib because they require it, and even then usually the values I feed the props with come from a vuex store)

And each of these components, because they’re fractal, can render its own tree and that will substitute the component call in the template.

Then a JS part, which allows me to have things like this:

<script>
import { mapGetters } from 'vuex'

export default {
    name: 'games-index',
    data() {
        return {
            message: "",
            password: "",
            selected_game: false,
            tome: "",
            show_create_warning: false
        }
    },
    computed: {
        ...mapGetters([
            'messages',
            'games',
            'user_id',
            'open_game',
            'duel_start',
            'timer'
        ])
    },
    methods: {
        send_message() {
            this.$store.commit('new_message', this.message)
            this.message = ''"
        },
        prepare_join(game) {
            this.selected_game = game
        },
        //.....
    }
}

Where the computed properties are just references to a Vuex Store getters. In this case it’s only a top-level store, but you can namespace it and then use:

...mapGetters({
     'name_I_want': "namespace_level_1/namespace_level2/getter_name"
})

Or

...mapGetters('namespace_level_1/namespace_level2', { 
   'name_I_want': "getter_name",
   'name_2' "another_getter_on_the_same_namespace"
})

This is syntactic sugar for mapping a property in your component to a getter in your store. Similarly you have mapActions for async dispatches and mapMutations for mutations (commits) that well, mutate the state of your store. The async handlers can also themselves perform a sequence of mutations, call other actions, getters or access the state. You can explicitly define and express those boundaries through name-spaces and handlers, although there’s nothing forcing you to do it.

But if you don’t want to use the mapping helpers, you can write something like this too, where you call the store without the sugar:

methods: {
       close_game(id) {
            this.$store.commit('close_game', id)
            // some people will argue that this is bad, this <- refers to the vm instance and not the component and not the component scope alone - I argue this allows for a great deal of flexibility, and again, the boundaries are what you make of them
        },
}

Now I can use everything I define in methods, computed, data, etc inside the component. Plus I can share these from the top level of the instance to all the children components through mixins. And I can override them individually in the components.

This to me makes for a very readable structure. Do you have to invest some time learning it? Yes although I would argue that less than React to use to good results - because I did look into react before vue, and sincerely it never clicked while Vue did almost immediately - in the end it’s a few keywords you need to learn, and they’re very familiar.

Perhaps if I had started with React it would make more sense to me than Vue. I’m also don’t agree with purist arguments - while I agree that understanding the underlying concepts is important - I can’t really just use a tool because it’s “purer” if that comes at extra mental strain to execute to no significant benefit while having its own trade-offs and worse parts. Now I agree that probably if you write React you’re better equipped to write good JS in case React goes away, than if you write Vue and Vue goes away, because vue is a bit more magic. At the same time, we use frameworks so that we don’t need to re-invent the wheel. I like the wheels they designed in vue - and given that if react goes away you will still probably not be able to replicate it alone by JS yourself, the point is kinda moot.

I’ve written (and I’m still writing) a quite complex app, with several different bundles in Vue (some with ~= 2000lines between templates, js and several namespaced stores - not counting styling) which interacts with an Elixir backend through Phoenix Channels and I’m very happy with the setup. It’s actually quite sane to read, change and expand.

This is a possible store definition
main_store.js

import axios  from 'axios'
axios.defaults.headers.common['Accept'] = 'application/json';
axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
axios.defaults.withCredentials = true;

const state = {
    socket: false,
    csrf: false,
    user: false,
    user_channel: false,
    game_channel: false,
    pods_channel: false,
    axios: axios,
    loaded: false
}

const mutations = {
    set_csrf(state, token) {
        state.csrt = token
        state.axios.defaults.headers.common['X-CSRF-Token'] = token;
    }
}

const actions = {
    request({ state }, request) {
        return state.axios(request)
    },
    get_user({ state, commit, dispatch }) {
        dispatch("request", {
            method: "get",
            url: "/identify"
        }).then(response => {
            //commit
        }).catch(error => {
            //log error & notice
        })
    }
}

const getters = {
    user(state) {
        return state.user
    }
}

export default {
    state,
    getters,
    actions,
    mutations,
    modules: {

    }
}
// @returns {VNode}
createElement(
  // {String | Object | Function}
  // An HTML tag name, component options, or async
  // function resolving to one of these. Required.
  'div',

  // {Object}
  // A data object corresponding to the attributes
  // you would use in a template. Optional.
  {
    // (see details in the next section below)
  },

  // {String | Array}
  // Children VNodes, built using `createElement()`,
  // or using strings to get 'text VNodes'. Optional.
  [
    'Some text comes first.',
    createElement('h1', 'A headline'),
    createElement(MyComponent, {
      props: {
        someProp: 'foobar'
      }
    })
  ]
)

And lastly JSX makes me think a bit of how I used jQuery, $('#el').append($("<div class="+variable+" onClick="+clickFun()+">...</div>"))

1 Like

Big fan of Vue and Phoenix. Actually, bigger fan of Nuxt (SSR Vue) talking to a headless GraphQL Phoenix API via sockets; which is what I used for Edgewise (https://edgewiserealty.com).

3 Likes

There is very little to JSX. Many people use it by choice (because it gives them the warm, fuzzy markup feeling) while others simply fall in line to follow the convention that seems to have been adopted by the majority. JSX is just about Components, prop values and children - that’s it. Personally I never felt JSX added much value - but then again semicolons never bothered me in JavaScript either. In Clojure/ClojureScript I was perfectly happy to use hiccup for rendering HTML. In Elm using straight functions made a lot of sense (the arguments being one list for the property/attribute values and another list for the children).

When it comes to templating languages I never understood the appeal of Jade (or Pug) - I was under the impression that template markup was supposed to “look-and-feel” like HTML.

Now Vue’s template language on the other hand is a lot more complicated and truely classifies as yet another thing you have to learn. While it starts out looking like good-ole HTML,

  • there are the Vue specific directives (and short forms) and the proper way to use each of them; (and understanding subtleties like v-if vs v-show).
  • special tags like slot, keep-alive and how they work in concert with the rest of Vue.
  • and all this is just ceremony to ultimately transpile to a JavaScript render function call!

just save me all the boiler plate code and in turn give me a neat organisation for my code.

I think that some “boilerplate” can be managed with ES modules and the perception of Vue requiring “less” may be indicative of it having an opinion and it’s advocates sharing that opinion. React leaves you with the tyranny/paradox of choice - and the responsibility to manage the “opinion” that you choose (which also implies that you have room to choose poorly).

The best argument I’ve seem for needing a template langauge is to be able to work together with web designers who are used to working with HTML. Seems the current solution in the React space is to define all the markup related (view) components as functional (stateless) components (using JSX) and then to have designers style them in storybook.

I did look into react before vue, and sincerely it never clicked while Vue did almost immediately.

I suspect React only clicks with people who approach UI solutions from the rendering perspective. If you have familiarity with a templating based approach like EEx you would be predisposed to feeling that templating is a requirement and therefore you would lean towards something like Vue. React didn’t click with be either but I kept digging because om, reagent, re-frame and thermite use it - I figured there had to be a reason why.

Ultimately React is a bit lower level than Vue and therefore more work for the developer who has to figure out what the best way to use it for a particular UX problem is. Vue is more opinionated while covering a lot of the popular use cases.

I’m also don’t agree with purist arguments

I don’t think it’s about purity but more about explicit, visible, traceable flows of data.

Ultimately Vue’s API seems larger and more complicated and while the reactivity system isn’t particularly complicated, one has to be aware how it works (but React’s shouldComponentUpdate has its quirks too).

Because of React’s “simpler API” it is usually more work for the developer to determine how to use it effectively. (If you are Facebook averse and don’t need native there is always Preact and Inferno (JavaScript Report: The Best JavaScript Frameworks You’re Not Using)).


LispCast: React: Another Level of Indirection (2014-01-01)
6 Things Reacters Do that Re-framers Avoid

dotJS 2017 -Adrian Holovaty - A framework author’s case against frameworks

1 Like

I agree, you do need to learn it to use it, the documentation is small and it’s both progressive in implementation and concepts. I usually like templating languages, specially for server side html, like slim, because they clean up the view files and make them much clearer (and more like a program declaration set by indentation) but funnily in vue I don’t, because I have all the other markup (directives, etc) to use on the elements that stripping them of their tags and delimiters makes it less readable (I might need to give pug another go sometime, perhaps it was just b/c of being unfamiliar with vue at the time).

The following aren’t just interesting because of the comparisons but because of how the technologies are compared.

Choosing a Javascript Framework in 2017 (2017-03-23)

  • Bias: Ember
  • Others: React, Angular 2
  • Dave Bouwman remarks that in his opinion Ember has found all the right “seams” (i.e. selected the correct boundaries).

NDC Oslo 2016 - Choosing a JavaScript Framework - Rob Eisenberg (June)

  • Bias: Aurelia
  • Others: AngularJS, Angular 2, Ember, Polymer, React

Now while Vue.js isn’t part of these comparisons it should be clear where it fits in - it is a framework of the minimalist persuasion - so comparatively speaking there should be less to learn to get going.

I personally much prefer the Vue templating syntax over pretty much anything else; especially JSX. I really don’t think it takes much to “learn” at all. I actually like directives, as it’s far less verbose than other templating languages. You don’t need to know much of anything about Vue, to intuitively understand what’s going on here:

<ul class="thumbnails">
  <li v-for="image in images" :key="image.id" class="thumbnail">
    <img :src="image.url" :title="image.title" :alt="image.description" />
  </li>
</ul>

I appreciate the intuitiveness of that syntax, for the same reasons I appreciate the intuitiveness of Elixir. The fact that a designer also understands that is bonus points.

I would argue slots are worth the time to learn (and let’s be honest, it doesn’t take a PhD). For example, this uses a custom modal component as a wrapper where the dialog content is a slot:

<Modal>Nice!</Modal>

If that looks simple, that’s because it is. The “hard” parts are abstracted away into the parent component; creating reusable components that are inline with where the standards specs are with webcomponents / slots. For instance, you could swap out that <Modal /> Vue component with a Stencil component.

I use slots in custom <Panel />, <Modal />, <Alert />, and <Snackbar /> components (among others).

This is JSX equivalent…

<ul className="thumbnails">
  {
    images.map(image => 
      <li key={image.id} className="thumbnail">
        <img src={image.url} title={image.title} alt={image.description} />
      </li>
    )
  }
</ul>

<Panel/> <Modal/> <Alert/> <Snackbar/>

It looks the same… but no directives, and more functions. I don’t like directives, it corrupts the html markup imho.

And composability is also present in React. Having a custom library of components You just drop in is not Vue specific.

What I prefer in React is being cross platform.

<View>
  <Text>Hi!</Text>
</View>

This code works on IOS and Android…

I think an argument can be made that both are “corrupting” the HTML (eg. className).

IMO, that doesn’t look the same to me; that looks like 50% more code to accomplish the same thing (if you combine the curly braces / parenthesis). And that compounds as you add more logic. For instance, even just a little conditional rendering:

<template v-if="userIsLoggedIn">
  <ul v-if="images.length > 0" class="thumbnails">
    <li v-for="image in images" :key="image.id" class="thumbnail">
      <img :src="image.url" :title="image.title" :alt="image.description" />
    </li>
  </ul>
  <div v-else class="no-images">Sorry, no images yet.</div>
</template>
<button v-else @click="onLoginClick">Log in to view images</button>

But, if JSX is your thing, you can actually use it in Vue.

Definitely a valid point about React Native. The Ionic team has been working on Vue Native, but it’s still very early. It really hasn’t been an issue for me, but if being able to write native apps using JS is a business need, then React is a solid choice there.

It’s true it depends on use case… and personal taste.

For information, class and for are js commands, so className, and forHtml are JSX equivalent. Those are the only 2 attributes that need to be translated.

And I needed to make a mobile client for a Phoenix backend.

v-if is equivalent to

{ 
  cond && <div>true</div>
}

and v-if v-else

{ 
  cond ?
  <div>true</div> :
  <div>false</div>
}

I havent worked with JSX yet, but at a glance I get flashbacks from my days of php templates where people made basicly the same arguments agains template engines like Twig. :smile:

Ooo I was not trying to make arguments, I was even trying to avoid them, because it’s really a personal choice. But to be fair to JSX, I put the equivalent code to @bjunc example, so anyone can make his own judgement :slight_smile:

1 Like

I think that Vue can work well with Phoenix forms like so:

<%= text_input f, :name, class: "form-control", v_model: "message" %>

Generally I just include vue.js from a CDN on the form pages I need it on and make the form or its container my ‘app’.

I’m definitely not a vue expert, but this has been helpful for simple bindings etc.

If I’m not mistaken you are penalizing the client twice:

  • The Full (Runtime + Compiler) version has to be downloaded instead of just the Runtime version.
  • Vue.js then has to compile the templates on the client - further delaying when the page will be ready.
  • If you are using the in-DOM HTML of the mounting DOM element as the template (aka DOM template), the browser will attempt to parse features only intended for Vue.js which can lead to issues.

See: Why You Should Avoid Vue.js DOM Templates and DOM Template Parsing Caveats

1 Like

Thanks for posting! I’m very new to Vue.js and hadn’t considered this. I’ve only been peppering Vue components into a few places trying to answer the same question as OP. Thanks!

https://hackernoon.com/how-it-feels-to-learn-javascript-in-2016-d3a717dd577f

HTH! :-))

2 Likes
3 Likes