diff --git a/docs/rules/README.md b/docs/rules/README.md
index 1fe658682..16e0ef983 100644
--- a/docs/rules/README.md
+++ b/docs/rules/README.md
@@ -42,6 +42,7 @@ Enforce all the rules in this category, as well as all higher priority rules, wi
| [vue/no-arrow-functions-in-watch](./no-arrow-functions-in-watch.md) | disallow using arrow functions to define watcher | |
| [vue/no-async-in-computed-properties](./no-async-in-computed-properties.md) | disallow asynchronous actions in computed properties | |
| [vue/no-deprecated-data-object-declaration](./no-deprecated-data-object-declaration.md) | disallow using deprecated object declaration on data (in Vue.js 3.0.0+) | :wrench: |
+| [vue/no-deprecated-destroyed-lifecycle](./no-deprecated-destroyed-lifecycle.md) | disallow using deprecated `destroyed` and `beforeDestroy` lifecycle hooks (in Vue.js 3.0.0+) | |
| [vue/no-deprecated-dollar-listeners-api](./no-deprecated-dollar-listeners-api.md) | disallow using deprecated `$listeners` (in Vue.js 3.0.0+) | |
| [vue/no-deprecated-dollar-scopedslots-api](./no-deprecated-dollar-scopedslots-api.md) | disallow using deprecated `$scopedSlots` (in Vue.js 3.0.0+) | :wrench: |
| [vue/no-deprecated-events-api](./no-deprecated-events-api.md) | disallow using deprecated events api (in Vue.js 3.0.0+) | |
diff --git a/docs/rules/no-deprecated-destroyed-lifecycle.md b/docs/rules/no-deprecated-destroyed-lifecycle.md
new file mode 100644
index 000000000..f7f7cd8dc
--- /dev/null
+++ b/docs/rules/no-deprecated-destroyed-lifecycle.md
@@ -0,0 +1,43 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/no-deprecated-destroyed-lifecycle
+description: disallow using deprecated `destroyed` and `beforeDestroy` lifecycle hooks (in Vue.js 3.0.0+)
+---
+# vue/no-deprecated-destroyed-lifecycle
+> disallow using deprecated `destroyed` and `beforeDestroy` lifecycle hooks (in Vue.js 3.0.0+)
+
+- :gear: This rule is included in all of `"plugin:vue/vue3-essential"`, `"plugin:vue/vue3-strongly-recommended"` and `"plugin:vue/vue3-recommended"`.
+
+## :book: Rule Details
+
+This rule reports use of deprecated `destroyed` and `beforeDestroy` lifecycle hooks. (in Vue.js 3.0.0+).
+
+
+
+```vue
+
+```
+
+
+
+## :wrench: Options
+
+Nothing.
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-deprecated-destroyed-lifecycle.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-deprecated-destroyed-lifecycle.js)
diff --git a/lib/configs/vue3-essential.js b/lib/configs/vue3-essential.js
index b802623da..432df355b 100644
--- a/lib/configs/vue3-essential.js
+++ b/lib/configs/vue3-essential.js
@@ -10,6 +10,7 @@ module.exports = {
'vue/no-arrow-functions-in-watch': 'error',
'vue/no-async-in-computed-properties': 'error',
'vue/no-deprecated-data-object-declaration': 'error',
+ 'vue/no-deprecated-destroyed-lifecycle': 'error',
'vue/no-deprecated-dollar-listeners-api': 'error',
'vue/no-deprecated-dollar-scopedslots-api': 'error',
'vue/no-deprecated-events-api': 'error',
diff --git a/lib/index.js b/lib/index.js
index f962015c3..7eda5423d 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -51,6 +51,7 @@ module.exports = {
'no-confusing-v-for-v-if': require('./rules/no-confusing-v-for-v-if'),
'no-custom-modifiers-on-v-model': require('./rules/no-custom-modifiers-on-v-model'),
'no-deprecated-data-object-declaration': require('./rules/no-deprecated-data-object-declaration'),
+ 'no-deprecated-destroyed-lifecycle': require('./rules/no-deprecated-destroyed-lifecycle'),
'no-deprecated-dollar-listeners-api': require('./rules/no-deprecated-dollar-listeners-api'),
'no-deprecated-dollar-scopedslots-api': require('./rules/no-deprecated-dollar-scopedslots-api'),
'no-deprecated-events-api': require('./rules/no-deprecated-events-api'),
diff --git a/lib/rules/no-deprecated-destroyed-lifecycle.js b/lib/rules/no-deprecated-destroyed-lifecycle.js
new file mode 100644
index 000000000..c3556084c
--- /dev/null
+++ b/lib/rules/no-deprecated-destroyed-lifecycle.js
@@ -0,0 +1,101 @@
+/**
+ * @author Yosuke Ota
+ * See LICENSE file in root directory for full license.
+ */
+'use strict'
+
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+
+const utils = require('../utils')
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ type: 'problem',
+ docs: {
+ description:
+ 'disallow using deprecated `destroyed` and `beforeDestroy` lifecycle hooks (in Vue.js 3.0.0+)',
+ categories: ['vue3-essential'],
+ url:
+ 'https://eslint.vuejs.org/rules/no-deprecated-destroyed-lifecycle.html'
+ },
+ fixable: null,
+ schema: [],
+ messages: {
+ deprecatedDestroyed:
+ 'The `destroyed` lifecycle hook is deprecated. Use `unmounted` instead.',
+ deprecatedBeforeDestroy:
+ 'The `beforeDestroy` lifecycle hook is deprecated. Use `beforeUnmount` instead.',
+ insteadUnmounted: 'Instead, change to `unmounted`.',
+ insteadBeforeUnmount: 'Instead, change to `beforeUnmount`.'
+ }
+ },
+ /** @param {RuleContext} context */
+ create(context) {
+ return utils.executeOnVue(context, (obj) => {
+ const destroyed = utils.findProperty(obj, 'destroyed')
+
+ if (destroyed) {
+ context.report({
+ node: destroyed.key,
+ messageId: 'deprecatedDestroyed',
+ // I don't know if they have exactly the same function, so don't do autofix.
+ suggest: [
+ {
+ messageId: 'insteadUnmounted',
+ fix(fixer) {
+ return fix(fixer, destroyed, 'unmounted')
+ }
+ }
+ ]
+ })
+ }
+
+ const beforeDestroy = utils.findProperty(obj, 'beforeDestroy')
+ if (beforeDestroy) {
+ context.report({
+ node: beforeDestroy.key,
+ messageId: 'deprecatedBeforeDestroy',
+ // I don't know if they have exactly the same function, so don't do autofix.
+ suggest: [
+ {
+ messageId: 'insteadBeforeUnmount',
+ fix(fixer) {
+ return fix(fixer, beforeDestroy, 'beforeUnmount')
+ }
+ }
+ ]
+ })
+ }
+
+ /**
+ * @param {RuleFixer} fixer
+ * @param {Property} property
+ * @param {string} newName
+ */
+ function fix(fixer, property, newName) {
+ if (property.computed) {
+ if (
+ property.key.type === 'Literal' ||
+ property.key.type === 'TemplateLiteral'
+ ) {
+ return fixer.replaceTextRange(
+ [property.key.range[0] + 1, property.key.range[1] - 1],
+ newName
+ )
+ }
+ return null
+ }
+ if (property.shorthand) {
+ return fixer.insertTextBefore(property.key, `${newName}:`)
+ }
+ return fixer.replaceText(property.key, newName)
+ }
+ })
+ }
+}
diff --git a/tests/lib/rules/no-deprecated-destroyed-lifecycle.js b/tests/lib/rules/no-deprecated-destroyed-lifecycle.js
new file mode 100644
index 000000000..00dda6ed8
--- /dev/null
+++ b/tests/lib/rules/no-deprecated-destroyed-lifecycle.js
@@ -0,0 +1,377 @@
+/**
+ * @author Yosuke Ota
+ * See LICENSE file in root directory for full license.
+ */
+'use strict'
+
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+
+const rule = require('../../../lib/rules/no-deprecated-destroyed-lifecycle')
+
+const RuleTester = require('eslint').RuleTester
+
+// ------------------------------------------------------------------------------
+// Tests
+// ------------------------------------------------------------------------------
+
+const ruleTester = new RuleTester({
+ parser: require.resolve('vue-eslint-parser'),
+ parserOptions: { ecmaVersion: 2020, sourceType: 'module' }
+})
+ruleTester.run('no-deprecated-destroyed-lifecycle', rule, {
+ valid: [
+ {
+ filename: 'test.vue',
+ code: `
+
+ `
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+ `
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+ `
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+ `
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+ `
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+ `
+ }
+ ],
+ invalid: [
+ {
+ filename: 'test.vue',
+ code: `
+
+ `,
+ errors: [
+ {
+ message:
+ 'The `beforeDestroy` lifecycle hook is deprecated. Use `beforeUnmount` instead.',
+ line: 4,
+ suggestions: [
+ {
+ desc: 'Instead, change to `beforeUnmount`.',
+ output: `
+
+ `
+ }
+ ]
+ },
+ {
+ message:
+ 'The `destroyed` lifecycle hook is deprecated. Use `unmounted` instead.',
+ line: 5,
+ suggestions: [
+ {
+ desc: 'Instead, change to `unmounted`.',
+ output: `
+
+ `
+ }
+ ]
+ }
+ ]
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+ `,
+ errors: [
+ {
+ message:
+ 'The `beforeDestroy` lifecycle hook is deprecated. Use `beforeUnmount` instead.',
+ line: 4,
+ suggestions: [
+ {
+ desc: 'Instead, change to `beforeUnmount`.',
+ output: `
+
+ `
+ }
+ ]
+ },
+ {
+ message:
+ 'The `destroyed` lifecycle hook is deprecated. Use `unmounted` instead.',
+ line: 5,
+ suggestions: [
+ {
+ desc: 'Instead, change to `unmounted`.',
+ output: `
+
+ `
+ }
+ ]
+ }
+ ]
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+ `,
+ errors: [
+ {
+ message:
+ 'The `beforeDestroy` lifecycle hook is deprecated. Use `beforeUnmount` instead.',
+ line: 12,
+ suggestions: [
+ {
+ desc: 'Instead, change to `beforeUnmount`.',
+ output: `
+
+ `
+ }
+ ]
+ },
+ {
+ message:
+ 'The `destroyed` lifecycle hook is deprecated. Use `unmounted` instead.',
+ line: 13,
+ suggestions: [
+ {
+ desc: 'Instead, change to `unmounted`.',
+ output: `
+
+ `
+ }
+ ]
+ }
+ ]
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+ `,
+ errors: [
+ {
+ message:
+ 'The `beforeDestroy` lifecycle hook is deprecated. Use `beforeUnmount` instead.',
+ line: 4,
+ suggestions: [
+ {
+ desc: 'Instead, change to `beforeUnmount`.',
+ output: `
+
+ `
+ }
+ ]
+ },
+ {
+ message:
+ 'The `destroyed` lifecycle hook is deprecated. Use `unmounted` instead.',
+ line: 5,
+ suggestions: [
+ {
+ desc: 'Instead, change to `unmounted`.',
+ output: `
+
+ `
+ }
+ ]
+ }
+ ]
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+ `,
+ errors: [
+ {
+ message:
+ 'The `beforeDestroy` lifecycle hook is deprecated. Use `beforeUnmount` instead.',
+ line: 4,
+ suggestions: [
+ {
+ desc: 'Instead, change to `beforeUnmount`.',
+ output: `
+
+ `
+ }
+ ]
+ },
+ {
+ message:
+ 'The `destroyed` lifecycle hook is deprecated. Use `unmounted` instead.',
+ line: 5,
+ suggestions: [
+ {
+ desc: 'Instead, change to `unmounted`.',
+ output: `
+
+ `
+ }
+ ]
+ }
+ ]
+ }
+ ]
+})