programing

Vue 라이프 사이클 리스너를 동적으로 추가하는 방법

prostudy 2022. 6. 12. 11:57
반응형

Vue 라이프 사이클 리스너를 동적으로 추가하는 방법

vue 컴포넌트가 파괴되었을 때 호출할 수 있어야 하는 특정 함수는 있지만 이벤트가 생성되기 전에 반드시 무엇인지 알 수는 없습니다.

vue 라이프 사이클 이벤트에 청취자를 동적으로 추가하는 방법이 있습니까?

달성하려고 하는 것:

...methods: {
    logOnDestroy(txt) {
        this.$on('beforeDestroy', () => {
            console.log(txt)
        }
    }
}

그러나 그것은 현재 호출되지 않았다.런타임에 청취자를 컴포넌트 라이프 사이클이벤트에 프로그램적으로 바인드하는 다른 방법이 있습니까?

좀 더 간단하게 할 수 없냐고 물으실 수도 있습니다.

Vue.js 컴포넌트 훅을 이벤트로 하면 다음과 같은 구문을 찾을 수 있습니다.

this.$once('hook:beforeDestroy', () => {

으로 역동적으로 '동적'으로 .logOnDestroy()앱인 Vue CLI의 HelloWorld의 메서드,

데모

Vue.component('helloworld', {
  template: '<h1>{{ msg }}</h1>',
  name: 'helloworld',
  props: { msg: String },
  mounted() {
    this.logOnDestroy('Goodbye HelloWorld')
  },
  methods: {
    logOnDestroy(txt) {
      this.$once('hook:beforeDestroy', () => {
        console.log(txt)
      })
    }    
  }
});

new Vue({
  el: '#app',
  data: {
    showHello: true
  },
  mounted() {
    setTimeout(() => {
      this.showHello = false
    }, 3000)
  }
});
Vue.config.productionTip = false
Vue.config.devtools = false
<script src="https://unpkg.com/vue"></script>
<div id="app">
  <img alt="Vue logo" src="https://upload.wikimedia.org/wikipedia/commons/thumb/9/95/Vue.js_Logo_2.svg/277px-Vue.js_Logo_2.svg.png" style="height: 50px;">
  <helloworld v-if="showHello" msg="Welcome to Your Vue.js App"/>
</div>

은 에 저장됩니다.this.$options 않은 ).대응하는 어레이에 푸시 하는 것으로, 핸들러를 추가할 수 있습니다(핸들러가 설정되어 있지 않은 경우는, 우선 어레이를 작성할 필요가 있습니다).

new Vue({
  el: '#app',
  created() {
    if (!this.$options.mounted) {
      this.$options.mounted = [];
    }
  
    this.$options.mounted.push(() => {
      console.log('mounted')
    });
    
    this.$options.mounted.push(() => {
      console.log('also mounted')
    });
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.min.js"></script>

<div id="app">
  <div></div>
</div>

고객님의 경우:

methods: {
  logOnDestroy(txt) {
    if (!this.$options.beforeDestroy) {
      this.$options.beforeDestroy = [];
    }

    this.$options.beforeDestroy.push(() => {
      console.log(txt)
    });
  }
}

2019년 1월 - 이 질문에 대한 답변에 대한 경고 - 이 코드를 사용하여 동적 청취자를 추가할 때 미묘한 문제가 발견되었습니다.

this.$options.beforeDestroy.push(() => {
  console.log(txt)
});

정적인 합니다.beforeDestroy정의되어 있습니다.은 "Direct " (입니다.$options.

, 스태틱을 beforeDestroy은 「」, 「」의 입니다.$options.__proto__이는 컴포넌트의 여러 인스턴스가 이전 인스턴스의 다이내믹핸들러를 상속받는 것을 의미합니다(실제로 위의 코드는 연속 인스턴스 작성에 사용되는 템플릿을 변경합니다).

이게 얼마나 현실적인 문제인지 잘 모르겠어요.앱을 탐색할수록 핸들러 배열이 커지기 때문에 상태가 좋지 않습니다(예: 매번 페이지 전환에 새로운 기능이 추가됨).


은 이 핸들러를 입니다.injectHook핫모듈 입니다(Vue는 해 주십시오).index.js"Vue" (Vue CLI 3을 하고 있습니다.[ Vue CLI 3 ]를 선택합니다.

function injectHook(options, name, hook) {
  var existing = options[name]
  options[name] = existing
    ? Array.isArray(existing) ? existing.concat(hook) : [existing, hook]
    : [hook]
}
...
injectHook(this.$options, 'beforeDestroy', myHandler)

여기서 발생하는 것은 새로운 어레이가 인스턴스 상에 생성되고 이 인스턴스에는 모든 핸들러가 포함됩니다.__proto__ 것 , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , .되지 않은)되므로, 에 있는 .__proto__핸들러 어레이

한 가지 간단한 해결책은 컴포넌트 내부에 추가하는 모든 다이내믹핸들러를 간단하게 추적하는 것입니다.

Vue.component('foo', {
  template: '<div>foo component</div>',
  data() {
    return {
      beforeDestroyHandlers: []
    }
  },
  created() {
    this.beforeDestroyHandlers.push(() => {
      console.log('new handler called');
    });
  },
  beforeDestroy() {
    this.beforeDestroyHandlers.forEach(handler => handler());
  }
});

new Vue({
  el: '#app',
  data: {
    includeComponent: false
  }
});
<script src="https://unpkg.com/vue"></script>

<div id="app">
  <button v-on:click="includeComponent = true">Add component</button>
  <button v-on:click="includeComponent = false">Destroy component</button>
  <foo v-if="includeComponent"></foo>
</div>

Eric99의 답변은 유익하지만, 같은 목표를 달성하기 위해 Vue의 내부를 만지작거리는 것보다 더 쉬운 것이 있다고 생각합니다.즉, 컴포넌트에 이벤트를 사용하는 것입니다.읽기 쉽고 쓰기에도 짧습니다.

Vue.component('foo', {
  methods {
    logOnDestroy (txt) {
      this.$on('beforeDestroy', () => {
        console.log(txt)
      })
    }
  },
  beforeDestroy () {
    this.$emit('beforeDestroy');
  }
});

질문하실 수 있습니다.전화해야 하나요?this.$off()메모리 누설을 방지하기 위해 이벤트를 내보낸 후?정답은 "아니오"입니다.Vue는 자동으로 이 작업을 수행하므로 위의 코드는 괜찮습니다.

Eric99의 답변은 훌륭하지만 다음과 같은 컴포넌트 내 내비게이션 가드에는 적합하지 않습니다.beforeRouteLeave,beforeRouteUpdate,beforeRouteEnter- VueRouter에 의해 유지된다(및 해당 컴포넌트 컴파일 시 수집).Vue-Router가 컴포넌트의 컨스트럭터를 캐시하여 실제 컴포넌트 인스턴스 대신 사용한다는 사실로 인해 우리의 삶은 더욱 어려워졌습니다.

이 문제를 해결하기 위해 VueRouter의 내부를 조사해야 했지만 다음 코드를 생각해냈습니다._Ctor캐시된 컨스트럭터입니다.그것만 사용해도 충분한지 확인하지 않았기 때문에 안전을 위해 컴포넌트 정의와 컨스트럭터를 모두 사용합니다.

const routeComponent = this.$route.component;
injectHook([
  routeComponent, 
  routeComponent._Ctor && routeComponent._Ctor[0] 
    ? routeComponent._Ctor[0].options 
    : null
], 'beforeRouteLeave', this.hookFunction);

function injectHook(routeComponentInstance, hookName, hookFunction)
{
  (Array.isArray(routeComponentInstance) 
    ? routeComponentInstance 
    : [routeComponentInstance]
  ).forEach(instance =>
  {
    if (instance)
    {
      const existing = instance[hookName];
      if (existing && Array.isArray(existing))
      {
        const index = existing.findIndex(item => item === hookFunction);
        if (index < 0) return;
      }
      instance[hookName] = existing
        ? Array.isArray(existing) ? existing.concat(hookFunction) : [existing, hookFunction]
        : [hookFunction];
    }
  });
}

function removeHook(routeComponentInstance, hookName, hookFunction)
{
  (Array.isArray(routeComponentInstance) 
    ? routeComponentInstance 
    : [routeComponentInstance]
  ).forEach(instance =>
  {
    if (instance)
    {
      const existing = instance[hookName];
      if (existing && Array.isArray(existing))
      {
        const index = existing.findIndex(item => item === hookFunction);
        if (index !== -1) existing.splice(index, 1);
      }
    }
  });
}

언급URL : https://stackoverflow.com/questions/48931936/how-to-add-vue-lifecycle-listener-dynamically

반응형