diff --git a/core/modules/rest/src/Plugin/views/display/RestExport.php b/core/modules/rest/src/Plugin/views/display/RestExport.php
index 1eba5a9..dc7604a 100644
--- a/core/modules/rest/src/Plugin/views/display/RestExport.php
+++ b/core/modules/rest/src/Plugin/views/display/RestExport.php
@@ -10,6 +10,7 @@
 use Drupal\Component\Utility\SafeMarkup;
 use Drupal\Core\State\StateInterface;
 use Drupal\Core\Routing\RouteProviderInterface;
+use Drupal\views\Plugin\views\display\DisplayPluginInterface;
 use Drupal\views\ViewExecutable;
 use Drupal\views\Plugin\views\display\PathPluginBase;
 use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -281,6 +282,19 @@ public function render() {
 
   /**
    * {@inheritdoc}
+   */
+  public function postSaveView(DisplayPluginInterface $original_display = NULL) {
+    parent::postSaveView($original_display);
+
+    $style_plugin = $this->getPlugin('style');
+    if (method_exists($style_plugin, 'postSaveView')) {
+      $style_plugin->postSaveView($original_display);
+    }
+  }
+
+
+  /**
+   * {@inheritdoc}
    *
    * The DisplayPluginBase preview method assumes we will be returning a render
    * array. The data plugin will already return the serialized string.
diff --git a/core/modules/rest/src/Plugin/views/style/Serializer.php b/core/modules/rest/src/Plugin/views/style/Serializer.php
index 09e94e6..4196669 100644
--- a/core/modules/rest/src/Plugin/views/style/Serializer.php
+++ b/core/modules/rest/src/Plugin/views/style/Serializer.php
@@ -8,9 +8,11 @@
 namespace Drupal\rest\Plugin\views\style;
 
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\views\Plugin\views\display\DisplayPluginInterface;
 use Drupal\views\ViewExecutable;
 use Drupal\views\Plugin\views\display\DisplayPluginBase;
 use Drupal\views\Plugin\views\style\StylePluginBase;
+use Drupal\views\Views;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\Serializer\SerializerInterface;
 
@@ -134,6 +136,27 @@ public function render() {
   }
 
   /**
+   * Act on saving a view.
+   *
+   * @param \Drupal\views\Plugin\views\display\DisplayPluginInterface $original_display
+   *   (optional) The original display.
+   */
+  public function postSaveView(DisplayPluginInterface $original_display = NULL) {
+    $original_style = $original_display ? $original_display->getOption('style') : [];
+    $original_style += ['options' => []];
+    $original_style['options'] += ['formats' => []];
+
+    $this->options += ['formats' => []];
+
+    $original_formats = $original_style['options']['formats'];
+    $new_formats = $this->options['formats'];
+
+    if ($original_formats != $new_formats) {
+      Views::routeRebuildNeeded();
+    }
+  }
+
+  /**
    * Gets a list of all available formats that can be requested.
    *
    * This will return the configured formats, or all formats if none have been
diff --git a/core/modules/views/src/Entity/View.php b/core/modules/views/src/Entity/View.php
index 34b4ab0..40b14c3 100644
--- a/core/modules/views/src/Entity/View.php
+++ b/core/modules/views/src/Entity/View.php
@@ -222,7 +222,13 @@ protected function generateDisplayId($plugin_id) {
    * {@inheritdoc}
    */
   public function &getDisplay($display_id) {
-    return $this->display[$display_id];
+    $empty_array = [];
+    if (isset($this->display[$display_id])) {
+      return $this->display[$display_id];
+    }
+    else {
+      return $empty_array;
+    }
   }
 
   /**
@@ -331,8 +337,25 @@ protected function addCacheMetadata() {
   public function postSave(EntityStorageInterface $storage, $update = TRUE) {
     parent::postSave($storage, $update);
 
-    // @todo Remove if views implements a view_builder controller.
-    views_invalidate_cache();
+    Views::invalidateCache();
+
+    $executable = $this->getExecutable();
+    /** @var \Drupal\views\ViewExecutable $original_executable */
+    $original_executable = NULL;
+    if (isset($this->original)) {
+      $original_executable = $this->original->getExecutable();
+      $original_executable->initDisplay();
+    }
+    foreach ($executable->displayHandlers as $display_id => $display_handler) {
+      // Note: The original might have not had the new display, so let's cast it
+      // to an array so postSaveView can deal with it
+      $original_display_handler = NULL;
+      if ($original_executable && $original_executable->displayHandlers->has($display_id)) {
+        $original_executable->setDisplay($display_id);
+        $original_display_handler = $original_executable->display_handler;
+      }
+      $display_handler->postSaveView($original_display_handler);
+    }
 
     // Rebuild the router if this is a new view, or it's status changed.
     if (!isset($this->original) || ($this->status() != $this->original->status())) {
diff --git a/core/modules/views/src/Plugin/views/display/Block.php b/core/modules/views/src/Plugin/views/display/Block.php
index 411c50e..d22e337 100644
--- a/core/modules/views/src/Plugin/views/display/Block.php
+++ b/core/modules/views/src/Plugin/views/display/Block.php
@@ -372,4 +372,17 @@ public function remove() {
     }
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function postSaveView(DisplayPluginInterface $original_display = NULL) {
+    parent::postSaveView($original_display);
+
+    // Invalidate the block cache to update views block derivatives.
+    if (\Drupal::moduleHandler()->moduleExists('block')) {
+      \Drupal::service('plugin.manager.block')->clearCachedDefinitions();
+    }
+  }
+
+
 }
diff --git a/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php b/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php
index 290d1a7..8928aa8 100644
--- a/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php
+++ b/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php
@@ -157,6 +157,7 @@ public function initDisplay(ViewExecutable $view, array &$display, array &$optio
     $this->view = $view;
 
     // Load extenders as soon as possible.
+    $display += ['display_options' => []];
     $display['display_options'] += ['display_extenders' => []];
     $this->extenders = array();
     if ($extenders = Views::getEnabledDisplayExtenders()) {
@@ -2627,6 +2628,13 @@ protected function isBaseTableTranslatable() {
     }
     return FALSE;
   }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function postSaveView(DisplayPluginInterface $original_display = NULL) {
+  }
+
 }
 
 /**
diff --git a/core/modules/views/src/Plugin/views/display/DisplayPluginInterface.php b/core/modules/views/src/Plugin/views/display/DisplayPluginInterface.php
index b82de15..010d96e 100644
--- a/core/modules/views/src/Plugin/views/display/DisplayPluginInterface.php
+++ b/core/modules/views/src/Plugin/views/display/DisplayPluginInterface.php
@@ -580,4 +580,12 @@ public function mergeDefaults();
    */
   public function getExtenders();
 
+  /**
+   * Act on saving a view.
+   *
+   * @param \Drupal\views\Plugin\views\display\DisplayPluginInterface $original_display
+   *   (optional) The original display.
+   */
+  public function postSaveView(DisplayPluginInterface $original_display = NULL);
+
 }
diff --git a/core/modules/views/src/Plugin/views/display/Page.php b/core/modules/views/src/Plugin/views/display/Page.php
index 27905b8..47bac2d 100644
--- a/core/modules/views/src/Plugin/views/display/Page.php
+++ b/core/modules/views/src/Plugin/views/display/Page.php
@@ -12,6 +12,7 @@
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\State\StateInterface;
 use Drupal\Core\Routing\RouteProviderInterface;
+use Drupal\views\Views;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
 use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
@@ -487,4 +488,30 @@ public function calculateDependencies() {
     return $dependencies;
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function postSaveView(DisplayPluginInterface $original_display = NULL) {
+    parent::postSaveView($original_display);
+
+    // An empty original display means a new view, we need a router rebuild.
+    $route_rebuild = FALSE;
+    if (!$original_display) {
+      $route_rebuild = TRUE;
+    }
+    else {
+      $determining_options = ['menu', 'tab_options'];
+      foreach ($determining_options as $determining_option) {
+        if ($this->getOption($determining_option) != $original_display->getOption($determining_option)) {
+          $route_rebuild = TRUE;
+        }
+      }
+    }
+
+
+    if ($route_rebuild) {
+      Views::routeRebuildNeeded();
+    }
+  }
+
 }
diff --git a/core/modules/views/src/Plugin/views/display/PathPluginBase.php b/core/modules/views/src/Plugin/views/display/PathPluginBase.php
index 415b704..87a0df5 100644
--- a/core/modules/views/src/Plugin/views/display/PathPluginBase.php
+++ b/core/modules/views/src/Plugin/views/display/PathPluginBase.php
@@ -512,4 +512,30 @@ public function getAlteredRouteNames() {
     return $this->state->get('views.view_route_names') ?: array();
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function postSaveView(DisplayPluginInterface $original_display = NULL) {
+    parent::postSaveView($original_display);
+
+    // An empty original display means a new view, we need a router rebuild.
+    $route_rebuild = FALSE;
+    if (!$original_display) {
+      $route_rebuild = TRUE;
+    }
+    else {
+      $determining_options = ['path', 'route_name', 'arguments', 'access', 'enabled'];
+      foreach ($determining_options as $determining_option) {
+        if ($this->getOption($determining_option) != $original_display->getOption($determining_option)) {
+          $route_rebuild = TRUE;
+        }
+      }
+    }
+
+    if ($route_rebuild) {
+      Views::routeRebuildNeeded();
+    }
+  }
+
+
 }
diff --git a/core/modules/views/src/Tests/Entity/BaseFieldAccessTest.php b/core/modules/views/src/Tests/Entity/BaseFieldAccessTest.php
index 6b12287..5abc661 100644
--- a/core/modules/views/src/Tests/Entity/BaseFieldAccessTest.php
+++ b/core/modules/views/src/Tests/Entity/BaseFieldAccessTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\views\Tests\Entity;
 
+use Drupal\Core\Cache\Cache;
 use Drupal\entity_test\Entity\EntityTest;
 use Drupal\views\Tests\ViewTestBase;
 use Drupal\views\Tests\ViewTestData;
@@ -44,15 +45,6 @@ protected function setUp() {
     \Drupal::entityManager()->clearCachedDefinitions();
     $update_manager->applyUpdates();
     ViewTestData::createTestViews(get_class($this), array('comment_test_views'));
-    \Drupal::state()->set('entity_test.views_data', [
-      'entity_test' => [
-        'test_text_access' => [
-          'field' => [
-            'id' => 'standard',
-          ],
-        ],
-      ],
-    ]);
     $entity_1 = EntityTest::create([
       'test_text_access' => 'no access value',
     ]);
diff --git a/core/modules/views/src/Tests/Plugin/AccessTest.php b/core/modules/views/src/Tests/Plugin/AccessTest.php
index 95ef502..10fdb44 100644
--- a/core/modules/views/src/Tests/Plugin/AccessTest.php
+++ b/core/modules/views/src/Tests/Plugin/AccessTest.php
@@ -88,16 +88,19 @@ function testStaticAccessPlugin() {
     $view = Views::getView('test_access_static');
     $view->setDisplay();
 
-    $access_plugin = $view->display_handler->getPlugin('access');
-
+    $access_plugin = $view->getDisplay()->getPlugin('access');
     $this->assertFalse($access_plugin->access($this->normalUser));
+    $view->destroy();
+
     $this->drupalGet('test_access_static');
     $this->assertResponse(403);
 
     $display = &$view->storage->getDisplay('default');
     $display['display_options']['access']['options']['access'] = TRUE;
     $access_plugin->options['access'] = TRUE;
+
     $view->save();
+
     // Saving a view will cause the router to be rebuilt when the kernel
     // termination event fires. Simulate that here.
     $this->container->get('router.builder')->rebuildIfNeeded();
@@ -110,6 +113,7 @@ function testStaticAccessPlugin() {
     //   page to be invalidated.
     Cache::invalidateTags(['rendered']);
 
+    $access_plugin = $view->getDisplay()->getPlugin('access');
     $this->assertTrue($access_plugin->access($this->normalUser));
 
     $this->drupalGet('test_access_static');
diff --git a/core/modules/views/src/Tests/Plugin/DisabledDisplayTest.php b/core/modules/views/src/Tests/Plugin/DisabledDisplayTest.php
index e9052b4..f89b923 100644
--- a/core/modules/views/src/Tests/Plugin/DisabledDisplayTest.php
+++ b/core/modules/views/src/Tests/Plugin/DisabledDisplayTest.php
@@ -68,10 +68,10 @@ public function testDisabledDisplays() {
     // Enable each disabled display and save the view.
     foreach ($display_ids as $display_id) {
       $view->getExecutable()->displayHandlers->get($display_id)->setOption('enabled', TRUE);
-      $view->save();
       $enabled = $view->getExecutable()->displayHandlers->get($display_id)->isEnabled();
       $this->assertTrue($enabled, 'Display ' . $display_id . ' is now enabled');
     }
+    $view->save();
 
     \Drupal::service('router.builder')->rebuildIfNeeded();
 
diff --git a/core/modules/views/src/Tests/Plugin/DisplayTest.php b/core/modules/views/src/Tests/Plugin/DisplayTest.php
index 81bb1c2..096f325 100644
--- a/core/modules/views/src/Tests/Plugin/DisplayTest.php
+++ b/core/modules/views/src/Tests/Plugin/DisplayTest.php
@@ -268,13 +268,7 @@ public function testInvalidDisplayPlugins() {
     $config->set('display.page_1.display_plugin', 'invalid');
     $config->save();
 
-    $this->drupalGet('test_display_invalid');
-    $this->assertResponse(200);
-    $this->assertText('The &quot;invalid&quot; plugin does not exist.');
-
-    // Rebuild the router, and ensure that the path is not accessible anymore.
-    views_invalidate_cache();
-    \Drupal::service('router.builder')->rebuildIfNeeded();
+    \Drupal::service('router.builder')->rebuild();
 
     $this->drupalGet('test_display_invalid');
     $this->assertResponse(404);
diff --git a/core/modules/views/src/ViewExecutable.php b/core/modules/views/src/ViewExecutable.php
index fe65fe3..deed59f 100644
--- a/core/modules/views/src/ViewExecutable.php
+++ b/core/modules/views/src/ViewExecutable.php
@@ -215,7 +215,7 @@ class ViewExecutable implements \Serializable {
    * An array containing Drupal\views\Plugin\views\display\DisplayPluginBase
    * objects.
    *
-   * @var \Drupal\views\DisplayPluginCollection
+   * @var \Drupal\views\DisplayPluginCollection|\Drupal\views\Plugin\views\display\DisplayPluginBase[]
    */
   public $displayHandlers;
 
diff --git a/core/modules/views/src/Views.php b/core/modules/views/src/Views.php
index 747f2f8..3c1d8d9 100644
--- a/core/modules/views/src/Views.php
+++ b/core/modules/views/src/Views.php
@@ -8,6 +8,7 @@
 namespace Drupal\views;
 
 use Drupal\Component\Utility\SafeMarkup;
+use Drupal\Core\Cache\Cache;
 
 /**
  * Static service container wrapper for views.
@@ -534,4 +535,25 @@ protected static function t($string, array $args = array(), array $options = arr
     return static::$translationManager->translate($string, $args, $options);
   }
 
+  /**
+   * Forces a router rebuild for views.
+   */
+  public static function routeRebuildNeeded() {
+    // Set the router as needed to be rebuilt.
+    \Drupal::service('router.builder')->setRebuildNeeded();
+
+    // Reset the RouteSubscriber from views.
+    \Drupal::service('views.route_subscriber')->reset();
+  }
+
+  /**
+   * Invalidate the views cache, forcing a rebuild on the next grab of table data.
+   */
+  public static function invalidateCache() {
+    \Drupal::service('cache_tags.invalidator')->invalidateTags(['views_data']);
+    $module_handler = \Drupal::moduleHandler();
+    // Allow modules to respond to the Views cache being cleared.
+    $module_handler->invokeAll('views_invalidate_cache');
+  }
+
 }
diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_disabled_display.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_disabled_display.yml
index c0c45c3..5075778 100644
--- a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_disabled_display.yml
+++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_disabled_display.yml
@@ -189,6 +189,7 @@ display:
     display_options:
       display_extenders: {  }
       path: test-disabled-display
+      enabled: true
   page_2:
     display_plugin: page
     id: page_2
diff --git a/core/modules/views/views.module b/core/modules/views/views.module
index 25c62f8..82fd7ba 100644
--- a/core/modules/views/views.module
+++ b/core/modules/views/views.module
@@ -483,21 +483,7 @@ function views_field_config_delete(FieldConfigInterface $field) {
  * Invalidate the views cache, forcing a rebuild on the next grab of table data.
  */
 function views_invalidate_cache() {
-  // Set the menu as needed to be rebuilt.
-  \Drupal::service('router.builder')->setRebuildNeeded();
-
-  $module_handler = \Drupal::moduleHandler();
-
-  // Reset the RouteSubscriber from views.
-  \Drupal::getContainer()->get('views.route_subscriber')->reset();
-
-  // Invalidate the block cache to update views block derivatives.
-  if ($module_handler->moduleExists('block')) {
-    \Drupal::service('plugin.manager.block')->clearCachedDefinitions();
-  }
-
-  // Allow modules to respond to the Views cache being cleared.
-  $module_handler->invokeAll('views_invalidate_cache');
+  Views::invalidateCache();
 }
 
 /**
