• vue 中监听生命周期事件


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

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

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

    {
      mounted() {
          this.onResize = () => {
            console.log('onResize')
          }
          window.addEventListener('resize', this.onResize)
        },
        beforeDestroy() {
          window.removeEventListener('resize', this.onResize)
        }
    }
    

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

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

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

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

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

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

    再看一个场景:

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

    通常的写法:

    // SonComponent.vue
    {
      mounted() {
        this.$emit('mounted')
      }
    }
    

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

    <template>
      <div>
        <SonComponent @mounted="onMounted" />
      div>
    template>
    
    <script>
      export default {
        methods: {
          onMounted() {
            console.log('onMounted')
          }
        }
      }
    script>
    

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

    上面的方法就无招了。

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

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

    <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

    <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 等。

    export default function MyButton(props, { slots }) {
      function onVnodeMounted(vnode) {
        console.log('vnode mounted', vnode)
      }
      return (
        
      )
    }
    

    在模板中也是能使用这些方法的,但是不推荐,使用 @vue:hook 更直观。

    vue:hookonVnodeXXX 在 vue3 的文档中没有找到,说明这些 API 可能不稳定,不要频繁使用它们。

    通过 vNode 操作 html

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

    animate.css和element-plus的样式冲突
    可以在分页组件挂载和更新后移除 disabled 属性,使用 onVnodeMountedonVnodeUpdated 事件实现。

    
    

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

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

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

    
    <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 作为子组件:

    <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 的父子生命周期执行顺序是:

    组件挂载

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

    挂载阶段

    销毁

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

    销毁阶段

    更新

    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 可能不稳定,不要频繁使用它们。
  • 相关阅读:
    promise.race方式使用
    IDEA快捷键记录
    智能汽车的基础软件「竞速赛」
    elasticsearch,为什么prefix性能消耗很大
    算法设计与分析 SCAU10346 带价值的作业安排问题
    espipe并发编程应用
    竞赛选题 大数据商城人流数据分析与可视化 - python 大数据分析
    不用编程实现USB或串口条码枪对接PLC,将数据写入到寄存器
    MES生产管理系统,你真的需要吗?
    MFC编辑框控件属性和用法
  • 原文地址:https://blog.csdn.net/JackZhouMine/article/details/139888658