Vue 生命周期钩子

'beforeCreate' 钩子

beforeCreate 生命周期钩子发生在组件初始化之前,因此这是在 Vue 设置组件的数据、计算属性、方法和事件侦听器之前。

beforeCreate 钩子可用于设置全局事件监听器,但我们应该避免尝试从 beforeCreate 生命周期钩子访问属于组件的元素,例如数据、观察者和方法,因为它们不是 在此阶段尚未创建。

此外,尝试从 beforeCreate 生命周期钩子访问 DOM 元素是没有意义的,因为它们是在组件 mounted 之后才创建的。

示例

CompOne.vue:

<template>
    <h2>组件</h2>
    <p>这是一个组件</p>
    <p id="pResult">{{ text }}</p>
</template>

<script>
export default {
	data() {
		return {
			text: '...'
		}
	},
  beforeCreate() {
		this.text = 'initial text'; // 该行没有任何作用
    console.log("beforeCreate: The component is not created yet.");
  }
}
</script>

App.vue:

<template>
  <h1>The 'beforeCreate' 生命周期钩子</h1>
  <p>我们可以从"beforeCreate"生命周期钩子中看到 console.log() 消息,但是我们尝试对 Vue 数据属性进行的文本更改没有效果,因为 Vue 数据属性尚未创建。</p>
  <button @click="this.activeComp = !this.activeComp">Add/Remove Component</button>
  <div>
    <comp-one v-if="activeComp"></comp-one>
  </div>
</template>

<script>
export default {
  data() {
    return {
      activeComp: false
    }
  }
}
</script>

<style>
#app > div {
  border: dashed black 1px;
  border-radius: 10px;
  padding: 10px;
  margin-top: 10px;
  background-color: lightgreen;
}
#pResult {
  background-color: lightcoral;
  display: inline-block;
}
</style>
运行示例 »

在上面的示例中,CompOne.vue 中的第 15 行没有任何效果,因为在该行中我们尝试更改 Vue 数据属性内的文本,但 Vue 数据属性实际上尚未创建。 另外,请记住打开浏览器控制台以查看第 16 行的 console.log() 调用的结果。


'created' 钩子

created 生命周期钩子在组件初始化后发生,因此 Vue 已经设置了组件的数据、计算属性、方法和事件侦听器。

我们应该避免尝试从 created 生命周期钩子访问 DOM 元素,因为在组件为 mounted 之前,无法访问 DOM 元素。

创建的生命周期钩子可用于通过 HTTP 请求获取数据,或设置初始数据值。 就像下面的例子一样,data 属性"text"被赋予一个初始值:

示例

CompOne.vue:

<template>
    <h2>组件</h2>
    <p>这是一个组件</p>
    <p id="pResult">{{ text }}</p>
</template>

<script>
export default {
	data() {
		return {
			text: '...'
		}
	},
  created() {
		this.text = 'initial text';
    console.log("created: The component just got created.");
  }
}
</script>

App.vue:

<template>
  <h1>'created' 生命周期钩子</h1>
  <p>我们可以从 'created' 生命周期钩子中看到 console.log() 消息,并且我们尝试对 Vue 数据属性进行的文本更改有效,因为 Vue data 属性已经在此阶段创建。</p>
  <button @click="this.activeComp = !this.activeComp">Add/Remove Component</button>
  <div>
    <comp-one v-if="activeComp"></comp-one>
  </div>
</template>

<script>
export default {
  data() {
    return {
      activeComp: false
    }
  }
}
</script>

<style>
#app > div {
  border: dashed black 1px;
  border-radius: 10px;
  padding: 10px;
  margin-top: 10px;
  background-color: lightgreen;
}
#pResult {
  background-color: lightcoral;
  display: inline-block;
}
</style>
运行示例 »

'beforeMount' 钩子

beforeMount 生命周期钩子发生在组件mounted之前,也就是将组件添加到 DOM 之前。

我们应该避免尝试从 beforeMount 生命周期钩子访问 DOM 元素,因为在组件安装之前,无法访问 DOM 元素。

下面的示例显示我们还无法访问组件中的 DOM 元素,CompOne.vue 中的第 11 行不起作用,并在浏览器控制台中生成错误:

示例

CompOne.vue:

<template>
    <h2>组件</h2>
    <p>这是一个组件</p>
    <p ref="pEl" id="pEl">We try to access this text from the 'beforeMount' hook.</p>
</template>

<script>
export default {
  beforeMount() {
    console.log("beforeMount: This is just before the component is mounted.");
    this.$refs.pEl.innerHTML = "Hello World!"; // <-- We cannot reach the 'pEl' DOM element at this stage 
  }
}
</script>

App.vue:

<template>
  <h1>'beforeMount' 生命周期钩子</h1>
  <p>我们可以从 'beforeMount' 生命周期钩子中看到 console.log() 消息,但是我们尝试对 'pEl' 段落 DOM 元素进行的文本更改不起作用,因为"pEl"段落 DOM 元素在此阶段还不存在。</p>
  <button @click="this.activeComp = !this.activeComp">Add/Remove Component</button>
  <div>
    <comp-one v-if="activeComp"></comp-one>
  </div>
</template>

<script>
export default {
  data() {
    return {
      activeComp: false
    }
  }
}
</script>

<style>
#app > div {
  border: dashed black 1px;
  border-radius: 10px;
  padding: 10px;
  margin-top: 10px;
  background-color: lightgreen;
}
#pEl {
  background-color: lightcoral;
  display: inline-block;
}
</style>
运行示例 »

'mounted' 钩子

将组件添加到 DOM 树后,立即调用 mounted() 函数,我们可以将代码添加到该阶段。

这是我们第一次有机会执行与属于该组件的 DOM 元素相关的操作,例如使用 ref 属性和 $refs 对象,就像我们在下面的第二个示例中所做的那样。

示例

CompOne.vue:

<template>
  <h2>组件</h2>
  <p>将此组件添加到 DOM 后,立即调用 Mounted() 函数,我们可以向该 Mounted() 函数添加代码。 在本例中,该组件安装后会出现一个警告弹出框。</p>
  <p><strong>注意:</strong> 警报在组件可见之前可见的原因是因为警报是在浏览器将组件渲染到屏幕之前调用的。</p>
</template>

<script>
export default {
  mounted() {
    alert("The component is mounted!");
  }
}
</script>

App.vue:

<template>
  <h1>The 'mounted' 生命周期钩子</h1>
  <button @click="this.activeComp = !this.activeComp">Create component</button>
  <div>
    <comp-one v-if="activeComp"></comp-one>
  </div>
</template>

<script>
export default {
  data() {
    return {
      activeComp: false
    }
  }
}
</script>

<style scoped>
  div {
    border: dashed black 1px;
    border-radius: 10px;
    padding: 20px;
    margin: 10px;
    width: 400px;
    background-color: lightgreen;
  }
</style>
运行示例 »

注意:mounted 阶段发生在组件添加到 DOM 之后,但在上面的示例中,alert() 在我们看到组件之前就可见。 原因是,首先将组件添加到 DOM,但在浏览器将组件渲染到屏幕之前,会发生 mounted 阶段,并且 alert() 变得可见并暂停浏览器渲染组件。

下面是一个可能更有用的示例:在安装表单组件后将光标放在输入字段内,以便用户可以开始输入。

示例

CompOne.vue:

<template>
  <h2>form 表单组件</h2>
  <p>当这个组件被添加到 DOM 树时,mounted() 函数被调用,我们将光标放在输入元素内。</p>
  <form @submit.prevent>
    <label>
      <p>
        Name: <br>
        <input type="text" ref="inpName">
      </p>
    </label>
    <label>
      <p>
        Age: <br>
        <input type="number">
      </p>
    </label>
    <button>Submit</button>
  </form>
  <p>(这种形式不起作用,它只是在这里显示挂载的生命周期钩子。)</p>
</template>

<script>
  export default {
    mounted() {
      this.$refs.inpName.focus();
    }
  }
</script>
运行示例 »

'beforeUpdate' 钩子

只要组件的数据发生更改,但在更新呈现到屏幕上之前,就会调用 beforeUpdate 生命周期钩子。 beforeUpdate 生命周期钩子发生在updated 生命周期钩子之前。

beforeUpdate 钩子的特殊之处在于,我们可以对应用程序进行更改,而不会触发新的更新,因此我们可以避免无限循环。 这就是不在 updated 生命周期钩子中对应用程序进行更改的原因,因为使用该钩子将创建无限循环。 请看下面的第三个示例(红色)。

示例

beforeUpdate() 函数向文档添加 <li> 标签,以指示 beforeUpdate() 函数已运行。

CompOne.vue:

<template>
  <h2>组件</h2>
  <p>这是一个组件</p>
</template>

App.vue:

<template>
  <h1>'beforeUpdate' 生命周期钩子</h1>
  <p>每当我们的页面发生更改时,应用程序都会"updated",并且"beforeUpdate"钩子会在此之前发生。</p>
  <p>像我们在这里所做的那样,在"beforeUpdate"钩子中修改页面是安全的,但是如果我们在"updated"钩子中修改页面,我们将生成无限循环。</p>
  <button @click="this.activeComp = !this.activeComp">Add/Remove Component</button>
  <div>
    <comp-one v-if="activeComp"></comp-one>
  </div>
  <ol ref="divLog"></ol>
</template>

<script>
export default {
  data() {
    return {
      activeComp: true
    }
  },
  beforeUpdate() {
    this.$refs.divLog.innerHTML += "<li>beforeUpdate: This happened just before the 'updated' hook.</li>";
  }
}
</script>

<style>
#app > div {
  border: dashed black 1px;
  border-radius: 10px;
  padding: 10px;
  margin-top: 10px;
  background-color: lightgreen;
}
</style>
运行示例 »

'updated' 钩子

updated 生命周期钩子在我们的组件更新其 DOM 树后被调用。

示例

updated() 函数使用 console.log() 写入一条消息。 每当页面更新时都会发生这种情况,在本例中是每次添加或删除组件时。

CompOne.vue:

<template>
  <h2>组件</h2>
  <p>这是一个组件</p>
</template>

App.vue:

<template>
  <h1>'updated' 生命周期钩子</h1>
  <p>每当我们的页面发生变化时,应用程序就会更新并调用updated()函数。 在此示例中,我们在更新应用程序时运行的updated() 函数中使用console.log()。</p>
  <button @click="this.activeComp = !this.activeComp">Add/Remove Component</button>
  <div>
    <comp-one v-if="activeComp"></comp-one>
  </div>
</template>

<script>
export default {
  data() {
    return {
      activeComp: true
    }
  },
  updated() {
    console.log("The component is updated!");
  }
}
</script>

<style>
#app {
  max-width: 450px;
}
#app > div {
  border: dashed black 1px;
  border-radius: 10px;
  padding: 10px;
  margin-top: 10px;
  width: 80%;
  background-color: lightgreen;
}
</style>
运行示例 »

点击"添加/删除组件"按钮10次后,我们可以在浏览器控制台中看到结果:

console screenshot

注意:我们必须小心,当调用 updated 生命周期钩子时,不要修改页面本身,因为这样页面会一次又一次更新 ,创建无限循环。

让我们尝试一下,如果我们完全按照上面警告我们的注释去做,会发生什么。 页面会无限期更新吗?:

示例

updated() 函数将文本添加到段落中,从而再次更新页面,并且该函数在无限循环中一次又一次地运行。 幸运的是,您的浏览器最终会停止这个循环。

CompOne.vue:

<template>
  <h2>组件</h2>
  <p>这是一个组件</p>
</template>

App.vue:

<template>
  <h1>'updated' 生命周期钩子</h1>
  <p>每当我们的页面发生变化时,应用程序就会更新并调用updated()函数。</p>
  <p>导致调用更新的钩子的第一个更改是当我们通过单击按钮删除组件时。 发生这种情况时, update() 函数会向最后一段添加文本,从而一次又一次地更新页面。</p>
  <button @click="this.activeComp = !this.activeComp">Add/Remove Component</button>
  <div>
    <comp-one v-if="activeComp"></comp-one>
  </div>
  <div>{{ text }}</div>
</template>

<script>
export default {
  data() {
    return {
      activeComp: true,
      text: "Hello, "
    }
  },
  updated() {
    this.text += "hi, ";
  }
}
</script>

<style>
#app {
  max-width: 450px;
}
#app > div {
  border: dashed black 1px;
  border-radius: 10px;
  padding: 10px;
  margin-top: 10px;
  width: 80%;
  background-color: lightgreen;
}
</style>
运行示例 »

在开发模式下在本地计算机上运行上述代码时,Chrome 浏览器控制台警告如下所示:

screenshot browser console warning

'beforeUnmount' 钩子

beforeUnmount 生命周期钩子在组件从 DOM 中删除之前被调用。

正如我们在下面的示例中看到的,我们仍然可以在 beforeUnmount 钩子中访问 DOM 中的组件元素。

示例

CompOne.vue:

<template>
  <h2>组件</h2>
  <p ref="pEl">Strawberries!</p>
</template>
  
<script>
export default {
  beforeUnmount() {
    alert("beforeUnmount: The text inside the p-tag is: " + this.$refs.pEl.innerHTML);
  }
}
</script>

App.vue:

<template>
  <h1>生命周期钩子</h1>
  <button @click="this.activeComp = !this.activeComp">{{ btnText }}</button>
  <div>
    <comp-one v-if="activeComp"></comp-one>
  </div>
</template>

<script>
export default {
  data() {
    return {
      activeComp: true
    }
  },
  computed: {
    btnText() {
      if(this.activeComp) {
        return 'Remove component'
      }
      else {
        return 'Add component'
      }
    }
  }
}
</script>

<style scoped>
  div {
    border: dashed black 1px;
    border-radius: 10px;
    padding: 20px;
    margin: 10px;
    width: 400px;
    background-color: lightgreen;
  }
</style>
运行示例 »

'unmounted' 钩子

从 DOM 中删除组件后,将调用 unmounted 生命周期钩子。

例如,此钩子可用于删除事件侦听器或取消计时器或间隔。

当组件为 unmounted 时,会调用unmounted()函数,我们可以向其中添加我们的代码:

示例

CompOne.vue:

<template>
  <h2>组件</h2>
  <p>当该组件从 DOM 树中删除时,会调用 unmounted() 函数,我们可以向该函数添加代码。 在此示例中,当该组件被删除时,我们创建一个警报弹出框。</p>
</template>

<script>
export default {
  unmounted() {
    alert("The component is removed (unmounted)!");
  }
}
</script>

App.vue:

<template>
  <h1>生命周期钩子</h1>
  <button @click="this.activeComp = !this.activeComp">{{ btnText }}</button>
  <div>
    <comp-one v-if="activeComp"></comp-one>
  </div>
</template>

<script>
export default {
  data() {
    return {
      activeComp: true
    }
  },
  computed: {
    btnText() {
      if(this.activeComp) {
        return 'Remove component'
      }
      else {
        return 'Add component'
      }
    }
  }
}
</script>

<style scoped>
  div {
    border: dashed black 1px;
    border-radius: 10px;
    padding: 20px;
    margin: 10px;
    width: 400px;
    background-color: lightgreen;
  }
</style>
运行示例 »

注意:unmounted 阶段发生在组件从 DOM 中删除之后,但在上面的示例中,alert() 在组件消失之前可见。 原因是首先从 DOM 中删除组件,但在浏览器将组件的删除呈现到屏幕上之前,会发生 unmounted 阶段,alert() 变得可见,并暂停浏览器以可见方式删除组件。


'errorCaptured' 钩子

当子/后代组件中发生错误时,将调用 errorCaptured 生命周期钩子。

该钩子可用于错误处理、记录或向用户显示错误。

示例

CompOne.vue:

<template>
  <h2>组件</h2>
  <p>这是一个组件</p>
  <button @click="generateError">Generate Error</button>
</template>

<script>
export default {
  methods: {
    generateError() {
      this.$refs.objEl.innerHTML = "hi";
    }
  }
}
</script>

App.vue:

<template>
  <h1>'errorCaptured' 生命周期钩子</h1>
  <p>每当子组件出现错误时,都会在父组件上调用 errorCaptured() 函数。</p>
  <p>当单击组件内的按钮时,将运行一个方法,尝试对不存在的 $refs 对象进行更改。 这会在组件中创建一个错误,触发父级中的"errorCaptured"生命周期钩子,并显示一个警告框,其中包含有关该错误的信息。</p>
  <p>单击警报框中的 "Ok" 后,您可以在浏览器控制台中看到错误。</p>
  <div>
    <comp-one></comp-one>
  </div>
</template>

<script>
export default {
  errorCaptured() {
    alert("An error occurred");
  }
}
</script>

<style>
#app > div {
  border: dashed black 1px;
  border-radius: 10px;
  padding: 10px;
  margin-top: 10px;
  background-color: lightgreen;
}
</style>
运行示例 »

有关错误的信息也可以作为参数捕获到 errorCaptured() 函数,这些参数是:

  1. 错误
  2. 触发错误的组件
  3. 错误源类型

在下面的示例中,这些参数在 errorCaptured() 函数中捕获并写入控制台:

示例

CompOne.vue:

<template>
  <h2>组件</h2>
  <p>这是一个组件</p>
  <button @click="generateError">Generate Error</button>
</template>

<script>
export default {
  methods: {
    generateError() {
      this.$refs.objEl.innerHTML = "hi";
    }
  }
}
</script>

App.vue:

<template>
  <h1>'errorCaptured' 生命周期钩子</h1>
  <p>每当子组件出现错误时,都会在父组件上调用 errorCaptured() 函数。</p>
  <p>打开浏览器控制台查看捕获的错误详细信息。</p>
  <div>
    <comp-one></comp-one>
  </div>
</template>

<script>
export default {
  errorCaptured(error,compInst,errorInfo) {
    console.log("error: ", error);
    console.log("compInst: ", compInst);
    console.log("errorInfo: ", errorInfo);
  }
}
</script>

<style>
#app > div {
  border: dashed black 1px;
  border-radius: 10px;
  padding: 10px;
  margin-top: 10px;
  background-color: lightgreen;
}
</style>
运行示例 »

"renderTracked"和"renderTriggered"生命周期钩子

当渲染函数设置为跟踪或监视反应式组件时,renderTracked 钩子就会运行。 renderTracked 钩子通常在反应式组件初始化时运行。

当此类跟踪的反应式组件发生变化时,renderTriggered 钩子就会运行,因此会触发新的渲染,以便屏幕会更新为最新的更改。

响应式组件是可以更改的组件。

渲染函数是由 Vue 编译的函数,用于跟踪反应式组件。 当反应性组件发生变化时,渲染函数被触发并将应用程序重新渲染到屏幕上。

renderTrackedrenderTriggered 钩子旨在用于调试,并且仅在开发模式下可用。

要从 renderTrackedrenderTriggered 钩子查看 alert()console.log(),您必须将以下示例中的代码复制到您的计算机并在开发模式下运行应用程序。

示例

CompOne.vue:

<template>
  <h2>第一个组件</h2>
  <p>这是一个组件。</p>
  <button @click="counter++">Add One</button>
  <p>{{ counter }}</p>
</template>
  
<script>
export default {
  data() {
    return {
      counter: 0
    }
  },
  renderTracked(evt) {
    console.log("renderTracked: ",evt);
    alert("renderTracked");
  },
  renderTriggered(evt) {
    console.log("renderTriggered: ",evt)
    alert("renderTriggered");
  }
}
</script>

App.vue:

<template>
  <h1>'renderTracked' 和 'renderTriggered' 生命周期钩子</h1>
  <p>'renderTracked' 和 'renderTriggered' 生命周期钩子用于调试。</p>
  <p><mark>此示例仅在开发模式下有效,因此要查看钩子运行,您必须复制此代码并在您自己的计算机上以开发模式运行它。</mark></p>
  <div>
    <comp-one></comp-one>
  </div>
</template>

<style scoped>
  div {
    border: dashed black 1px;
    border-radius: 10px;
    padding: 20px;
    margin-top: 10px;
    background-color: lightgreen;
  }
</style>
运行示例 »

注意:上例中的代码旨在以开发模式复制并在您的计算机上本地运行,因为 renderTrackedrenderTriggered 钩子仅在开发模式下工作。


'activated' 和 'deactivated' 生命周期钩子

正如我们在此页面上看到的,当组件被删除或添加到 DOM 时,我们有 mountedunmounted 生命周期钩子。

activateddeactivated 生命周期钩子适用于添加或删除缓存的动态组件,但不适用于 DOM。 下面的示例中使用 <KeepAlive> 标签来缓存动态组件。

示例

CompOne.vue:

<template>
  <h2>组件</h2>
  <p>下面是每次"mounted"或"activated"钩子运行时的日志。</p>
  <ol ref="olEl"></ol>
  <p>您还可以看到这些钩子何时在控制台中运行。</p>
</template>
  
<script>
export default {
  mounted() {
    console.log("mounted");
    const liEl = document.createElement("li");
    liEl.innerHTML = "mounted";
    this.$refs.olEl.appendChild(liEl);
  },
  activated() {
    console.log("activated");
    const liEl = document.createElement("li");
    liEl.innerHTML = "activated";
    this.$refs.olEl.appendChild(liEl);
  }
}
</script>

<style>
  li {
    background-color: lightcoral;
    width: 5em;
  }
</style>

App.vue:

<template>
  <h1>'activated' 生命周期钩子</h1>
  <p>在这个"activated"钩子示例中,我们检查组件是否使用 <KeepAlive> 正确缓存。</p>
  <p>如果使用 <KeepAlive> 正确缓存了组件,我们期望 'mounted' 钩子在第一次包含组件时运行(必须第一次添加到 DOM),并且我们期望 'activated' 钩子在每次包含组件时运行( 也是第一次)。</p>
  <button @click="this.activeComp = !this.activeComp">Include component</button>
  <div>
    <KeepAlive>
      <comp-one v-if="activeComp"></comp-one>
    </KeepAlive>
  </div>
</template>

<script>
export default {
  data() {
    return {
      activeComp: false
    }
  }
}
</script>

<style scoped>
  div {
    border: dashed black 1px;
    border-radius: 10px;
    padding: 20px;
    margin-top: 10px;
    background-color: lightgreen;
  }
</style>
运行示例 »

让我们扩展上面的示例,看看 activateddeactivated 钩子如何工作。 我们还使用 mountedunmounted 钩子,这样我们就可以看到 mounted 钩子在第一次添加缓存组件时运行,而 unmounted 钩子永远不会为缓存组件运行。

示例

CompOne.vue:

<template>
  <h2>组件</h2>
  <p>下面是每次"activated"、"deactivated"、"mounted"或"unmounted"钩子运行时的日志。</p>
  <ol ref="olEl"></ol>
  <p>You can also see when these hooks run in the console.</p>
</template>
  
<script>
export default {
  mounted() {
    this.logHook("mounted");
  },
  unmounted() {
    this.logHook("unmounted");
  },
  activated() {
    this.logHook("activated");
  },
  deactivated() {
    this.logHook("deactivated");
  },
  methods: {
    logHook(hookName) {
      console.log(hookName);
      const liEl = document.createElement("li");
      liEl.innerHTML = hookName;
      this.$refs.olEl.appendChild(liEl);
    }
  }
}
</script>

<style>
  li {
    background-color: lightcoral;
    width: 5em;
  }
</style>

App.vue:

<template>
  <h1>'activated' 和 'deactivated' 生命周期钩子</h1>
  <p>在这个"activated"和"deactivated"钩子的示例中,我们还看到"mounted"和"unmounted"钩子何时以及是否运行。</p>
  <button @click="this.activeComp = !this.activeComp">Include component</button>
  <div>
    <KeepAlive>
      <comp-one v-if="activeComp"></comp-one>
    </KeepAlive>
  </div>
</template>

<script>
export default {
  data() {
    return {
      activeComp: false
    }
  }
}
</script>

<style scoped>
  div {
    border: dashed black 1px;
    border-radius: 10px;
    padding: 20px;
    margin-top: 10px;
    background-color: lightgreen;
  }
</style>
运行示例 »

serverPrefetch 生命周期钩子

"serverPrefetch" 钩子仅在服务器端渲染 (SSR) 期间调用。

解释和创建 "serverPrefetch" 钩子的示例需要相对较长的介绍和设置,这超出了本教程的范围。


Vue 练习

通过练习测试自己

练习题:

 生命周期钩子在组件从 DOM 中删除之前调用。

开始练习