최근 작업에 Vue를 사용해야 하는데, 가장 최근에 접하게 된 것이 Vue 컴포넌트 간 이벤트 전송에 대해 소개해 드리겠습니다. 필요하신 분들은 참고하시면 됩니다
새 작업에는 Vue를 사용해야 하는데, 가장 최근에 접하게 된 것도 Vue입니다. 이전에 React를 사용해 왔기 때문에 매우 빠르게 Vue를 시작했습니다.
두 가지의 유사점과 차이점을 찾으려고 노력합니다. 추가적인 보조 방법 외에도 가장 큰 차이점은 소품뿐만 아니라 이벤트 모니터링도 가능하다는 것입니다. 전달된 구성 요소 사이에 사용됩니다.
그러나 vue2.+에서는 vue는 효율성을 높이기 위해 diff 알고리즘과 가상 돔을 도입했습니다. 우리는 이러한 사실을 알고 있습니다. dom 요소의 빈번한 업데이트를 처리하기 위해 최적화 솔루션이 제안됩니다. 잦은 변경 및 업데이트와 이벤트 리스너 초기화 사이에 충돌이 있습니까? 구성 요소를 변경해야 할 때 등록된 이벤트가 바인딩 해제됩니까? ? 이를 검증하기 위해 간단한 코드를 작성해 보겠습니다.
p로 만든 버튼 두 개를 작성하는데, 하나는 html 코드로 작성하고, 다른 하나는 컴포넌트 형태로 삽입합니다. 두 버튼은 완전히 동일하지만 외부 레이어에 비활성화 속성을 추가하고 if를 사용합니다. -else는 비활성화된 것으로 결정하여 다른 버튼을 표시합니다(물론 일반적인 시나리오에서는 이와 같은 코드를 작성하지 않을 것입니다. 여기서는 이런 방식으로 특별한 시나리오를 시뮬레이션하고 이 시나리오가 우리 비즈니스에 존재하는지 고려할 것입니다).
<template> <p class="test"> <p class="btn" v-if="disabled" @click="handleClick">可点击</p> <p class="btn" v-else >不可点击</p> <Button v-if="disabled" @clickTest="handleClick">可点击</Button> <Button v-else>不可点击</Button> </p> </template> <script> import Button from './Button' export default { data () { return { disabled: true } }, methods: { handleClick() { alert('可点击') } }, components: { Button, }, mounted() { setTimeout(() => { this.disabled = false }, 1000) } } </script> <style> .btn{ margin: 100px auto; width: 200px; line-height: 50px; border: 1px solid #42b983; border-radius: 5px; color: #42b983; } </style>
최대한 보기 좋게 보이도록 약간의 스타일을 추가했습니다. 두 개의 버튼이 클릭 가능하면 클릭 이벤트에 바인딩되고, 클릭 가능하지 않으면 바인딩되지 않습니다. 차이점은 하나는 HTML 코드를 직접 작성한 것이고, 다른 하나는 컴포넌트라는 점입니다. 구성 요소의 코드는 다음과 같습니다.
<template> <p class="btn" @click="handleClick"><slot></slot></p> </template> <script> export default { methods: { handleClick() { this.$emit('clickTest') } } } </script>
그런 다음 탑재된 주기에 1초 settimeout을 추가하여 비활성화를 false로 변경한 다음 테스트해 보겠습니다.
비활성화가 여전히 true이면 두 버튼이 모두 작동합니다. 클릭 시 팝업 알림을 클릭하세요. 하지만 비활성화가 false로 변경되면 HTML로 작성된 내용은 더 이상 팝업되지 않지만 아래 구성 요소에 작성된 내용은 계속 팝업됩니다.
이런 종류의 문제가 발생했을 때 위치를 찾는 것은 매우 어렵습니다. 왜냐하면 clicktest 이벤트가 코드에서 호출되지 않을 것이 분명하고 페이지에서는 버튼을 클릭할 수 없게 된 것도 확인할 수 있기 때문입니다. . 그렇다면 이 이벤트가 여전히 호출되는 이유는 무엇입니까?
diff 알고리즘부터 시작해 보겠습니다. 기존 diff 트리 알고리즘의 알고리즘 복잡도는 O(n^3)입니다. React가 diff 알고리즘을 도입하면 교차 수준 이동이 제거됩니다. 즉, 동일한 수준만 비교합니다. 노드의 유사점과 차이점은 알고리즘 복잡도를 O(n)로 줄여 제한 없이(물론 적당히) 전체 페이지를 자주 새로 고칠 수 있게 해줍니다.
(하하, 사진 없음)
diff의 한 가지 전략은 동일한 클래스를 가진 두 구성 요소가 유사한 트리 구조를 생성하고, 다른 클래스를 가진 두 구성 요소가 서로 다른 트리 구조를 생성한다는 것입니다. 따라서 비교 순서는
1) 트리 diff
2) 컴포넌트 diff
3) 요소 diff
입니다. 코드로 돌아가서 컴포넌트 diff를 수행할 때 동일한 컴포넌트라고 생각한 다음 요소 diff를 수행합니다. 즉, 추가, 삭제, 이동이 있어서 여기서 문제가 발생합니다.컴포넌트를 인스턴스화할 때 이벤트 리스너를 초기화했는데 동일한 컴포넌트에서 DOM을 교체할 때 Vue가 해당 컴포넌트에 추가된 콘텐츠에 응답하지 않았습니다. . 이벤트 리스너가 삭제되었습니다.
vue 코드를 살펴보겠습니다.
Vue.prototype.$emit = function (event: string): Component { const vm: Component = this if (process.env.NODE_ENV !== 'production') { const lowerCaseEvent = event.toLowerCase() if (lowerCaseEvent !== event && vm._events[lowerCaseEvent]) { tip( `Event "${lowerCaseEvent}" is emitted in component ` + `${formatComponentName(vm)} but the handler is registered for "${event}". ` + `Note that HTML attributes are case-insensitive and you cannot use ` + `v-on to listen to camelCase events when using in-DOM templates. ` + `You should probably use "${hyphenate(event)}" instead of "${event}".` ) } } let cbs = vm._events[event] if (cbs) { cbs = cbs.length > 1 ? toArray(cbs) : cbs const args = toArray(arguments, 1) for (let i = 0, l = cbs.length; i < l; i++) { try { cbs[i].apply(vm, args) } catch (e) { handleError(e, vm, `event handler for "${event}"`) } } } return vm }
vue는 vdom의 _events 속성을 통해 바인딩된 이벤트가 있는지 확인합니다. 클릭할 수 없는 버튼
: clickTest : Array(1) 0 : ƒ invoker() length :
의 _events를 살펴본 결과 클릭 테스트가 여전히 존재하는 것을 발견했습니다. 그게 문제입니다.
그러면 이런 문제를 어떻게 피해야 할까요? 차이점 비교를 통해 문제를 해결해야 할까요? 아니면 코드를 살펴봐야 할까요?
function sameVnode (a, b) { return ( a.key === b.key && ( ( a.tag === b.tag && a.isComment === b.isComment && isDef(a.data) === isDef(b.data) && sameInputType(a, b) ) || ( isTrue(a.isAsyncPlaceholder) && a.asyncFactory === b.asyncFactory && isUndef(b.asyncFactory.error) ) ) ) }
즉, diff의 경우 소위 동일 우선 판단 원칙이 핵심입니다.
key는 React가 diff를 도입할 때 추가되는 속성이기도 합니다. 전면 및 후면 vdom 트리가 통합 요소인지 확인하는 데 사용되므로(형제 관계임을 참고) 피하려면 코드에 키만 추가하면 됩니다. 이 문제
<Button key="1" v-if="disabled" @clickTest="handleClick">可点击</Button> <Button key="2" v-else>不可点击</Button>
이렇게 하면 버튼을 클릭해도 팝업창이 더 이상 뜨지 않게 됩니다.
Key에는 다양한 기능이 있습니다. 배열을 탐색하여 DOM을 생성할 때 결정 가능한 고유 ID(배열 인덱스를 사용하면 안 됨)를 추가하면 비교 효율성이 최적화되고 DOM 작업이 덜 필요합니다. 또한 형제 요소의 변경으로 인해 다시 렌더링되지 않도록 p에 키를 추가할 것입니다. (이 유형의 p는 일반적으로 캔버스 생성 등과 같은 반응 또는 vue 이외의 이벤트나 작업에 바인딩됩니다. ).
그럼 이렇게 불필요한 키 값을 컴포넌트에 추가하는 것 외에 다른 해결 방법은 없을까요?
예, Vue에 매우 반대이지만 반응과 유사한 방식이 있습니다. 즉, 콜백 이벤트를 소품을 통해 전달하는 것입니다.
<Button v-if="disabled" :clickTest="handleClick">可点击</Button> <Button v-else>不可点击</Button> props: { 'clickTest': { type: Function } }, methods: { handleClick() { //this.$emit('clickTest') this.clickTest && this.clickTest() } }
虽然vue给了我们更方便的事件传递的方式,但props里是允许我们去传递任何类型的,我的期望是在真实的dom上或者在公共组件的入口处以外的地方,都是通过props的方式来传递结果的。虽然这种方式很不vue,而且也享受不到v-on给我们带来的遍历,但是这样确实可以减少不必要的麻烦。
当然既然用了vue,更好的利用vue给我们带来的便利也很重要,所以对于这种很少会出现的麻烦,我们有一个预期,并可以快速定位并修复问题,就可以了。
上面是我整理给大家的,希望今后会对大家有帮助。
相关文章:
위 내용은 Vue 구성 요소에서 이벤트를 전달하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!