diff --git a/core/modules/aggregator/config/optional/views.view.aggregator_sources.yml b/core/modules/aggregator/config/optional/views.view.aggregator_sources.yml
index cccd45b..c4d6a15 100644
--- a/core/modules/aggregator/config/optional/views.view.aggregator_sources.yml
+++ b/core/modules/aggregator/config/optional/views.view.aggregator_sources.yml
@@ -141,7 +141,7 @@ display:
       contexts:
         - 'languages:language_content'
         - 'languages:language_interface'
-      cacheable: false
+      max_age: 0
   feed_1:
     display_plugin: feed
     id: feed_1
@@ -398,7 +398,7 @@ display:
       contexts:
         - 'languages:language_content'
         - 'languages:language_interface'
-      cacheable: false
+      max_age: 0
   page_1:
     display_plugin: page
     id: page_1
@@ -418,4 +418,4 @@ display:
       contexts:
         - 'languages:language_content'
         - 'languages:language_interface'
-      cacheable: false
+      max_age: 0
diff --git a/core/modules/block_content/config/optional/views.view.block_content.yml b/core/modules/block_content/config/optional/views.view.block_content.yml
index ad5e1f9..5409102 100644
--- a/core/modules/block_content/config/optional/views.view.block_content.yml
+++ b/core/modules/block_content/config/optional/views.view.block_content.yml
@@ -464,7 +464,7 @@ display:
         - 'languages:language_content'
         - 'languages:language_interface'
         - url
-      cacheable: false
+      max_age: 0
   page_1:
     display_plugin: page
     id: page_1
@@ -486,4 +486,4 @@ display:
         - 'languages:language_content'
         - 'languages:language_interface'
         - url
-      cacheable: false
+      max_age: 0
diff --git a/core/modules/comment/tests/modules/comment_test_views/test_views/views.view.test_comment_field_name.yml b/core/modules/comment/tests/modules/comment_test_views/test_views/views.view.test_comment_field_name.yml
index 9938a65..57ba803 100644
--- a/core/modules/comment/tests/modules/comment_test_views/test_views/views.view.test_comment_field_name.yml
+++ b/core/modules/comment/tests/modules/comment_test_views/test_views/views.view.test_comment_field_name.yml
@@ -200,4 +200,4 @@ display:
       contexts:
         - languages
         - user
-      cacheable: false
+      max_age: 0
diff --git a/core/modules/contact/tests/modules/contact_test_views/test_views/views.view.test_contact_link.yml b/core/modules/contact/tests/modules/contact_test_views/test_views/views.view.test_contact_link.yml
index 84b2a2b..9ba9e55 100644
--- a/core/modules/contact/tests/modules/contact_test_views/test_views/views.view.test_contact_link.yml
+++ b/core/modules/contact/tests/modules/contact_test_views/test_views/views.view.test_contact_link.yml
@@ -133,7 +133,7 @@ display:
       contexts:
         - 'languages:language_content'
         - 'languages:language_interface'
-      cacheable: false
+      max_age: 0
   page_1:
     display_plugin: page
     id: page_1
@@ -146,4 +146,4 @@ display:
       contexts:
         - 'languages:language_content'
         - 'languages:language_interface'
-      cacheable: false
+      max_age: 0
diff --git a/core/modules/entity_reference/tests/modules/entity_reference_test_views/test_views/views.view.test_entity_reference_entity_test_mul_view.yml b/core/modules/entity_reference/tests/modules/entity_reference_test_views/test_views/views.view.test_entity_reference_entity_test_mul_view.yml
index 489ea04..e95103a 100644
--- a/core/modules/entity_reference/tests/modules/entity_reference_test_views/test_views/views.view.test_entity_reference_entity_test_mul_view.yml
+++ b/core/modules/entity_reference/tests/modules/entity_reference_test_views/test_views/views.view.test_entity_reference_entity_test_mul_view.yml
@@ -117,4 +117,4 @@ display:
       contexts:
         - languages
         - 'languages:language_interface'
-      cacheable: false
+      max_age: 0
diff --git a/core/modules/entity_reference/tests/modules/entity_reference_test_views/test_views/views.view.test_entity_reference_entity_test_view.yml b/core/modules/entity_reference/tests/modules/entity_reference_test_views/test_views/views.view.test_entity_reference_entity_test_view.yml
index 291b474..d190788 100644
--- a/core/modules/entity_reference/tests/modules/entity_reference_test_views/test_views/views.view.test_entity_reference_entity_test_view.yml
+++ b/core/modules/entity_reference/tests/modules/entity_reference_test_views/test_views/views.view.test_entity_reference_entity_test_view.yml
@@ -118,4 +118,4 @@ display:
         - entity_test_view_grants
         - languages
         - 'languages:language_interface'
-      cacheable: false
+      max_age: 0
diff --git a/core/modules/entity_reference/tests/modules/entity_reference_test_views/test_views/views.view.test_entity_reference_reverse_entity_test_mul_view.yml b/core/modules/entity_reference/tests/modules/entity_reference_test_views/test_views/views.view.test_entity_reference_reverse_entity_test_mul_view.yml
index 33911a6..ff48483 100644
--- a/core/modules/entity_reference/tests/modules/entity_reference_test_views/test_views/views.view.test_entity_reference_reverse_entity_test_mul_view.yml
+++ b/core/modules/entity_reference/tests/modules/entity_reference_test_views/test_views/views.view.test_entity_reference_reverse_entity_test_mul_view.yml
@@ -127,4 +127,4 @@ display:
         - entity_test_view_grants
         - languages
         - 'languages:language_interface'
-      cacheable: false
+      max_age: 0
diff --git a/core/modules/entity_reference/tests/modules/entity_reference_test_views/test_views/views.view.test_entity_reference_reverse_entity_test_view.yml b/core/modules/entity_reference/tests/modules/entity_reference_test_views/test_views/views.view.test_entity_reference_reverse_entity_test_view.yml
index c281ef5c..6cb60b6 100644
--- a/core/modules/entity_reference/tests/modules/entity_reference_test_views/test_views/views.view.test_entity_reference_reverse_entity_test_view.yml
+++ b/core/modules/entity_reference/tests/modules/entity_reference_test_views/test_views/views.view.test_entity_reference_reverse_entity_test_view.yml
@@ -126,4 +126,4 @@ display:
       contexts:
         - languages
         - 'languages:language_interface'
-      cacheable: false
+      max_age: 0
diff --git a/core/modules/file/config/optional/views.view.files.yml b/core/modules/file/config/optional/views.view.files.yml
index 5e727fb..f9a521c 100644
--- a/core/modules/file/config/optional/views.view.files.yml
+++ b/core/modules/file/config/optional/views.view.files.yml
@@ -716,7 +716,7 @@ display:
       contexts:
         - languages
         - url
-      cacheable: false
+      max_age: 0
   page_1:
     display_plugin: page
     id: page_1
@@ -749,7 +749,7 @@ display:
       contexts:
         - languages
         - url
-      cacheable: false
+      max_age: 0
   page_2:
     display_plugin: page
     id: page_2
@@ -1103,4 +1103,4 @@ display:
       contexts:
         - languages
         - url
-      cacheable: false
+      max_age: 0
diff --git a/core/modules/history/src/Plugin/views/filter/HistoryUserTimestamp.php b/core/modules/history/src/Plugin/views/filter/HistoryUserTimestamp.php
index 4117ec0..2216927 100644
--- a/core/modules/history/src/Plugin/views/filter/HistoryUserTimestamp.php
+++ b/core/modules/history/src/Plugin/views/filter/HistoryUserTimestamp.php
@@ -102,9 +102,9 @@ public function adminSummary() {
   /**
    * {@inheritdoc}
    */
-  public function isCacheable() {
+  public function getCacheMaxAge() {
     // This filter depends on the current time and therefore is never cacheable.
-    return FALSE;
+    return 0;
   }
 
 }
diff --git a/core/modules/node/config/optional/views.view.content.yml b/core/modules/node/config/optional/views.view.content.yml
index c8201f2..4214f79 100644
--- a/core/modules/node/config/optional/views.view.content.yml
+++ b/core/modules/node/config/optional/views.view.content.yml
@@ -563,7 +563,7 @@ display:
         - url
         - user
         - 'user.node_grants:view'
-      cacheable: false
+      max_age: 0
   page_1:
     display_options:
       path: admin/content/node
@@ -591,4 +591,4 @@ display:
         - url
         - user
         - 'user.node_grants:view'
-      cacheable: false
+      max_age: 0
diff --git a/core/modules/node/src/Plugin/views/argument_default/Node.php b/core/modules/node/src/Plugin/views/argument_default/Node.php
index 556b9aa..afd66da 100644
--- a/core/modules/node/src/Plugin/views/argument_default/Node.php
+++ b/core/modules/node/src/Plugin/views/argument_default/Node.php
@@ -7,8 +7,9 @@
 
 namespace Drupal\node\Plugin\views\argument_default;
 
+use Drupal\Core\Cache\Cache;
+use Drupal\Core\Cache\CacheableDependencyInterface;
 use Drupal\Core\Routing\RouteMatchInterface;
-use Drupal\views\Plugin\CacheablePluginInterface;
 use Drupal\views\Plugin\views\argument_default\ArgumentDefaultPluginBase;
 use Drupal\node\NodeInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -23,7 +24,7 @@
  *   title = @Translation("Content ID from URL")
  * )
  */
-class Node extends ArgumentDefaultPluginBase implements CacheablePluginInterface {
+class Node extends ArgumentDefaultPluginBase implements CacheableDependencyInterface {
 
   /**
    * The route match.
@@ -75,8 +76,8 @@ public function getArgument() {
   /**
    * {@inheritdoc}
    */
-  public function isCacheable() {
-    return TRUE;
+  public function getCacheMaxAge() {
+    return Cache::PERMANENT;
   }
 
   /**
@@ -86,4 +87,11 @@ public function getCacheContexts() {
     return ['url'];
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function getCacheTags() {
+    return [];
+  }
+
 }
diff --git a/core/modules/rest/src/Plugin/views/style/Serializer.php b/core/modules/rest/src/Plugin/views/style/Serializer.php
index 67a8364..ceccd61 100644
--- a/core/modules/rest/src/Plugin/views/style/Serializer.php
+++ b/core/modules/rest/src/Plugin/views/style/Serializer.php
@@ -7,8 +7,9 @@
 
 namespace Drupal\rest\Plugin\views\style;
 
+use Drupal\Core\Cache\Cache;
+use Drupal\Core\Cache\CacheableDependencyInterface;
 use Drupal\Core\Form\FormStateInterface;
-use Drupal\views\Plugin\CacheablePluginInterface;
 use Drupal\views\ViewExecutable;
 use Drupal\views\Plugin\views\display\DisplayPluginBase;
 use Drupal\views\Plugin\views\style\StylePluginBase;
@@ -27,7 +28,7 @@
  *   display_types = {"data"}
  * )
  */
-class Serializer extends StylePluginBase implements CacheablePluginInterface {
+class Serializer extends StylePluginBase implements CacheableDependencyInterface {
 
   /**
    * Overrides \Drupal\views\Plugin\views\style\StylePluginBase::$usesRowPlugin.
@@ -153,8 +154,8 @@ public function getFormats() {
   /**
    * {@inheritdoc}
    */
-  public function isCacheable() {
-    return TRUE;
+  public function getCacheMaxAge() {
+    return Cache::PERMANENT;
   }
 
   /**
@@ -164,4 +165,11 @@ public function getCacheContexts() {
     return ['request_format'];
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function getCacheTags() {
+    return [];
+  }
+
 }
diff --git a/core/modules/system/src/Plugin/views/field/BulkForm.php b/core/modules/system/src/Plugin/views/field/BulkForm.php
index 609d4c0..3a3b65c 100644
--- a/core/modules/system/src/Plugin/views/field/BulkForm.php
+++ b/core/modules/system/src/Plugin/views/field/BulkForm.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\system\Plugin\views\field;
 
+use Drupal\Core\Cache\CacheableDependencyInterface;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\Entity\RevisionableInterface;
@@ -15,7 +16,6 @@
 use Drupal\Core\Routing\RedirectDestinationTrait;
 use Drupal\Core\TypedData\TranslatableInterface;
 use Drupal\views\Entity\Render\EntityTranslationRenderTrait;
-use Drupal\views\Plugin\CacheablePluginInterface;
 use Drupal\views\Plugin\views\display\DisplayPluginBase;
 use Drupal\views\Plugin\views\field\FieldPluginBase;
 use Drupal\views\Plugin\views\field\UncacheableFieldHandlerTrait;
@@ -29,7 +29,7 @@
  *
  * @ViewsField("bulk_form")
  */
-class BulkForm extends FieldPluginBase implements CacheablePluginInterface {
+class BulkForm extends FieldPluginBase implements CacheableDependencyInterface {
 
   use RedirectDestinationTrait;
   use UncacheableFieldHandlerTrait;
@@ -114,10 +114,10 @@ public function init(ViewExecutable $view, DisplayPluginBase $display, array &$o
   /**
    * {@inheritdoc}
    */
-  public function isCacheable() {
+  public function getCacheMaxAge() {
     // @todo Consider making the bulk operation form cacheable. See
     //   https://www.drupal.org/node/2503009.
-    return FALSE;
+    return 0;
   }
 
   /**
@@ -130,6 +130,13 @@ public function getCacheContexts() {
   /**
    * {@inheritdoc}
    */
+  public function getCacheTags() {
+    return [];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public function getEntityTypeId() {
     return $this->getEntityType();
   }
diff --git a/core/modules/taxonomy/config/optional/views.view.taxonomy_term.yml b/core/modules/taxonomy/config/optional/views.view.taxonomy_term.yml
index e1086d1..f3784d6 100644
--- a/core/modules/taxonomy/config/optional/views.view.taxonomy_term.yml
+++ b/core/modules/taxonomy/config/optional/views.view.taxonomy_term.yml
@@ -247,7 +247,7 @@ display:
         - 'languages:language_interface'
         - url
         - 'user.node_grants:view'
-      cacheable: false
+      max_age: 0
   feed_1:
     id: feed_1
     display_title: Feed
@@ -283,7 +283,7 @@ display:
         - 'languages:language_interface'
         - url
         - 'user.node_grants:view'
-      cacheable: false
+      max_age: 0
   page_1:
     id: page_1
     display_title: Page
@@ -300,4 +300,4 @@ display:
         - 'languages:language_interface'
         - url
         - 'user.node_grants:view'
-      cacheable: false
+      max_age: 0
diff --git a/core/modules/taxonomy/src/Plugin/views/argument_default/Tid.php b/core/modules/taxonomy/src/Plugin/views/argument_default/Tid.php
index f162195..55cc6ef 100644
--- a/core/modules/taxonomy/src/Plugin/views/argument_default/Tid.php
+++ b/core/modules/taxonomy/src/Plugin/views/argument_default/Tid.php
@@ -7,10 +7,11 @@
 
 namespace Drupal\taxonomy\Plugin\views\argument_default;
 
+use Drupal\Core\Cache\Cache;
+use Drupal\Core\Cache\CacheableDependencyInterface;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Routing\RouteMatchInterface;
 use Drupal\taxonomy\TermInterface;
-use Drupal\views\Plugin\CacheablePluginInterface;
 use Drupal\views\ViewExecutable;
 use Drupal\views\Plugin\views\display\DisplayPluginBase;
 use Drupal\views\Plugin\views\argument_default\ArgumentDefaultPluginBase;
@@ -28,7 +29,7 @@
  *   title = @Translation("Taxonomy term ID from URL")
  * )
  */
-class Tid extends ArgumentDefaultPluginBase implements CacheablePluginInterface {
+class Tid extends ArgumentDefaultPluginBase implements CacheableDependencyInterface {
 
   /**
    * The route match.
@@ -216,8 +217,8 @@ public function getArgument() {
   /**
    * {@inheritdoc}
    */
-  public function isCacheable() {
-    return TRUE;
+  public function getCacheMaxAge() {
+    return Cache::PERMANENT;
   }
 
   /**
@@ -230,6 +231,13 @@ public function getCacheContexts() {
   /**
    * {@inheritdoc}
    */
+  public function getCacheTags() {
+    return [];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public function calculateDependencies() {
     $dependencies = parent::calculateDependencies();
 
diff --git a/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_taxonomy_term_relationship.yml b/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_taxonomy_term_relationship.yml
index 22521bc..5e13b59 100644
--- a/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_taxonomy_term_relationship.yml
+++ b/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_taxonomy_term_relationship.yml
@@ -199,4 +199,4 @@ display:
       contexts:
         - 'languages:language_content'
         - 'languages:language_interface'
-      cacheable: false
+      max_age: 0
diff --git a/core/modules/user/src/Plugin/views/access/Permission.php b/core/modules/user/src/Plugin/views/access/Permission.php
index 0dc5165..7b9f7a7 100644
--- a/core/modules/user/src/Plugin/views/access/Permission.php
+++ b/core/modules/user/src/Plugin/views/access/Permission.php
@@ -8,11 +8,12 @@
 namespace Drupal\user\Plugin\views\access;
 
 use Drupal\Component\Utility\SafeMarkup;
+use Drupal\Core\Cache\Cache;
+use Drupal\Core\Cache\CacheableDependencyInterface;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Session\AccountInterface;
 use Drupal\user\PermissionHandlerInterface;
-use Drupal\views\Plugin\CacheablePluginInterface;
 use Drupal\views\Plugin\views\access\AccessPluginBase;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\Routing\Route;
@@ -28,7 +29,7 @@
  *   help = @Translation("Access will be granted to users with the specified permission string.")
  * )
  */
-class Permission extends AccessPluginBase implements CacheablePluginInterface {
+class Permission extends AccessPluginBase implements CacheableDependencyInterface {
 
   /**
    * Overrides Drupal\views\Plugin\Plugin::$usesOptions.
@@ -136,8 +137,8 @@ public function buildOptionsForm(&$form, FormStateInterface $form_state) {
   /**
    * {@inheritdoc}
    */
-  public function isCacheable() {
-    return TRUE;
+  public function getCacheMaxAge() {
+    return Cache::PERMANENT;
   }
 
   /**
@@ -147,4 +148,11 @@ public function getCacheContexts() {
     return ['user.permissions'];
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function getCacheTags() {
+    return [];
+  }
+
 }
diff --git a/core/modules/user/src/Plugin/views/access/Role.php b/core/modules/user/src/Plugin/views/access/Role.php
index 4c2b732..b21af04 100644
--- a/core/modules/user/src/Plugin/views/access/Role.php
+++ b/core/modules/user/src/Plugin/views/access/Role.php
@@ -8,9 +8,9 @@
 namespace Drupal\user\Plugin\views\access;
 
 use Drupal\Component\Utility\SafeMarkup;
+use Drupal\Core\Cache\CacheableDependencyInterface;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\user\RoleStorageInterface;
-use Drupal\views\Plugin\CacheablePluginInterface;
 use Drupal\views\Plugin\views\access\AccessPluginBase;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\Routing\Route;
@@ -27,7 +27,7 @@
  *   help = @Translation("Access will be granted to users with any of the specified roles.")
  * )
  */
-class Role extends AccessPluginBase implements CacheablePluginInterface {
+class Role extends AccessPluginBase implements CacheableDependencyInterface {
 
   /**
    * Overrides Drupal\views\Plugin\Plugin::$usesOptions.
@@ -149,8 +149,8 @@ public function calculateDependencies() {
   /**
    * {@inheritdoc}
    */
-  public function isCacheable() {
-    return TRUE;
+  public function getCacheMaxAge() {
+    return 0;
   }
 
   /**
@@ -160,5 +160,12 @@ public function getCacheContexts() {
     return ['user.roles'];
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function getCacheTags() {
+    return [];
+  }
+
 }
 
diff --git a/core/modules/user/src/Plugin/views/argument_default/CurrentUser.php b/core/modules/user/src/Plugin/views/argument_default/CurrentUser.php
index b3c86ec..0d435df 100644
--- a/core/modules/user/src/Plugin/views/argument_default/CurrentUser.php
+++ b/core/modules/user/src/Plugin/views/argument_default/CurrentUser.php
@@ -7,7 +7,8 @@
 
 namespace Drupal\user\Plugin\views\argument_default;
 
-use Drupal\views\Plugin\CacheablePluginInterface;
+use Drupal\Core\Cache\Cache;
+use Drupal\Core\Cache\CacheableDependencyInterface;
 use Drupal\views\Plugin\views\argument_default\ArgumentDefaultPluginBase;
 
 /**
@@ -20,7 +21,7 @@
  *   title = @Translation("User ID from logged in user")
  * )
  */
-class CurrentUser extends ArgumentDefaultPluginBase implements CacheablePluginInterface {
+class CurrentUser extends ArgumentDefaultPluginBase implements CacheableDependencyInterface {
 
   public function getArgument() {
     return \Drupal::currentUser()->id();
@@ -29,8 +30,8 @@ public function getArgument() {
   /**
    * {@inheritdoc}
    */
-  public function isCacheable() {
-    return TRUE;
+  public function getCacheMaxAge() {
+    return Cache::PERMANENT;
   }
 
   /**
@@ -40,4 +41,11 @@ public function getCacheContexts() {
     return ['user'];
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function getCacheTags() {
+    return [];
+  }
+
 }
diff --git a/core/modules/user/src/Plugin/views/argument_default/User.php b/core/modules/user/src/Plugin/views/argument_default/User.php
index edfb709..d50d2c0 100644
--- a/core/modules/user/src/Plugin/views/argument_default/User.php
+++ b/core/modules/user/src/Plugin/views/argument_default/User.php
@@ -7,9 +7,10 @@
 
 namespace Drupal\user\Plugin\views\argument_default;
 
+use Drupal\Core\Cache\Cache;
+use Drupal\Core\Cache\CacheableDependencyInterface;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Routing\RouteMatchInterface;
-use Drupal\views\Plugin\CacheablePluginInterface;
 use Drupal\views\Plugin\views\argument_default\ArgumentDefaultPluginBase;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\HttpFoundation\Request;
@@ -24,7 +25,7 @@
  *   title = @Translation("User ID from route context")
  * )
  */
-class User extends ArgumentDefaultPluginBase implements CacheablePluginInterface {
+class User extends ArgumentDefaultPluginBase implements CacheableDependencyInterface {
 
   /**
    * The route match.
@@ -108,8 +109,8 @@ public function getArgument() {
   /**
    * {@inheritdoc}
    */
-  public function isCacheable() {
-    return TRUE;
+  public function getCacheMaxAge() {
+    return Cache::PERMANENT;
   }
 
   /**
@@ -119,4 +120,11 @@ public function getCacheContexts() {
     return ['url'];
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function getCacheTags() {
+    return [];
+  }
+
 }
diff --git a/core/modules/views/config/schema/views.schema.yml b/core/modules/views/config/schema/views.schema.yml
index 7ea599d..ea1bc38 100644
--- a/core/modules/views/config/schema/views.schema.yml
+++ b/core/modules/views/config/schema/views.schema.yml
@@ -118,15 +118,19 @@ views.view.*:
             type: mapping
             label: 'Cache metadata'
             mapping:
-              cacheable:
-                type: boolean
-                label: 'Cacheable'
+              max_age:
+                type: integer
+                label: 'Cache maximum age'
               contexts:
                 type: sequence
                 label: 'Cache contexts'
                 sequence:
                   type: string
-
+              tags:
+                type: sequence
+                label: 'Cache tags'
+                sequence:
+                  type: string
 views_block:
   type: block_settings
   label: 'View block'
diff --git a/core/modules/views/src/Entity/Render/RendererBase.php b/core/modules/views/src/Entity/Render/RendererBase.php
index deb1806..19f98c3 100644
--- a/core/modules/views/src/Entity/Render/RendererBase.php
+++ b/core/modules/views/src/Entity/Render/RendererBase.php
@@ -7,9 +7,10 @@
 
 namespace Drupal\views\Entity\Render;
 
+use Drupal\Core\Cache\Cache;
+use Drupal\Core\Cache\CacheableDependencyInterface;
 use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Language\LanguageManagerInterface;
-use Drupal\views\Plugin\CacheablePluginInterface;
 use Drupal\views\Plugin\views\query\QueryPluginBase;
 use Drupal\views\ResultRow;
 use Drupal\views\ViewExecutable;
@@ -17,7 +18,7 @@
 /**
  * Defines a base class for entity renderers.
  */
-abstract class RendererBase implements CacheablePluginInterface {
+abstract class RendererBase implements CacheableDependencyInterface {
 
   /**
    * The view executable wrapping the view storage entity.
@@ -66,8 +67,8 @@ public function __construct(ViewExecutable $view, LanguageManagerInterface $lang
   /**
    * {@inheritdoc}
    */
-  public function isCacheable() {
-    return TRUE;
+  public function getCacheMaxAge() {
+    return Cache::PERMANENT;
   }
 
   /**
@@ -78,6 +79,13 @@ public function getCacheContexts() {
   }
 
   /**
+   * {@inheritdoc}
+   */
+  public function getCacheTags() {
+    return [];
+  }
+
+  /**
    * Alters the query if needed.
    *
    * @param \Drupal\views\Plugin\views\query\QueryPluginBase $query
diff --git a/core/modules/views/src/Entity/View.php b/core/modules/views/src/Entity/View.php
index de736ec..4978ae0 100644
--- a/core/modules/views/src/Entity/View.php
+++ b/core/modules/views/src/Entity/View.php
@@ -311,8 +311,9 @@ public function preSave(EntityStorageInterface $storage) {
    *
    * Cache metadata is set per view and per display, and ends up being stored in
    * the view's configuration. This allows Views to determine very efficiently:
-   * - whether a view is cacheable at all
-   * - what the cache key for a given view should be
+   * - the max-age
+   * - the cache contexts
+   * - the cache tags
    *
    * In other words: this allows us to do the (expensive) work of initializing
    * Views plugins and handlers to determine their effect on the cacheability of
@@ -327,7 +328,10 @@ protected function addCacheMetadata() {
       $display =& $this->getDisplay($display_id);
       $executable->setDisplay($display_id);
 
-      list($display['cache_metadata']['cacheable'], $display['cache_metadata']['contexts']) = $executable->getDisplay()->calculateCacheMetadata();
+      $cache_metadata = $executable->getDisplay()->calculateCacheMetadata();
+      $display['cache_metadata']['max_age'] = $cache_metadata->getCacheMaxAge();
+      $display['cache_metadata']['contexts'] = $cache_metadata->getCacheContexts();
+      $display['cache_metadata']['tags'] = $cache_metadata->getCacheTags();
       // Always include at least the 'languages:' context as there will most
       // probably be translatable strings in the view output.
       $display['cache_metadata']['contexts'] = Cache::mergeContexts($display['cache_metadata']['contexts'], ['languages:' . LanguageInterface::TYPE_INTERFACE]);
diff --git a/core/modules/views/src/Plugin/CacheablePluginInterface.php b/core/modules/views/src/Plugin/CacheablePluginInterface.php
deleted file mode 100644
index b700ce7..0000000
--- a/core/modules/views/src/Plugin/CacheablePluginInterface.php
+++ /dev/null
@@ -1,35 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\views\Plugin\CacheablePluginInterface.
- */
-
-namespace Drupal\views\Plugin;
-
-/**
- * Provides caching information about the result cacheability of views plugins.
- *
- * For caching on the render level, we rely on bubbling of the cache contexts.
- */
-interface CacheablePluginInterface {
-
-  /**
-   * Returns TRUE if this plugin is cacheable at all.
-   *
-   * @return bool
-   */
-  public function isCacheable();
-
-  /**
-   * Returns an array of cache contexts, this plugin varies by.
-   *
-   * Note: This method is called on views safe time, so you do have the
-   * configuration available. For example an exposed filter changes its
-   * cacheability depending on the URL.
-   *
-   * @return string[]
-   */
-  public function getCacheContexts();
-
-}
diff --git a/core/modules/views/src/Plugin/views/argument/ArgumentPluginBase.php b/core/modules/views/src/Plugin/views/argument/ArgumentPluginBase.php
index a1f1c5e..2737bde 100644
--- a/core/modules/views/src/Plugin/views/argument/ArgumentPluginBase.php
+++ b/core/modules/views/src/Plugin/views/argument/ArgumentPluginBase.php
@@ -11,9 +11,10 @@
 use Drupal\Component\Utility\Html;
 use Drupal\Component\Utility\NestedArray;
 use Drupal\Component\Utility\SafeMarkup;
+use Drupal\Core\Cache\Cache;
+use Drupal\Core\Cache\CacheableDependencyInterface;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Render\Element;
-use Drupal\views\Plugin\CacheablePluginInterface;
 use Drupal\views\Plugin\views\PluginBase;
 use Drupal\views\Plugin\views\display\DisplayPluginBase;
 use Drupal\views\ViewExecutable;
@@ -59,7 +60,7 @@
  * - numeric: If set to TRUE this field is numeric and will use %d instead of
  *            %s in queries.
  */
-abstract class ArgumentPluginBase extends HandlerBase implements CacheablePluginInterface {
+abstract class ArgumentPluginBase extends HandlerBase implements CacheableDependencyInterface {
 
   var $validator = NULL;
   var $argument = NULL;
@@ -1180,24 +1181,24 @@ protected function unpackArgumentValue($force_int = FALSE) {
   /**
    * {@inheritdoc}
    */
-  public function isCacheable() {
-    $result = TRUE;
+  public function getCacheMaxAge() {
+    $max_age = Cache::PERMANENT;
 
     // Asks all subplugins (argument defaults, argument validator and styles).
-    if (($plugin = $this->getPlugin('argument_default')) && $plugin instanceof CacheablePluginInterface) {
-      $result &= $plugin->isCacheable();
+    if (($plugin = $this->getPlugin('argument_default')) && $plugin instanceof CacheableDependencyInterface) {
+      $max_age = Cache::mergeMaxAges($max_age, $plugin->getCacheMaxAge());
     }
 
-    if (($plugin = $this->getPlugin('argument_validator')) && $plugin instanceof CacheablePluginInterface) {
-      $result &= $plugin->isCacheable();
+    if (($plugin = $this->getPlugin('argument_validator')) && $plugin instanceof CacheableDependencyInterface) {
+      $max_age = Cache::mergeMaxAges($max_age, $plugin->getCacheMaxAge());
     }
 
     // Summaries use style plugins.
-    if (($plugin = $this->getPlugin('style')) && $plugin instanceof CacheablePluginInterface) {
-      $result &= $plugin->isCacheable();
+    if (($plugin = $this->getPlugin('style')) && $plugin instanceof CacheableDependencyInterface) {
+      $max_age = Cache::mergeMaxAges($max_age, $plugin->getCacheMaxAge());
     }
 
-    return $result;
+    return $max_age;
   }
 
   /**
@@ -1211,16 +1212,16 @@ public function getCacheContexts() {
     $contexts[] = 'url';
 
     // Asks all subplugins (argument defaults, argument validator and styles).
-    if (($plugin = $this->getPlugin('argument_default')) && $plugin instanceof CacheablePluginInterface) {
-      $contexts = array_merge($plugin->getCacheContexts(), $contexts);
+    if (($plugin = $this->getPlugin('argument_default')) && $plugin instanceof CacheableDependencyInterface) {
+      $contexts = Cache::mergeContexts($contexts, $plugin->getCacheContexts());
     }
 
-    if (($plugin = $this->getPlugin('argument_validator')) && $plugin instanceof CacheablePluginInterface) {
-      $contexts = array_merge($plugin->getCacheContexts(), $contexts);
+    if (($plugin = $this->getPlugin('argument_validator')) && $plugin instanceof CacheableDependencyInterface) {
+      $contexts = Cache::mergeContexts($contexts, $plugin->getCacheContexts());
     }
 
-    if (($plugin = $this->getPlugin('style')) && $plugin instanceof CacheablePluginInterface) {
-      $contexts = array_merge($plugin->getCacheContexts(), $contexts);
+    if (($plugin = $this->getPlugin('style')) && $plugin instanceof CacheableDependencyInterface) {
+      $contexts = Cache::mergeContexts($contexts, $plugin->getCacheContexts());
     }
 
     return $contexts;
@@ -1229,6 +1230,28 @@ public function getCacheContexts() {
   /**
    * {@inheritdoc}
    */
+  public function getCacheTags() {
+    $tags = [];
+
+    // Asks all subplugins (argument defaults, argument validator and styles).
+    if (($plugin = $this->getPlugin('argument_default')) && $plugin instanceof CacheableDependencyInterface) {
+      $tags = Cache::mergeTags($tags, $plugin->getCacheTags());
+    }
+
+    if (($plugin = $this->getPlugin('argument_validator')) && $plugin instanceof CacheableDependencyInterface) {
+      $tags = Cache::mergeTags($tags, $plugin->getCacheTags());
+    }
+
+    if (($plugin = $this->getPlugin('style')) && $plugin instanceof CacheableDependencyInterface) {
+      $tags = Cache::mergeTags($tags, $plugin->getCacheTags());
+    }
+
+    return $tags;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public function calculateDependencies() {
     $dependencies = [];
     if (($argument_default = $this->getPlugin('argument_default')) && $argument_default instanceof DependentPluginInterface) {
diff --git a/core/modules/views/src/Plugin/views/argument_default/Fixed.php b/core/modules/views/src/Plugin/views/argument_default/Fixed.php
index 99f55ae..43eb1f9 100644
--- a/core/modules/views/src/Plugin/views/argument_default/Fixed.php
+++ b/core/modules/views/src/Plugin/views/argument_default/Fixed.php
@@ -7,8 +7,9 @@
 
 namespace Drupal\views\Plugin\views\argument_default;
 
+use Drupal\Core\Cache\Cache;
+use Drupal\Core\Cache\CacheableDependencyInterface;
 use Drupal\Core\Form\FormStateInterface;
-use Drupal\views\Plugin\CacheablePluginInterface;
 
 /**
  * The fixed argument default handler.
@@ -20,7 +21,7 @@
  *   title = @Translation("Fixed")
  * )
  */
-class Fixed extends ArgumentDefaultPluginBase implements CacheablePluginInterface {
+class Fixed extends ArgumentDefaultPluginBase implements CacheableDependencyInterface {
 
   protected function defineOptions() {
     $options = parent::defineOptions();
@@ -48,8 +49,8 @@ public function getArgument() {
   /**
    * {@inheritdoc}
    */
-  public function isCacheable() {
-    return TRUE;
+  public function getCacheMaxAge() {
+    return Cache::PERMANENT;
   }
 
   /**
@@ -59,4 +60,11 @@ public function getCacheContexts() {
     return [];
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function getCacheTags() {
+    return [];
+  }
+
 }
diff --git a/core/modules/views/src/Plugin/views/argument_default/QueryParameter.php b/core/modules/views/src/Plugin/views/argument_default/QueryParameter.php
index feccd21..f4bf3f4 100644
--- a/core/modules/views/src/Plugin/views/argument_default/QueryParameter.php
+++ b/core/modules/views/src/Plugin/views/argument_default/QueryParameter.php
@@ -7,8 +7,9 @@
 
 namespace Drupal\views\Plugin\views\argument_default;
 
+use Drupal\Core\Cache\Cache;
+use Drupal\Core\Cache\CacheableDependencyInterface;
 use Drupal\Core\Form\FormStateInterface;
-use Drupal\views\Plugin\CacheablePluginInterface;
 
 /**
  * A query parameter argument default handler.
@@ -20,7 +21,7 @@
  *   title = @Translation("Query parameter")
  * )
  */
-class QueryParameter extends ArgumentDefaultPluginBase implements CacheablePluginInterface {
+class QueryParameter extends ArgumentDefaultPluginBase implements CacheableDependencyInterface {
 
   /**
    * {@inheritdoc}
@@ -87,8 +88,8 @@ public function getArgument() {
   /**
    * {@inheritdoc}
    */
-  public function isCacheable() {
-    return TRUE;
+  public function getCacheMaxAge() {
+    return Cache::PERMANENT;
   }
 
   /**
@@ -98,4 +99,11 @@ public function getCacheContexts() {
     return ['url'];
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function getCacheTags() {
+    return [];
+  }
+
 }
diff --git a/core/modules/views/src/Plugin/views/argument_default/Raw.php b/core/modules/views/src/Plugin/views/argument_default/Raw.php
index 20715f3..bb88532 100644
--- a/core/modules/views/src/Plugin/views/argument_default/Raw.php
+++ b/core/modules/views/src/Plugin/views/argument_default/Raw.php
@@ -7,10 +7,11 @@
 
 namespace Drupal\views\Plugin\views\argument_default;
 
+use Drupal\Core\Cache\Cache;
+use Drupal\Core\Cache\CacheableDependencyInterface;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Path\AliasManagerInterface;
 use Drupal\Core\Path\CurrentPathStack;
-use Drupal\views\Plugin\CacheablePluginInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\HttpFoundation\Request;
 
@@ -24,7 +25,7 @@
  *   title = @Translation("Raw value from URL")
  * )
  */
-class Raw extends ArgumentDefaultPluginBase implements CacheablePluginInterface {
+class Raw extends ArgumentDefaultPluginBase implements CacheableDependencyInterface {
 
   /**
    * The alias manager.
@@ -116,8 +117,8 @@ public function getArgument() {
   /**
    * {@inheritdoc}
    */
-  public function isCacheable() {
-    return TRUE;
+  public function getCacheMaxAge() {
+    return Cache::PERMANENT;
   }
 
   /**
@@ -127,4 +128,11 @@ public function getCacheContexts() {
     return ['url'];
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function getCacheTags() {
+    return [];
+  }
+
 }
diff --git a/core/modules/views/src/Plugin/views/cache/CachePluginBase.php b/core/modules/views/src/Plugin/views/cache/CachePluginBase.php
index 06a18e3..3b28ffa 100644
--- a/core/modules/views/src/Plugin/views/cache/CachePluginBase.php
+++ b/core/modules/views/src/Plugin/views/cache/CachePluginBase.php
@@ -8,6 +8,7 @@
 namespace Drupal\views\Plugin\views\cache;
 
 use Drupal\Core\Cache\Cache;
+use Drupal\Core\Cache\CacheableMetadata;
 use Drupal\views\Plugin\views\PluginBase;
 use Drupal\Core\Database\Query\Select;
 use Drupal\views\ResultRow;
@@ -211,7 +212,7 @@ public function generateResultsKey() {
         'items_per_page' => $this->view->getItemsPerPage(),
         'offset' => $this->view->getOffset(),
       ];
-      $key_data += \Drupal::service('cache_contexts_manager')->convertTokensToKeys($this->displayHandler->getCacheMetadata()['contexts']);
+      $key_data += \Drupal::service('cache_contexts_manager')->convertTokensToKeys($this->displayHandler->getCacheMetadata()->getCacheContexts());
 
       $this->resultsKey = $this->view->storage->id() . ':' . $this->displayHandler->display['id'] . ':results:' . hash('sha256', serialize($key_data));
     }
@@ -288,12 +289,10 @@ protected function prepareViewResult(array $result) {
   /**
    * Alters the cache metadata of a display upon saving a view.
    *
-   * @param bool $is_cacheable
-   *   Whether the display is cacheable.
-   * @param string[] $cache_contexts
-   *   The cache contexts the display varies by.
+   * @param \Drupal\Core\Cache\CacheableMetadata $cache_metadata
+   *   The cache metadata.
    */
-  public function alterCacheMetadata(&$is_cacheable, array &$cache_contexts) {
+  public function alterCacheMetadata(CacheableMetadata $cache_metadata) {
   }
 
   /**
diff --git a/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php b/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php
index 3245bd2..92c393f 100644
--- a/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php
+++ b/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php
@@ -13,6 +13,7 @@
 use Drupal\Component\Utility\SafeMarkup;
 use Drupal\Core\Cache\Cache;
 use Drupal\Core\Cache\CacheableMetadata;
+use Drupal\Core\Cache\CacheableDependencyInterface;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Language\LanguageInterface;
 use Drupal\Core\Plugin\PluginDependencyTrait;
@@ -20,7 +21,6 @@
 use Drupal\Core\Theme\Registry;
 use Drupal\Core\Url;
 use Drupal\views\Form\ViewsForm;
-use Drupal\views\Plugin\CacheablePluginInterface;
 use Drupal\views\Plugin\views\area\AreaPluginBase;
 use Drupal\views\ViewExecutable;
 use Drupal\views\Plugin\views\PluginBase;
@@ -2151,9 +2151,9 @@ protected function applyDisplayCachablityMetadata(array &$element) {
     $cache = $this->getPlugin('cache');
 
     (new CacheableMetadata())
-      ->setCacheTags($this->view->getCacheTags())
+      ->setCacheTags(Cache::mergeTags($this->view->getCacheTags(), isset($this->display['cache_metadata']['tags']) ? $this->display['cache_metadata']['tags'] : []))
       ->setCacheContexts(isset($this->display['cache_metadata']['contexts']) ? $this->display['cache_metadata']['contexts'] : [])
-      ->setCacheMaxAge($cache->getCacheMaxAge())
+      ->setCacheMaxAge(Cache::mergeMaxAges($cache->getCacheMaxAge(), isset($this->display['cache_metadata']['max_age']) ? $this->display['cache_metadata']['max_age'] : Cache::PERMANENT))
       ->merge(CacheableMetadata::createFromRenderArray($element))
       ->applyTo($element);
   }
@@ -2271,18 +2271,13 @@ public function preExecute() {
    * {@inheritdoc}
    */
   public function calculateCacheMetadata () {
-    $is_cacheable = TRUE;
-    $cache_contexts = [];
+    $cache_metadata = new CacheableMetadata();
 
     // Iterate over ordinary views plugins.
     foreach (Views::getPluginTypes('plugin') as $plugin_type) {
       $plugin = $this->getPlugin($plugin_type);
-      if ($plugin instanceof CacheablePluginInterface) {
-        $cache_contexts = array_merge($cache_contexts, $plugin->getCacheContexts());
-        $is_cacheable &= $plugin->isCacheable();
-      }
-      else {
-        $is_cacheable = FALSE;
+      if ($plugin instanceof CacheableDependencyInterface) {
+        $cache_metadata = $cache_metadata->merge(CacheableMetadata::createFromObject($plugin));
       }
     }
 
@@ -2291,19 +2286,18 @@ public function calculateCacheMetadata () {
     foreach (array_keys(Views::getHandlerTypes()) as $handler_type) {
       $handlers = $this->getHandlers($handler_type);
       foreach ($handlers as $handler) {
-        if ($handler instanceof CacheablePluginInterface) {
-          $cache_contexts = array_merge($cache_contexts, $handler->getCacheContexts());
-          $is_cacheable &= $handler->isCacheable();
+        if ($handler instanceof CacheableDependencyInterface) {
+          $cache_metadata = $cache_metadata->merge(CacheableMetadata::createFromObject($handler));
         }
       }
     }
 
     /** @var \Drupal\views\Plugin\views\cache\CachePluginBase $cache_plugin */
     if ($cache_plugin = $this->getPlugin('cache')) {
-      $cache_plugin->alterCacheMetadata($is_cacheable, $cache_contexts);
+      $cache_plugin->alterCacheMetadata($cache_metadata);
     }
 
-    return [(bool) $is_cacheable, $cache_contexts];
+    return $cache_metadata;
   }
 
   /**
@@ -2311,9 +2305,18 @@ public function calculateCacheMetadata () {
    */
   public function getCacheMetadata() {
     if (!isset($this->display['cache_metadata'])) {
-      list($this->display['cache_metadata']['cacheable'], $this->display['cache_metadata']['contexts']) = $this->calculateCacheMetadata();
+      $cache_metadata = $this->calculateCacheMetadata();
+      $this->display['cache_metadata']['max_age'] = $cache_metadata->getCacheMaxAge();
+      $this->display['cache_metadata']['contexts'] = $cache_metadata->getCacheContexts();
+      $this->display['cache_metadata']['tags'] = $cache_metadata->getCacheTags();
+    }
+    else {
+      $cache_metadata = (new CacheableMetadata())
+        ->setCacheMaxAge($this->display['cache_metadata']['max_age'])
+        ->setCacheContexts($this->display['cache_metadata']['contexts'])
+        ->setCacheTags($this->display['cache_metadata']['tags']);
     }
-    return $this->display['cache_metadata'];
+    return $cache_metadata;
   }
 
   /**
diff --git a/core/modules/views/src/Plugin/views/display/DisplayPluginInterface.php b/core/modules/views/src/Plugin/views/display/DisplayPluginInterface.php
index c1aba0c..6690c21 100644
--- a/core/modules/views/src/Plugin/views/display/DisplayPluginInterface.php
+++ b/core/modules/views/src/Plugin/views/display/DisplayPluginInterface.php
@@ -439,20 +439,16 @@ public function preExecute();
   /**
    * Calculates the display's cache metadata by inspecting each handler/plugin.
    *
-   * @return array
-   *   Returns an array:
-   *   - first value: (boolean) Whether the display is cacheable.
-   *   - second value: (string[]) The cache contexts the display varies by.
+   * @return \Drupal\Core\Cache\CacheableMetadata
+   *   The cache metadata.
    */
   public function calculateCacheMetadata();
 
   /**
    * Gets the cache metadata.
    *
-   * @return array
-   *   Returns an array:
-   *   - first value: (boolean) Whether the display is cacheable.
-   *   - second value: (string[]) The cache contexts the display varies by.
+   * @return \Drupal\Core\Cache\CacheableMetadata
+   *   The cache metadata.
    */
   public function getCacheMetadata();
 
diff --git a/core/modules/views/src/Plugin/views/exposed_form/ExposedFormPluginBase.php b/core/modules/views/src/Plugin/views/exposed_form/ExposedFormPluginBase.php
index 11a3e2d..7e19634 100644
--- a/core/modules/views/src/Plugin/views/exposed_form/ExposedFormPluginBase.php
+++ b/core/modules/views/src/Plugin/views/exposed_form/ExposedFormPluginBase.php
@@ -8,10 +8,11 @@
 namespace Drupal\views\Plugin\views\exposed_form;
 
 use Drupal\Component\Utility\SafeMarkup;
+use Drupal\Core\Cache\Cache;
+use Drupal\Core\Cache\CacheableDependencyInterface;
 use Drupal\Core\Form\FormState;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\views\Form\ViewsExposedForm;
-use Drupal\views\Plugin\CacheablePluginInterface;
 use Drupal\views\ViewExecutable;
 use Drupal\views\Plugin\views\display\DisplayPluginBase;
 use Drupal\views\Plugin\views\PluginBase;
@@ -35,7 +36,7 @@
 /**
  * Base class for Views exposed filter form plugins.
  */
-abstract class ExposedFormPluginBase extends PluginBase implements CacheablePluginInterface {
+abstract class ExposedFormPluginBase extends PluginBase implements CacheableDependencyInterface {
 
   /**
    * Overrides Drupal\views\Plugin\Plugin::$usesOptions.
@@ -336,8 +337,8 @@ public function resetForm(&$form, FormStateInterface $form_state) {
   /**
    * {@inheritdoc}
    */
-  public function isCacheable() {
-    return TRUE;
+  public function getCacheMaxAge() {
+    return Cache::PERMANENT;
   }
 
   /**
@@ -364,6 +365,13 @@ public function getCacheContexts() {
     return $contexts;
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function getCacheTags() {
+    return [];
+  }
+
 }
 
 /**
diff --git a/core/modules/views/src/Plugin/views/field/Field.php b/core/modules/views/src/Plugin/views/field/Field.php
index 386456c..28122df 100644
--- a/core/modules/views/src/Plugin/views/field/Field.php
+++ b/core/modules/views/src/Plugin/views/field/Field.php
@@ -9,6 +9,8 @@
 
 use Drupal\Component\Utility\SafeMarkup;
 use Drupal\Component\Utility\Xss as CoreXss;
+use Drupal\Core\Cache\Cache;
+use Drupal\Core\Cache\CacheableDependencyInterface;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
@@ -23,7 +25,6 @@
 use Drupal\Core\Session\AccountInterface;
 use Drupal\views\FieldAPIHandlerTrait;
 use Drupal\views\Entity\Render\EntityFieldRenderer;
-use Drupal\views\Plugin\CacheablePluginInterface;
 use Drupal\views\Plugin\views\display\DisplayPluginBase;
 use Drupal\views\ResultRow;
 use Drupal\views\ViewExecutable;
@@ -38,7 +39,7 @@
  *
  * @ViewsField("field")
  */
-class Field extends FieldPluginBase implements CacheablePluginInterface, MultiItemsFieldHandlerInterface {
+class Field extends FieldPluginBase implements CacheableDependencyInterface, MultiItemsFieldHandlerInterface {
   use FieldAPIHandlerTrait;
 
   /**
@@ -952,8 +953,8 @@ public function calculateDependencies() {
   /**
    * {@inheritdoc}
    */
-  public function isCacheable() {
-    return FALSE;
+  public function getCacheMaxAge() {
+    return Cache::PERMANENT;
   }
 
   /**
@@ -964,6 +965,18 @@ public function getCacheContexts() {
   }
 
   /**
+   * {@inheritdoc}
+   */
+  public function getCacheTags() {
+    $field_definition = $this->getFieldDefinition();
+    $field_storage_definition = $this->getFieldStorageDefinition();
+    return Cache::mergeTags(
+      $field_definition instanceof CacheableDependencyInterface ? $field_definition->getCacheTags() : [],
+      $field_storage_definition instanceof CacheableDependencyInterface ? $field_storage_definition->getCacheTags() : []
+    );
+  }
+
+  /**
    * Gets the table mapping for the entity type of the field.
    *
    * @return \Drupal\Core\Entity\Sql\DefaultTableMapping
diff --git a/core/modules/views/src/Plugin/views/filter/FilterPluginBase.php b/core/modules/views/src/Plugin/views/filter/FilterPluginBase.php
index d2e9fe8..ee383b1 100644
--- a/core/modules/views/src/Plugin/views/filter/FilterPluginBase.php
+++ b/core/modules/views/src/Plugin/views/filter/FilterPluginBase.php
@@ -7,11 +7,12 @@
 
 namespace Drupal\views\Plugin\views\filter;
 
+use Drupal\Core\Cache\Cache;
+use Drupal\Core\Cache\CacheableDependencyInterface;
 use Drupal\Core\Form\FormHelper;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Render\Element;
 use Drupal\user\RoleInterface;
-use Drupal\views\Plugin\CacheablePluginInterface;
 use Drupal\views\Plugin\views\HandlerBase;
 use Drupal\Component\Utility\Html;
 use Drupal\Component\Utility\SafeMarkup;
@@ -47,7 +48,7 @@
 /**
  * Base class for Views filters handler plugins.
  */
-abstract class FilterPluginBase extends HandlerBase implements CacheablePluginInterface {
+abstract class FilterPluginBase extends HandlerBase implements CacheableDependencyInterface {
 
   /**
    * Contains the actual value of the field,either configured in the views ui
@@ -1465,8 +1466,8 @@ protected static function arrayFilterZero($var) {
   /**
    * {@inheritdoc}
    */
-  public function isCacheable() {
-    return TRUE;
+  public function getCacheMaxAge() {
+    return Cache::PERMANENT;
   }
 
   /**
@@ -1483,6 +1484,13 @@ public function getCacheContexts() {
     return $cache_contexts;
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function getCacheTags() {
+    return [];
+  }
+
 }
 
 /**
diff --git a/core/modules/views/src/Plugin/views/pager/SqlBase.php b/core/modules/views/src/Plugin/views/pager/SqlBase.php
index b2d7389..597bb26 100644
--- a/core/modules/views/src/Plugin/views/pager/SqlBase.php
+++ b/core/modules/views/src/Plugin/views/pager/SqlBase.php
@@ -7,13 +7,14 @@
 
 namespace Drupal\views\Plugin\views\pager;
 
+use Drupal\Core\Cache\Cache;
+use Drupal\Core\Cache\CacheableDependencyInterface;
 use Drupal\Core\Form\FormStateInterface;
-use Drupal\views\Plugin\CacheablePluginInterface;
 
 /**
  * A common base class for sql based pager.
  */
-abstract class SqlBase extends PagerPluginBase implements CacheablePluginInterface {
+abstract class SqlBase extends PagerPluginBase implements CacheableDependencyInterface {
 
   protected function defineOptions() {
     $options = parent::defineOptions();
@@ -374,8 +375,8 @@ public function exposedFormValidate(&$form, FormStateInterface $form_state) {
   /**
    * {@inheritdoc}
    */
-  public function isCacheable() {
-    return TRUE;
+  public function getCacheMaxAge() {
+    return Cache::PERMANENT;
   }
 
   /**
@@ -392,4 +393,11 @@ public function getCacheContexts() {
     return $contexts;
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function getCacheTags() {
+    return [];
+  }
+
 }
diff --git a/core/modules/views/src/Plugin/views/query/QueryPluginBase.php b/core/modules/views/src/Plugin/views/query/QueryPluginBase.php
index 0d6d9f8..540a110 100644
--- a/core/modules/views/src/Plugin/views/query/QueryPluginBase.php
+++ b/core/modules/views/src/Plugin/views/query/QueryPluginBase.php
@@ -8,8 +8,8 @@
 namespace Drupal\views\Plugin\views\query;
 
 use Drupal\Core\Cache\Cache;
+use Drupal\Core\Cache\CacheableDependencyInterface;
 use Drupal\Core\Form\FormStateInterface;
-use Drupal\views\Plugin\CacheablePluginInterface;
 use Drupal\views\Plugin\views\PluginBase;
 use Drupal\views\Plugin\views\display\DisplayPluginBase;
 use Drupal\views\ViewExecutable;
@@ -37,7 +37,7 @@
 /**
  * Base plugin class for Views queries.
  */
-abstract class QueryPluginBase extends PluginBase implements CacheablePluginInterface {
+abstract class QueryPluginBase extends PluginBase implements CacheableDependencyInterface {
 
   /**
    * A pager plugin that should be provided by the display.
@@ -317,9 +317,9 @@ public function getEntityTableInfo() {
   /**
    * {@inheritdoc}
    */
-  public function isCacheable() {
+  public function getCacheMaxAge() {
     // This plugin can't really determine that.
-    return TRUE;
+    return Cache::PERMANENT;
   }
 
   /**
@@ -342,13 +342,6 @@ public function getCacheTags() {
     return [];
   }
 
-  /**
-   * {@inheritdoc}
-   */
-  public function getCacheMaxAge() {
-    return Cache::PERMANENT;
-  }
-
 }
 
 /**
diff --git a/core/modules/views/src/Plugin/views/sort/Random.php b/core/modules/views/src/Plugin/views/sort/Random.php
index 9d8fdbb..944ac7f 100644
--- a/core/modules/views/src/Plugin/views/sort/Random.php
+++ b/core/modules/views/src/Plugin/views/sort/Random.php
@@ -7,15 +7,15 @@
 
 namespace Drupal\views\Plugin\views\sort;
 
+use Drupal\Core\Cache\CacheableDependencyInterface;
 use Drupal\Core\Form\FormStateInterface;
-use Drupal\views\Plugin\CacheablePluginInterface;
 
 /**
  * Handle a random sort.
  *
  * @ViewsSort("random")
  */
-class Random extends SortPluginBase implements CacheablePluginInterface {
+class Random extends SortPluginBase implements CacheableDependencyInterface {
 
   /**
    * {@inheritdoc}
@@ -26,8 +26,6 @@ public function usesGroupBy() {
 
   public function query() {
     $this->query->addOrderBy('rand');
-    // @todo Replace this once https://www.drupal.org/node/2464427 is in.
-    $this->view->element['#cache']['max-age'] = 0;
   }
 
   public function buildOptionsForm(&$form, FormStateInterface $form_state) {
@@ -38,8 +36,8 @@ public function buildOptionsForm(&$form, FormStateInterface $form_state) {
   /**
    * {@inheritdoc}
    */
-  public function isCacheable() {
-    return FALSE;
+  public function getCacheMaxAge() {
+    return 0;
   }
 
   /**
@@ -49,4 +47,11 @@ public function getCacheContexts() {
     return [];
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function getCacheTags() {
+    return [];
+  }
+
 }
diff --git a/core/modules/views/src/Plugin/views/sort/SortPluginBase.php b/core/modules/views/src/Plugin/views/sort/SortPluginBase.php
index 98f3797..f2742be 100644
--- a/core/modules/views/src/Plugin/views/sort/SortPluginBase.php
+++ b/core/modules/views/src/Plugin/views/sort/SortPluginBase.php
@@ -7,8 +7,9 @@
 
 namespace Drupal\views\Plugin\views\sort;
 
+use Drupal\Core\Cache\Cache;
+use Drupal\Core\Cache\CacheableDependencyInterface;
 use Drupal\Core\Form\FormStateInterface;
-use Drupal\views\Plugin\CacheablePluginInterface;
 use Drupal\views\Plugin\views\HandlerBase;
 
 /**
@@ -27,7 +28,7 @@
 /**
  * Base sort handler that has no options and performs a simple sort.
  */
-abstract class SortPluginBase extends HandlerBase implements CacheablePluginInterface {
+abstract class SortPluginBase extends HandlerBase implements CacheableDependencyInterface {
 
   /**
    * Determine if a sort can be exposed.
@@ -228,10 +229,10 @@ public function defaultExposeOptions() {
   /**
    * {@inheritdoc}
    */
-  public function isCacheable() {
+  public function getCacheMaxAge() {
     // The result of a sort does not depend on outside information, so by
     // default it is cacheable.
-    return TRUE;
+    return Cache::PERMANENT;
   }
 
   /**
@@ -246,6 +247,13 @@ public function getCacheContexts() {
     return $cache_contexts;
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function getCacheTags() {
+    return [];
+  }
+
 }
 
 /**
diff --git a/core/modules/views/src/Plugin/views/style/Table.php b/core/modules/views/src/Plugin/views/style/Table.php
index 4efeda2..3960bf9 100644
--- a/core/modules/views/src/Plugin/views/style/Table.php
+++ b/core/modules/views/src/Plugin/views/style/Table.php
@@ -9,8 +9,8 @@
 
 use Drupal\Component\Plugin\Discovery\DiscoveryInterface;
 use Drupal\Component\Utility\Html;
+use Drupal\Core\Cache\CacheableDependencyInterface;
 use Drupal\Core\Form\FormStateInterface;
-use Drupal\views\Plugin\CacheablePluginInterface;
 use Drupal\views\Plugin\views\wizard\WizardInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\HttpFoundation\Request;
@@ -28,7 +28,7 @@
  *   display_types = {"normal"}
  * )
  */
-class Table extends StylePluginBase implements CacheablePluginInterface {
+class Table extends StylePluginBase implements CacheableDependencyInterface {
 
   /**
    * Does the style plugin for itself support to add fields to it's output.
@@ -432,8 +432,8 @@ public function wizardSubmit(&$form, FormStateInterface $form_state, WizardInter
   /**
    * {@inheritdoc}
    */
-  public function isCacheable() {
-    return TRUE;
+  public function getCacheMaxAge() {
+    return 0;
   }
 
   /**
@@ -453,4 +453,11 @@ public function getCacheContexts() {
     return $contexts;
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function getCacheTags() {
+    return [];
+  }
+
 }
diff --git a/core/modules/views/src/Tests/GlossaryTest.php b/core/modules/views/src/Tests/GlossaryTest.php
index 7d55f85..8919854 100644
--- a/core/modules/views/src/Tests/GlossaryTest.php
+++ b/core/modules/views/src/Tests/GlossaryTest.php
@@ -73,7 +73,18 @@ public function testGlossaryView() {
     // Verify cache tags.
     $this->assertPageCacheContextsAndTags($url, ['languages:' . LanguageInterface::TYPE_CONTENT, 'languages:' . LanguageInterface::TYPE_INTERFACE, 'theme', 'url', 'user.node_grants:view', 'user.permissions'], [
       'config:views.view.glossary',
+      // Listed for letter 'a'.
       'node:' . $nodes_by_char['a'][0]->id(), 'node:' . $nodes_by_char['a'][1]->id(), 'node:' . $nodes_by_char['a'][2]->id(),
+      // Link for letter 'd'.
+      'node:1',
+      // Link for letter 'p'.
+      'node:16',
+      // Link for letter 'r'.
+      'node:2',
+      // Link for letter 'l'.
+      'node:21',
+      // Link for letter 'u'.
+      'node:6',
       'node_list',
       'user:0',
       'user_list',
diff --git a/core/modules/views/src/Tests/Update/Update8001Test.php b/core/modules/views/src/Tests/Update/Update8001Test.php
new file mode 100644
index 0000000..afd43b2
--- /dev/null
+++ b/core/modules/views/src/Tests/Update/Update8001Test.php
@@ -0,0 +1,104 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\views\Tests\Update\Update8001Test.
+ */
+
+namespace Drupal\views\Tests\Update;
+
+use Drupal\Core\Url;
+use Drupal\views\Tests\ViewTestBase;
+use Drupal\views\Views;
+
+/**
+ * Tests that views update hooks are properly run.
+ *
+ * @group Update
+ */
+class Update8001Test extends ViewTestBase {
+
+  /**
+   * Set to FALSE to avoid strict check all configuration saved.
+   *
+   * @see \Drupal\Core\Config\Testing\ConfigSchemaChecker
+   *
+   * @var bool
+   */
+  protected $strictConfigSchema = FALSE;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = ['views'];
+
+  /**
+   * Views used by this test.
+   *
+   * @var array
+   */
+  public static $testViews = ['test_view_update'];
+
+  /**
+   * The user who can run updates.
+   *
+   * @var \Drupal\user\UserInterface
+   */
+  protected $user;
+
+  /**
+   * The update URL.
+   *
+   * @var string
+   */
+  protected $updateUrl;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setUp() {
+    parent::setUp();
+
+    require_once \Drupal::root() . '/core/includes/update.inc';
+    $this->user = $this->drupalCreateUser(['administer software updates', 'access site in maintenance mode']);
+    $this->updateUrl = Url::fromRoute('system.db_update');
+  }
+
+  /**
+   * Tests that update hooks are properly run.
+   */
+  public function testUpdateHooks() {
+    // Verify that the 8000 schema is in place.
+    $this->assertEqual(drupal_get_installed_schema_version('views'), 8000);
+    /* @var \Drupal\views\Entity\View $view */
+    $view = Views::getView('content');
+    $displays = $view->get('display');
+    foreach (array_keys($displays) as $display_id) {
+      $display = $view->getDisplay($display_id);
+      unset($display['cache_metadata']['cacheable']);
+    }
+    $this->assertTrue(isset($display['cache_metadata']['cacheable']));
+
+    // Increment the schema version.
+    \Drupal::state()->set('views', 8001);
+
+    $this->drupalLogin($this->user);
+    $this->drupalGet($this->updateUrl, ['external' => TRUE]);
+    $this->clickLink(t('Continue'));
+    $this->assertRaw('Schema version 8001.');
+    // Run the update hooks.
+    $this->clickLink(t('Apply pending updates'));
+
+    // Ensure schema has changed.
+    $this->assertEqual(drupal_get_installed_schema_version('views', TRUE), 8001);
+    /* @var \Drupal\views\Entity\View $view */
+    $view = Views::getView('content');
+    $displays = $view->get('display');
+    foreach (array_keys($displays) as $display_id) {
+      $display = $view->getDisplay($display_id);
+      unset($display['cache_metadata']['cacheable']);
+    }
+    $this->assertFalse(isset($display['cache_metadata']['cacheable']));
+  }
+
+}
diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.numeric_test.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.numeric_test.yml
index 8593665..7bcc16b 100644
--- a/core/modules/views/tests/modules/views_test_config/test_views/views.view.numeric_test.yml
+++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.numeric_test.yml
@@ -174,7 +174,7 @@ display:
     cache_metadata:
       contexts:
         - language
-      cacheable: false
+      max_age: 0
   page_1:
     display_plugin: page
     id: page_1
@@ -186,4 +186,4 @@ display:
     cache_metadata:
       contexts:
         - language
-      cacheable: false
+      max_age: 0
diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_entity_test_link.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_entity_test_link.yml
index 509826c..e2a148c 100644
--- a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_entity_test_link.yml
+++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_entity_test_link.yml
@@ -323,4 +323,4 @@ display:
         - entity_test_view_grants
         - 'languages:language_content'
         - 'languages:language_interface'
-      cacheable: false
+      max_age: 0
diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_feed_icon.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_feed_icon.yml
index a5bb9f5..b0087c7 100644
--- a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_feed_icon.yml
+++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_feed_icon.yml
@@ -174,7 +174,7 @@ display:
         - 'languages:language_interface'
         - url
         - 'user.node_grants:view'
-      cacheable: false
+      max_age: 0
   feed_1:
     display_plugin: feed
     id: feed_1
@@ -198,7 +198,7 @@ display:
         - 'languages:language_interface'
         - url
         - 'user.node_grants:view'
-      cacheable: false
+      max_age: 0
   page_1:
     display_plugin: page
     id: page_1
@@ -213,4 +213,4 @@ display:
         - 'languages:language_interface'
         - url
         - 'user.node_grants:view'
-      cacheable: false
+      max_age: 0
diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_field_field_attachment_test.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_field_field_attachment_test.yml
index eae936f..eeb345e 100644
--- a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_field_field_attachment_test.yml
+++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_field_field_attachment_test.yml
@@ -137,4 +137,4 @@ display:
       contexts:
         - 'languages:language_content'
         - 'languages:language_interface'
-      cacheable: false
+      max_age: 0
diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_row_render_cache.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_row_render_cache.yml
index 28a1812..aed7b54 100644
--- a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_row_render_cache.yml
+++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_row_render_cache.yml
@@ -500,4 +500,4 @@ display:
         - 'languages:language_content'
         - 'languages:language_interface'
         - 'user.node_grants:view'
-      cacheable: false
+      max_age: 0
diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_view_update.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_view_update.yml
new file mode 100644
index 0000000..ce3f76a
--- /dev/null
+++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_view_update.yml
@@ -0,0 +1,75 @@
+langcode: en
+status: true
+dependencies: {  }
+id: test_view_update
+label: 'Test view embed'
+module: views
+description: ''
+tag: ''
+base_table: views_test_data
+base_field: ''
+core: '8'
+display:
+  default:
+    display_options:
+      defaults:
+        fields: false
+        pager: false
+        sorts: false
+      row:
+        type: fields
+      fields:
+        age:
+          field: age
+          id: age
+          relationship: none
+          table: views_test_data
+          plugin_id: numeric
+        id:
+          field: id
+          id: id
+          relationship: none
+          table: views_test_data
+          plugin_id: numeric
+        name:
+          field: name
+          id: name
+          relationship: none
+          table: views_test_data
+          plugin_id: string
+      pager:
+        options:
+          offset: 0
+        type: none
+      sorts:
+        id:
+          field: id
+          id: id
+          order: ASC
+          relationship: none
+          table: views_test_data
+          plugin_id: numeric
+      display_extenders: {  }
+    display_plugin: default
+    display_title: Master
+    id: default
+    position: 0
+    cache_metadata:
+      max_age: -1
+      contexts:
+        - 'languages:language_interface'
+      tags: {  }
+      cacheable: false
+  embed_1:
+    display_options:
+      display_extenders: {  }
+    display_plugin: embed
+    display_title: Embedded
+    id: embed_1
+    position: 1
+    cache_metadata:
+      max_age: -1
+      contexts:
+        - 'languages:language_interface'
+      tags: {  }
+      cacheable: false
diff --git a/core/modules/views/views.install b/core/modules/views/views.install
index e8e3e5e..f5cc48e 100644
--- a/core/modules/views/views.install
+++ b/core/modules/views/views.install
@@ -5,9 +5,36 @@
  * Contains install and update functions for Views.
  */
 
+use Drupal\views\Entity\View;
+
 /**
  * Implements hook_install().
  */
 function views_install() {
   module_set_weight('views', 10);
 }
+
+/**
+ * @addtogroup updates-8.0.x-beta
+ * @{
+ */
+
+/**
+ * Update all views to re-generate the cacheability metadata.
+ */
+function views_update_8001(&$sandbox) {
+  // Remove cacheable key form cache_metadata.
+  // @see https://www.drupal.org/node/2464427
+  foreach (View::loadMultiple() as $view) {
+    $displays = $view->get('display');
+    foreach (array_keys($displays) as $display_id) {
+      $display =& $view->getDisplay($display_id);
+      unset($display['cache_metadata']['cacheable']);
+    }
+    $view->save();
+  }
+}
+
+/**
+ * @} End of "addtogroup updates-8.0.x-beta".
+ */
