diff --git a/core/modules/views/includes/ajax.inc b/core/modules/views/includes/ajax.inc
index 73c3d0213f5139f2973b7794b2ef950d9a314679..6bf8869cbb987b4d929f2235067d3ab6bb7c7231 100644
--- a/core/modules/views/includes/ajax.inc
+++ b/core/modules/views/includes/ajax.inc
@@ -6,6 +6,12 @@
  */
 
 use Symfony\Component\HttpFoundation\JsonResponse;
+use Drupal\views\Ajax\HighlightCommand;
+use Drupal\views\Ajax\SetFormCommand;
+use Drupal\Core\Ajax\ReplaceCommand;
+use Drupal\views\Ajax\ScrollTopCommand;
+use Drupal\views\Ajax\ViewAjaxResponse;
+use Drupal\Core\Ajax\AjaxResponse;
 
 /**
  * @defgroup views_ajax Views AJAX library
@@ -29,7 +35,7 @@ function views_ajax() {
     $pager_element = $request->request->get('pager_element');
     $pager_element = isset($pager_element) ? intval($pager_element) : NULL;
 
-    $commands = array();
+    $response = new ViewAjaxResponse();
 
     // Remove all of this stuff from the query of the request so it doesn't end
     // up in pagers and tablesort URLs.
@@ -68,160 +74,18 @@ function views_ajax() {
 
       // Override the display's pager_element with the one actually used.
       if (isset($pager_element)) {
-        $commands[] = views_ajax_command_scroll_top('.view-dom-id-' . $dom_id);
+        $response->addCommand(new ScrollTopCommand(".view-dom-id-$dom_id"));
         $view->displayHandlers->get($display_id)->setOption('pager_element', $pager_element);
       }
       // Reuse the same DOM id so it matches that in Drupal.settings.
       $view->dom_id = $dom_id;
 
-      $commands[] = ajax_command_replace('.view-dom-id-' . $dom_id, $view->preview($display_id, $args));
+      $response->addCommand(new ReplaceCommand(".view-dom-id-$dom_id", $view->preview($display_id, $args)));
     }
-    drupal_alter('views_ajax_data', $commands, $view);
-    return array('#type' => 'ajax', '#commands' => $commands);
+    return $response;
   }
 }
 
-/**
- * Creates a Drupal AJAX 'viewsSetForm' command.
- *
- * @param $output
- *   The form to display in the modal.
- * @param $title
- *   The title.
- * @param $url
- *   An optional URL.
- *
- * @return
- *   An array suitable for use with the ajax_render() function.
- */
-function views_ajax_command_set_form($output, $title, $url = NULL) {
-  $command = array(
-    'command' => 'viewsSetForm',
-    'output' => $output,
-    'title' => $title,
-  );
-  if (isset($url)) {
-    $command['url'] = $url;
-  }
-  return $command;
-}
-
-/**
- * Creates a Drupal AJAX 'viewsDismissForm' command.
- *
- * @return
- *   An array suitable for use with the ajax_render() function.
- */
-function views_ajax_command_dismiss_form() {
-  $command = array(
-    'command' => 'viewsDismissForm',
-  );
-  return $command;
-}
-
-/**
- * Creates a Drupal AJAX 'viewsHilite' command.
- *
- * @param $selector
- *   The selector to highlight
- *
- * @return
- *   An array suitable for use with the ajax_render() function.
- */
-function views_ajax_command_hilite($selector) {
-  return array(
-    'command' => 'viewsHilite',
-    'selector' => $selector,
-  );
-}
-
-/**
- * Creates a Drupal AJAX 'addTab' command.
- *
- * @param $id
- *   The DOM ID.
- * @param $title
- *   The title.
- * @param $body
- *   The body.
- *
- * @return
- *   An array suitable for use with the ajax_render() function.
- */
-function views_ajax_command_add_tab($id, $title, $body) {
-  $command = array(
-    'command' => 'viewsAddTab',
-    'id' => $id,
-    'title' => $title,
-    'body' => $body,
-  );
-  return $command;
-}
-
-/**
- * Scroll to top of the current view.
- *
- * @return
- *   An array suitable for use with the ajax_render() function.
- */
-function views_ajax_command_scroll_top($selector) {
-  $command = array(
-    'command' => 'viewsScrollTop',
-    'selector' => $selector,
-  );
-  return $command;
-}
-
-/**
- * Shows Save and Cancel buttons.
- *
- * @return
- *   An array suitable for use with the ajax_render() function.
- */
-function views_ajax_command_show_buttons() {
-  $command = array(
-    'command' => 'viewsShowButtons',
-  );
-  return $command;
-}
-
-/**
- * Trigger the Views live preview.
- *
- * @return
- *   An array suitable for use with the ajax_render() function.
- */
-function views_ajax_command_trigger_preview() {
-  $command = array(
-    'command' => 'viewsTriggerPreview',
-  );
-  return $command;
-}
-
-/**
- * Replace the page title.
- *
- * @return
- *   An array suitable for use with the ajax_render() function.
- */
-function views_ajax_command_replace_title($title) {
-  $command = array(
-    'command' => 'viewsReplaceTitle',
-    'title' => $title,
-    'siteName' => config('system.site')->get('name'),
-  );
-  return $command;
-}
-
-/**
- * Return an AJAX error.
- */
-function views_ajax_error($message) {
-  $commands = array();
-  $commands[] = views_ajax_command_set_form($message, t('Error'));
-  return $commands;
-}
-
 /**
  * Wrapper around drupal_build_form to handle some AJAX stuff automatically.
  * This makes some assumptions about the client.
@@ -249,7 +113,7 @@ function views_ajax_form_wrapper($form_id, &$form_state) {
   if (!empty($form_state['ajax']) && (empty($form_state['executed']) || !empty($form_state['rerender']))) {
     // If the form didn't execute and we're using ajax, build up a
     // Ajax command list to execute.
-    $commands = array();
+    $response = new AjaxResponse();
 
     $display = '';
     if ($messages = theme('status_messages')) {
@@ -261,13 +125,13 @@ function views_ajax_form_wrapper($form_id, &$form_state) {
 
     $url = empty($form_state['url']) ? url(current_path(), array('absolute' => TRUE)) : $form_state['url'];
 
-    $commands[] = views_ajax_command_set_form($display, $title, $url);
+    $response->addCommand(new SetFormCommand($display, $title, $url));
 
     if (!empty($form_state['#section'])) {
-      $commands[] = views_ajax_command_hilite('.' . drupal_clean_css_identifier($form_state['#section']));
+      $response->addCommand(new HighlightCommand('.' . drupal_clean_css_identifier($form_state['#section'])));
     }
 
-    return $commands;
+    return $response;
   }
 
   // These forms have the title built in, so set the title here:
diff --git a/core/modules/views/lib/Drupal/views/Ajax/DismissFormCommand.php b/core/modules/views/lib/Drupal/views/Ajax/DismissFormCommand.php
new file mode 100644
index 0000000000000000000000000000000000000000..7dfa90fb356d4ee7e6aabf608bc1bac369fc8419
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Ajax/DismissFormCommand.php
@@ -0,0 +1,28 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\views\Ajax\DismissFormCommand.
+ */
+
+namespace Drupal\views\Ajax;
+
+use Drupal\Core\Ajax\CommandInterface;
+
+/**
+ * Provides an AJAX command for closing the views form modal.
+ *
+ * This command is implemented in Drupal.ajax.prototype.commands.viewsDismissForm.
+ */
+class DismissFormCommand implements CommandInterface {
+
+  /**
+   * Implements \Drupal\Core\Ajax\CommandInterface::render().
+   */
+  public function render() {
+    return array(
+      'command' => 'viewsDismissForm',
+    );
+  }
+
+}
diff --git a/core/modules/views/lib/Drupal/views/Ajax/HighlightCommand.php b/core/modules/views/lib/Drupal/views/Ajax/HighlightCommand.php
new file mode 100644
index 0000000000000000000000000000000000000000..91729022b1772d18a9c3d997fc1a30a95c35d1df
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Ajax/HighlightCommand.php
@@ -0,0 +1,46 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\views\Ajax\HighlightCommand.
+ */
+
+namespace Drupal\views\Ajax;
+
+use Drupal\Core\Ajax\CommandInterface;
+
+/**
+ * Provides an AJAX command for highlighting a certain new piece of html.
+ *
+ * This command is implemented in Drupal.ajax.prototype.commands.viewsHighlight.
+ */
+class HighlightCommand implements CommandInterface {
+
+  /**
+   * A CSS selector string.
+   *
+   * @var string
+   */
+  protected $selector;
+
+  /**
+   * Constructs a \Drupal\views\Ajax\HighlightCommand object.
+   *
+   * @param string $selector
+   *   A CSS selector.
+   */
+  public function __construct($selector) {
+    $this->selector = $selector;
+  }
+
+  /**
+   * Implements \Drupal\Core\Ajax\CommandInterface::render().
+   */
+  public function render() {
+    return array(
+      'command' => 'viewsHighlight',
+      'selector' => $this->selector,
+    );
+  }
+
+}
diff --git a/core/modules/views/lib/Drupal/views/Ajax/ReplaceTitleCommand.php b/core/modules/views/lib/Drupal/views/Ajax/ReplaceTitleCommand.php
new file mode 100644
index 0000000000000000000000000000000000000000..2d79e97387500149bb0a6c726d4aff0f56fc0a79
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Ajax/ReplaceTitleCommand.php
@@ -0,0 +1,46 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\views\Ajax\ReplaceTitleCommand.
+ */
+
+namespace Drupal\views\Ajax;
+
+use Drupal\Core\Ajax\CommandInterface;
+
+/**
+ * Provides an AJAX command for replacing the page title.
+ *
+ * This command is implemented in Drupal.ajax.prototype.commands.viewsReplaceTitle.
+ */
+class ReplaceTitleCommand implements CommandInterface {
+
+  /**
+   * The page title to replace.
+   *
+   * @var string
+   */
+  protected $title;
+
+  /**
+   * Constructs a \Drupal\views\Ajax\ReplaceTitleCommand object.
+   *
+   * @param string $title
+   *   The title of the page.
+   */
+  public function __construct($title) {
+    $this->title = $title;
+  }
+
+  /**
+   * Implements \Drupal\Core\Ajax\CommandInterface::render().
+   */
+  public function render() {
+    return array(
+      'command' => 'viewsReplaceTitle',
+      'selector' => $this->title,
+    );
+  }
+
+}
diff --git a/core/modules/views/lib/Drupal/views/Ajax/ScrollTopCommand.php b/core/modules/views/lib/Drupal/views/Ajax/ScrollTopCommand.php
new file mode 100644
index 0000000000000000000000000000000000000000..6b9eb21f2e2bf4ca595dda54a208385ab11adae3
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Ajax/ScrollTopCommand.php
@@ -0,0 +1,46 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\views\Ajax\ScrollTopCommand.
+ */
+
+namespace Drupal\views\Ajax;
+
+use Drupal\Core\Ajax\CommandInterface;
+
+/**
+ * Provides an AJAX command for scolling to the top of an element.
+ *
+ * This command is implemented in Drupal.ajax.prototype.commands.viewsScrollTop.
+ */
+class ScrollTopCommand implements CommandInterface {
+
+  /**
+   * A CSS selector string.
+   *
+   * @var string
+   */
+  protected $selector;
+
+  /**
+   * Constructs a \Drupal\views\Ajax\ScrollTopCommand object.
+   *
+   * @param string $selector
+   *   A CSS selector.
+   */
+  public function __construct($selector) {
+    $this->selector = $selector;
+  }
+
+  /**
+   * Implements \Drupal\Core\Ajax\CommandInterface::render().
+   */
+  public function render() {
+    return array(
+      'command' => 'viewsScrollTop',
+      'selector' => $this->selector,
+    );
+  }
+
+}
diff --git a/core/modules/views/lib/Drupal/views/Ajax/SetFormCommand.php b/core/modules/views/lib/Drupal/views/Ajax/SetFormCommand.php
new file mode 100644
index 0000000000000000000000000000000000000000..b4f629738cb2d86ea8754e2e6e105b792f3ebcb5
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Ajax/SetFormCommand.php
@@ -0,0 +1,71 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\views\Ajax\SetFormCommand.
+ */
+
+namespace Drupal\views\Ajax;
+
+use Drupal\Core\Ajax\CommandInterface;
+
+/**
+ * Provides an AJAX command for setting a form in the views edit modal.
+ *
+ * This command is implemented in Drupal.ajax.prototype.commands.viewsSetForm.
+ */
+class SetFormCommand implements CommandInterface {
+
+  /**
+   * The rendered output of the form.
+   *
+   * @var string
+   */
+  protected $output;
+
+  /**
+   * The title of the form.
+   *
+   * @var string
+   */
+  protected $title;
+
+  /**
+   * The URL of the form.
+   *
+   * @var string
+   */
+  protected $url;
+
+  /**
+   * Constructs a \Drupal\views\Ajax\ReplaceTitleCommand object.
+   *
+   * @param string $output
+   *   The form to display in the modal.
+   * @param string $title
+   *   The title of the form.
+   * @param string $url
+   *   (optional) An optional URL of the form.
+   */
+  public function __construct($output, $title, $url = NULL) {
+    $this->output = $output;
+    $this->title = $title;
+    $this->url = $url;
+  }
+
+  /**
+   * Implements \Drupal\Core\Ajax\CommandInterface::render().
+   */
+  public function render() {
+    $command = array(
+      'command' => 'viewsSetForm',
+      'output' => $this->output,
+      'title' => $this->title,
+    );
+    if (isset($this->url)) {
+      $command['url'] = $this->url;
+    }
+    return $command;
+  }
+
+}
diff --git a/core/modules/views/lib/Drupal/views/Ajax/ShowButtonsCommand.php b/core/modules/views/lib/Drupal/views/Ajax/ShowButtonsCommand.php
new file mode 100644
index 0000000000000000000000000000000000000000..2543fad17bbf4d72df582b95da7f4079e102ca95
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Ajax/ShowButtonsCommand.php
@@ -0,0 +1,28 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\views\Ajax\ShowButtonsCommand.
+ */
+
+namespace Drupal\views\Ajax;
+
+use Drupal\Core\Ajax\CommandInterface;
+
+/**
+ * Provides an AJAX command for showing the save and cancel buttons.
+ *
+ * This command is implemented in Drupal.ajax.prototype.commands.viewsShowButtons.
+ */
+class ShowButtonsCommand implements CommandInterface {
+
+  /**
+   * Implements \Drupal\Core\Ajax\CommandInterface::render().
+   */
+  public function render() {
+    return array(
+      'command' => 'viewsShowButtons',
+    );
+  }
+
+}
diff --git a/core/modules/views/lib/Drupal/views/Ajax/TriggerPreviewCommand.php b/core/modules/views/lib/Drupal/views/Ajax/TriggerPreviewCommand.php
new file mode 100644
index 0000000000000000000000000000000000000000..345017d8be52154e9cce01e8d7c8eeccb33ed9a0
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Ajax/TriggerPreviewCommand.php
@@ -0,0 +1,28 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\views\Ajax\TriggerPreviewCommand.
+ */
+
+namespace Drupal\views\Ajax;
+
+use Drupal\Core\Ajax\CommandInterface;
+
+/**
+ * Provides an AJAX command for triggering the views live preview.
+ *
+ * This command is implemented in Drupal.ajax.prototype.commands.viewsTriggerPreview.
+ */
+class TriggerPreviewCommand implements CommandInterface {
+
+  /**
+   * Implements \Drupal\Core\Ajax\CommandInterface::render().
+   */
+  public function render() {
+    return array(
+      'command' => 'viewsTriggerPreview',
+    );
+  }
+
+}
diff --git a/core/modules/views/lib/Drupal/views/Ajax/ViewAjaxResponse.php b/core/modules/views/lib/Drupal/views/Ajax/ViewAjaxResponse.php
new file mode 100644
index 0000000000000000000000000000000000000000..3ac8900635f7c0c6d4c2c0ce65e0948b79278461
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Ajax/ViewAjaxResponse.php
@@ -0,0 +1,47 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\views\Ajax\ViewAjaxResponse.
+ */
+
+namespace Drupal\views\Ajax;
+
+use Drupal\Core\Ajax\AjaxResponse;
+use Drupal\views\ViewExecutable;
+
+/**
+ * Custom JSON response object for an ajax view response.
+ *
+ * We use a special response object to be able to fire a proper alter hook.
+ */
+class ViewAjaxResponse extends AjaxResponse {
+
+  /**
+   * The view executed on this ajax request.
+   *
+   * @var \Drupal\views\ViewExecutable
+   */
+  protected $view;
+
+  /**
+   * Sets the executed view of this response.
+   *
+   * @param \Drupal\views\ViewExecutable $view
+   *   The View executed on this ajax request.
+   */
+  public function setView(ViewExecutable $view) {
+    $this->view = $view;
+  }
+
+  /**
+   * Gets the executed view of this response.
+   *
+   * @return \Drupal\views\ViewExecutable $view
+   *   The View executed on this ajax request.
+   */
+  public function getView() {
+    return $this->view;
+  }
+
+}
diff --git a/core/modules/views/views.api.php b/core/modules/views/views.api.php
index 0687b9db215d34b0ef6dcc13c8e1119905808137..9a7abc5631a2c52acaecd44e50f44f8efb72acd3 100644
--- a/core/modules/views/views.api.php
+++ b/core/modules/views/views.api.php
@@ -594,25 +594,7 @@ function hook_views_ui_display_top_links_alter(array &$links, ViewExecutable $vi
   }
 }
 
-/**
- * Alter the commands used on a Views AJAX request.
- *
- * @param array $commands
- *   An array of ajax commands.
- * @param \Drupal\views\ViewExecutable $view
- *   The view which is requested.
- *
- * @see views_ajax()
- */
-function hook_views_ajax_data_alter(array &$commands, ViewExecutable $view) {
-  // Replace Views' method for scrolling to the top of the element with your
-  // custom scrolling method.
-  foreach ($commands as &$command) {
-    if ($command['method'] == 'viewsScrollTop') {
-      $command['method'] .= 'myScrollTop';
-    }
-  }
-}
+// @todo Describe how to alter a view ajax response with event listeners.
 
 /**
  * Allow modules to respond to the invalidation of the Views cache.
diff --git a/core/modules/views/views_ui/admin.inc b/core/modules/views/views_ui/admin.inc
index d769edceef2da6f29350d7764895a4d7ab0e52b6..fd97af4befa008fc8a2181de9df3c5aeadf97bfa 100644
--- a/core/modules/views/views_ui/admin.inc
+++ b/core/modules/views/views_ui/admin.inc
@@ -7,6 +7,11 @@
 
 use Drupal\Component\Utility\NestedArray;
 use Drupal\Core\Database\Database;
+use Drupal\views\Ajax\ReplaceTitleCommand;
+use Drupal\views\Ajax\TriggerPreviewCommand;
+use Drupal\views\Ajax\ShowButtonsCommand;
+use Drupal\views\Ajax\DismissFormCommand;
+use Drupal\Core\Ajax\AjaxResponse;
 use Symfony\Component\HttpFoundation\JsonResponse;
 use Drupal\views_ui\ViewUI;
 use Drupal\views_ui\ViewFormControllerBase;
@@ -603,7 +608,7 @@ function views_ui_ajax_form($js, $key, ViewUI $view, $display_id = '') {
   // reset the drupal_add_js() static before rendering the second time.
   $drupal_add_js_original = drupal_add_js();
   $drupal_add_js = &drupal_static('drupal_add_js');
-  $output = views_ajax_form_wrapper($form_state['form_id'], $form_state);
+  $response = views_ajax_form_wrapper($form_state['form_id'], $form_state);
   if ($form_state['submitted'] && empty($form_state['rerender'])) {
     // Sometimes we need to re-generate the form for multi-step type operations.
     $object = NULL;
@@ -618,19 +623,19 @@ function views_ui_ajax_form($js, $key, ViewUI $view, $display_id = '') {
       if (!$js) {
         return drupal_goto(views_ui_build_form_url($form_state));
       }
-      $output = views_ajax_form_wrapper($form_state['form_id'], $form_state);
+      $response = views_ajax_form_wrapper($form_state['form_id'], $form_state);
     }
     elseif (!$js) {
       // if nothing on the stack, non-js forms just go back to the main view editor.
       return drupal_goto("admin/structure/views/view/{$view->id()}/edit");
     }
     else {
-      $output = array();
-      $output[] = views_ajax_command_dismiss_form();
-      $output[] = views_ajax_command_show_buttons();
-      $output[] = views_ajax_command_trigger_preview();
+      $response = new AjaxResponse();
+      $response->addCommand(new DismissFormCommand());
+      $response->addCommand(new ShowButtonsCommand());
+      $response->addCommand(new TriggerPreviewCommand());
       if (!empty($form_state['#page_title'])) {
-        $output[] = views_ajax_command_replace_title($form_state['#page_title']);
+        $response->addCommand(new ReplaceTitleCommand($form_state['#page_title']));
       }
     }
     // If this form was for view-wide changes, there's no need to regenerate
@@ -638,11 +643,11 @@ function views_ui_ajax_form($js, $key, ViewUI $view, $display_id = '') {
     if ($display_id !== '') {
       drupal_container()->get('plugin.manager.entity')
         ->getFormController('view', 'edit')
-        ->rebuildCurrentTab($view, $output, $display_id);
+        ->rebuildCurrentTab($view, $response, $display_id);
     }
   }
 
-  return $js ? array('#type' => 'ajax', '#commands' => $output) : $output;
+  return $response;
 }
 
 /**
diff --git a/core/modules/views/views_ui/js/ajax.js b/core/modules/views/views_ui/js/ajax.js
index 65ebfa42a2e922b68f3e3d30fdc01e27968e00cc..8595dc80d47ba11e5004a65aef86ec622576df40 100644
--- a/core/modules/views/views_ui/js/ajax.js
+++ b/core/modules/views/views_ui/js/ajax.js
@@ -42,25 +42,11 @@
     $(Drupal.settings.views.ajax.popup).dialog('close');
   };
 
-  Drupal.ajax.prototype.commands.viewsHilite = function (ajax, response, status) {
+  Drupal.ajax.prototype.commands.viewsHighlight = function (ajax, response, status) {
     $('.hilited').removeClass('hilited');
     $(response.selector).addClass('hilited');
   };
 
-  Drupal.ajax.prototype.commands.viewsAddTab = function (ajax, response, status) {
-    var id = '#views-tab-' + response.id;
-    $('#views-tabset').viewsAddTab(id, response.title, 0);
-    $(id).html(response.body).addClass('views-tab');
-
-    // Update the preview widget to preview the new tab.
-    var display_id = id.replace('#views-tab-', '');
-    $("#preview-display-id").append('<option selected="selected" value="' + display_id + '">' + response.title + '</option>');
-
-    Drupal.attachBehaviors(id);
-    var instance = $.viewsUi.tabs.instances[$('#views-tabset').get(0).UI_TABS_UUID];
-    $('#views-tabset').viewsClickTab(instance.$tabs.length);
-  };
-
   Drupal.ajax.prototype.commands.viewsShowButtons = function (ajax, response, status) {
     $('div.views-edit-view div.form-actions').removeClass('js-hide');
     $('div.views-edit-view div.view-changed.messages').removeClass('js-hide');
diff --git a/core/modules/views/views_ui/lib/Drupal/views_ui/ViewEditFormController.php b/core/modules/views/views_ui/lib/Drupal/views_ui/ViewEditFormController.php
index a43d3ce8baca7c15f42b828de226f1cf3771879b..aef675ab9dffb155e04b0ba6f20f7a95c32375e0 100644
--- a/core/modules/views/views_ui/lib/Drupal/views_ui/ViewEditFormController.php
+++ b/core/modules/views/views_ui/lib/Drupal/views_ui/ViewEditFormController.php
@@ -7,6 +7,9 @@
 
 namespace Drupal\views_ui;
 
+use Drupal\Core\Ajax\AjaxResponse;
+use Drupal\Core\Ajax\HtmlCommand;
+use Drupal\Core\Ajax\ReplaceCommand;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Component\Utility\NestedArray;
 use Drupal\views\ViewExecutable;
@@ -607,8 +610,15 @@ public function submitDisplayDelete($form, &$form_state) {
 
   /**
    * Regenerate the current tab for AJAX updates.
+   *
+   * @param \Drupal\views_ui\ViewUI $view
+   *   The view to regenerate its tab.
+   * @param \Drupal\Core\Ajax\AjaxResponse $response
+   *   The response object to add new commands to.
+   * @param string $display_id
+   *   The display ID of the tab to regenerate.
    */
-  public function rebuildCurrentTab(ViewUI $view, &$output, $display_id) {
+  public function rebuildCurrentTab(ViewUI $view, AjaxResponse $response, $display_id) {
     $view->displayID = $display_id;
     if (!$view->get('executable')->setDisplay('default')) {
       return;
@@ -617,12 +627,12 @@ public function rebuildCurrentTab(ViewUI $view, &$output, $display_id) {
     // Regenerate the main display area.
     $build = $this->getDisplayTab($view);
     static::addMicroweights($build);
-    $output[] = ajax_command_html('#views-tab-' . $display_id, drupal_render($build));
+    $response->addCommand(new HtmlCommand('#views-tab-' . $display_id, drupal_render($build)));
 
     // Regenerate the top area so changes to display names and order will appear.
     $build = $this->renderDisplayTop($view);
     static::addMicroweights($build);
-    $output[] = ajax_command_replace('#views-display-top', drupal_render($build));
+    $response->addCommand(new ReplaceCommand('#views-display-top', drupal_render($build)));
   }
 
   /**
diff --git a/core/modules/views/views_ui/views_ui.module b/core/modules/views/views_ui/views_ui.module
index e328f35c31bfa923e6aa6982e9d698cc3832c07d..5d271c9a2c72c09c138c0ebd087b953914ec5859 100644
--- a/core/modules/views/views_ui/views_ui.module
+++ b/core/modules/views/views_ui/views_ui.module
@@ -10,7 +10,8 @@
 use Drupal\views_ui\ViewUI;
 use Drupal\views\Analyzer;
 use Drupal\Core\Entity\EntityInterface;
-use Symfony\Component\HttpFoundation\JsonResponse;
+use Drupal\Core\Ajax\AjaxResponse;
+use Drupal\Core\Ajax\ReplaceCommand;
 
 /**
  * Implements hook_menu().
@@ -696,8 +697,9 @@ function views_ui_ajax_callback(ViewExecutable $view, $op) {
   // If the request is via AJAX, return the rendered list as JSON.
   if (drupal_container()->get('request')->request->get('js')) {
     $list = views_ui_list_page();
-    $commands = array(ajax_command_replace('#views-entity-list', drupal_render($list)));
-    return new JsonResponse(ajax_render($commands));
+    $response = new AjaxResponse();
+    $response->addCommand(new ReplaceCommand('#views-entity-list', drupal_render($list)));
+    return $response;
   }
   // Otherwise, redirect back to the page.
   else {