Update a user image on the front end using Vue.js and Ajax
In the end I found a solution which doesn't require creating my own controller. I am not sure it is possible to post directly to the $_FILES array in Craft but by using the native FormData object in Javascript you can basically pass all the form fields in the correct format to the vue-resource post method. I found this article useful on my search for solving what I needed. In doing this it kind of bypasses the Vue data model which is not exactly what I was after but it works well and keeps things simple. One caveat is that support for the FileData object is only on modern browsers but you can polyfill for older browsers.

Here is my final test form code:

&lt;form method="post" action="" enctype="multipart/form-data" accept-charset="UTF-8" @submit.prevent="submitForm" id="imageForm"&gt;
  {{ getCsrfInput() }}
  &lt;input type="hidden" name="action" value="users/saveUser"&gt;
  &lt;input type="hidden" name="redirect" value="members/profile/{% include '_includes/helpers/user_profile_link' %}"&gt;
  &lt;input type="hidden" name="userId" value="{{ currentUser.id }}"&gt;

  {% if currentUser.photoUrl %}
    &lt;transition name="fade"&gt;
      &lt;img src="{{ currentUser.photoUrl }}" alt="{{ currentUser.fullName }}" v-if="showImage"&gt;
    &lt;/transition&gt;

    &lt;a href="#" id="userPhoto" v-on:click.prevent="deletePhoto" v-if="showImage"&gt;Delete&lt;/a&gt;

    &lt;input type="hidden" name="deleteUserPhoto" id="deleteUserPhotoInput" value="" v-model="user.deleteUserPhoto"&gt;

    &lt;div class="control"&gt;
      &lt;label for="avatar" class="label"&gt;Member Image&lt;/label&gt;
      &lt;input id="avatar" type="file" name="userPhoto"&gt;
    &lt;/div&gt;
  {% else %}
    &lt;div class="control"&gt;
      &lt;label for="avatar" class="label"&gt;Member Image&lt;/label&gt;
      &lt;input id="avatar" type="file" name="userPhoto"&gt;
    &lt;/div&gt;
  {% endif %}

  &lt;button type="submit" class="button is-primary"&gt;Save Profile&lt;/button&gt;
&lt;/form&gt;


Here is my final Vue.js code:

Vue.http.options.emulateJSON = true;
Vue.http.options.emulateHTTP = true;

var app = new Vue({
  delimiters: ['@{', '}'],
  el: '#app',
  data: {
    user: {
      deleteUserPhoto: '',
      userId: '{{ currentUser.id }}',
      action: 'users/saveUser'
    },
    showImage: true
  },
  methods: {
    submitForm: function() {
      var form = document.getElementById('imageForm');
      var formData = new FormData(form);

      this.$http.post('/', formData)
        .then(function(response) {
          //this.updated = true;
        });
    },
    deletePhoto: function () {
      this.user.deleteUserPhoto = 'noMorePhoto';

      var data = this.user;

      data[window.csrfTokenName] = window.csrfTokenValue; // Append CSRF Token

      this.$http.post('/', data)
        .then(function(response) {
          this.showImage = false;
        });
    }
  }
});