vue 中监听生命周期事件

vue 中监听生命周期事件

vue2 提供了一些生命周期事件的方式,在组件销毁后触发一个事件,父组件可监听到该事件,然后执行某些操作。

命名为 hook:hookName ,前面的 hook: 是固定写法,比如 hook:mountedhook:beforeDestroy

常见的添加自定义事件的写法

js 复制代码
{
  mounted() {
      this.onResize = () => {
        console.log('onResize')
      }
      window.addEventListener('resize', this.onResize)
    },
    beforeDestroy() {
      window.removeEventListener('resize', this.onResize)
    }
}

这种写法存在两个小问题:

  1. 添加了额外的变量onResize,感觉有点多余;
  2. 监听 resize 的逻辑分散在不同的生命周期中,不好维护。

使用监听生命周期事件的方式优化:

js 复制代码
{

  mounted() {
    const onResize = () => {
      console.log('onResize')
    }
    window.addEventListener('resize', onResize)
    // hook:lifeHook $once 只执行一次
    this.$once('hook:beforeDestroy', () => {
      window.removeEventListener('resize', onResize)
    })
  }
}

凡在销毁时执行的操作,都可这样优化。

有人说方式1的问题不大,也没有优化多少,似乎没有多少用。

再看一个场景:

希望在子组件挂载时通知父组件

通常的写法:

js 复制代码
// SonComponent.vue
{
  mounted() {
    this.$emit('mounted')
  }
}

在父组件中监听子组件触发的事件:

html 复制代码
<template>
  <div>
    <SonComponent @mounted="onMounted" />
  </div>
</template>

<script>
  export default {
    methods: {
      onMounted() {
        console.log('onMounted')
      }
    }
  }
</script>

问题解决了,但是 SonComponent 是自己写的组件,具有完全的控制权,如果是第三方组件呢?

上面的方法就无招了。

生命周期事件可以解决这个问题:

在模板上监听组件生命周期

html 复制代码
<template>
  <el-input @hook:mounted="onMounted" @hook:beforeDestroy="onBeforeDestroy" />
</template>
<script>
  export default {
    methods: {
      onMounted() {
        console.log('el-input onMounted')
      },
      onBeforeDestroy() {
        console.log('el-input onBeforeDestroy')
      }
    }
  }
</script>

生命周期事件在监听第三方组件的生命周期时很有用。

vue3 生命周期事件吗?

vue3 提供了 vue:hookName 的写法,比如 vue:mountedvue:unmounted

html 复制代码
<script setup>
  import {
    ref
  } from 'vue'

  const input = ref('')

  function whenMounted(params) {
    console.log('whenMounted')
    console.log(params)
  }

  function whenBeforeUnmount() {
    console.log('whenBeforeUnmount')
  }

  function whenUpdated() {
    console.log('whenUpdated')
  }
</script>

<template>
  <el-input v-model="input" @vue:mounted="whenMounted" @vue:beforeUnmount="whenBeforeUnmount" @vue:updated="whenUpdated" />
</template>

this.$oncethis.$on 不再支持,可使用第三方库解决。
这些生命周期事件抛出的数据是组件的 vNode 对象,通过它,可获取到 elpropsref 等组件的重要属性。
比如,可获取 el ,然后手动修改它,以达到某种目的。

jsx 中如何监听生命周期事件呢?

vue3 提供了 onVnodeHookName 方法,可以监听组件的生命周期事件。

onVnodeMountedonVnodeBeforeUnmountonVnodeUpdated 等。

jsx 复制代码
export default function MyButton(props, { slots }) {
  function onVnodeMounted(vnode) {
    console.log('vnode mounted', vnode)
  }
  return (
    <button onVnodeMounted={onVnodeMounted}>
      {{
        default: () => slots.default?.() ?? '按钮',
      }}
    </button>
  )
}

在模板中也是能使用这些方法的,但是不推荐,使用 @vue:hook 更直观。
vue:hookonVnodeXXX 在 vue3 的文档中没有找到,说明这些 API 可能不稳定,不要频繁使用它们。

通过 vNode 操作 html

项目中 animate.css 和 element-plus 一起使用,animate 的 [disabled] 样式属性权重太高,导致 element-plus 分页有禁用样式。

可以在分页组件挂载和更新后移除 disabled 属性,使用 onVnodeMountedonVnodeUpdated 事件实现。

jsx 复制代码
<ElPagination 
    onVnodeMounted={removePaginationDisabled} 
    onVnodeUpdated={removePaginationDisabled} />

removePaginationDisabled 的参数是一个 vNode 对象,可通过 el 属性拿到 html 模板,然后手动修改它。

vue3 父子组件的生命周期的执行顺序是怎样的?

通过两个组件,可以观察到:

html 复制代码
<!-- Parent.vue -->
<script setup>
  import {
    onBeforeMount,
    onBeforeUnmount,
    onBeforeUpdate,
    onMounted,
    onUnmounted,
    onUpdated,
    ref,
  } from 'vue'

  import MyInput from './MyInput.vue'

  const myInput = ref('')

  function whenBeforeMount() {
    console.log('Son vue:beforeMount')
  }

  function whenMounted() {
    console.log('Son vue:mounted')
  }

  function whenBeforeUpdate() {
    console.log('Son vue:beforeUpdate')
  }

  function whenUpdated(vNode) {
    console.log('Son vue:updated')
    console.log(vNode)
  }

  function whenBeforeUnmount() {
    console.log('Son vue:beforeUnmount')
  }

  function whenUnmounted() {
    console.log('Son vue:unmounted')
  }

  console.log('Parent setup')

  onBeforeMount(() => {
    console.log('Parent onBeforeMount')
  })

  onMounted(() => {
    console.log('Parent onMounted')
  })
  onBeforeUpdate(() => {
    console.log('Parent onBeforeUpdate')
  })
  onUpdated(() => {
    console.log('Parent onUpdated')
  })
  onBeforeUnmount(() => {
    console.log('Parent onBeforeUnmount')
  })
  onUnmounted(() => {
    console.log('Parent onUnmounted')
  })
</script>

<template>
  <div class="init-options">
    <p>this is p tag {{ myInput }}</p>
    <MyInput v-model="myInput" @vue:beforeMount="whenBeforeMount" @vue:mounted="whenMounted" @vue:beforeUpdate="whenBeforeUpdate" @vue:updated="whenUpdated" @vue:beforeUnmount="whenBeforeUnmount" @vue:unmounted="whenUnmounted" />
  </div>
</template>

MyInput.vue 作为子组件:

html 复制代码
<script setup>
  import {
    onBeforeMount,
    onBeforeUnmount,
    onBeforeUpdate,
    onMounted,
    onUnmounted,
    onUpdated,
    ref,
  } from 'vue'

  console.log('Son setup')
  const props = defineProps({
    modelValue: {
      type: String,
      default: '',
    },
  })

  const emits = defineEmits(['update:modelValue'])

  const input = ref(props.modelValue)

  onBeforeMount(() => {
    console.log('Son onBeforeMount')
  })
  onMounted(() => {
    console.log('Son onMounted')
  })

  onBeforeUpdate(() => {
    console.log('Son onBeforeUpdate')
  })

  onUpdated(() => {
    console.log('Son updated')
  })

  onBeforeUnmount(() => {
    console.log('Son onBeforeUnmount')
  })

  onUnmounted(() => {
    console.log('Son onUnmounted')
  })

  function onInput(e) {
    input.value = e.target.value
    emits('update:modelValue', e.target.value)
  }
</script>

<template>
  <div>
    <input :value="input" @input="onInput" />
    <p>{{ input }}</p>
  </div>
</template>

vue3 的父子生命周期执行顺序是:

组件挂载

bash 复制代码
parent setup
parent onBeforeMount
parent vue:beforeMount
   son setup
   son onBeforeMount
   son vue:beforeMount
   son onMounted
   son vue:mounted
parent onMounted
# 类似两个圈,或者洋葱模型
# 父组件挂载阶段先进入后挂载

销毁

bash 复制代码
Parent onBeforeUnmount
   Son vue:beforeUnmount # !生命周期事件先触发
   Son onBeforeUnmount
   Son onUnmounted
   Son vue:unmounted # ! 生命周期事件后触发
Parent onUnmounted
# 也类似洋葱模型

更新

bash 复制代码
Parent onBeforeUpdate
   Son onBeforeUpdate
   Son vue:beforeUpdate
   Son updated
   Son vue:updated
Parent onUpdated
# 也类似两个圈 父组件先进入更新阶段,后更新

规律:父子组件的生命周期执行顺序类似一个两个圈,setup beforeXXX 在前方,XXXed 在后方,子组件的圈在内部。
生命周期事件在相应的生命周期钩子执行后触发,但是 vue:beforeUnmount 先于 onBeforeUnmount 执行,这点特殊。

小结

  • vue2 监听生命周期事件的方式:@hook:hookName
  • vue3 的方式 @vue:hookName或者onVnodeHookName
  • vue3 的父子组件每个阶段的生命周期执行顺序是两个圈,setup onBeforeXXX 在前方,XXXed 在后方,子组件的圈在内部。
  • vue:hookName 在 vue3 的文档中没有找到,说明这些 API 可能不稳定,不要频繁使用它们。
相关推荐
To_OC16 分钟前
LC 17 电话号码的字母组合:我的回溯算法,就是从这道题开窍的
javascript·算法·leetcode
vipbic24 分钟前
中后台越做越乱后,我用插件化把它救回来了
前端·vue.js
Hyyy30 分钟前
Computer Use 适合做什么,不适合做什么——一次真实使用后的思考
前端
小和尚同志1 小时前
前端 AI 单元测试思考与落地
前端·人工智能·aigc
invicinble2 小时前
c端系统,其实更像一个信息展示平台
前端
你怎么知道我是队长2 小时前
JavaScript的变量和数据类型介绍
开发语言·javascript·ecmascript
李姆斯3 小时前
管理是否可以被完全量化
前端·产品经理·团队管理
名字还没想好☜3 小时前
Next.js 中间件实战:鉴权、重定向与 A/B 分流
开发语言·前端·javascript·中间件·react·next.js
广州灵眸科技有限公司4 小时前
瑞芯微RV1126B开发板(EASY-EAI-PI2) INI文件操作
java·前端·javascript·网络·人工智能
心中有国也有家4 小时前
AtomGit Flutter 鸿蒙客户端:ModalBottomSheet 实战
android·javascript·学习·flutter·华为·harmonyos