ホームページ > 記事 > ウェブフロントエンド > Vueコンポーネントでイベントを渡す方法
最近の仕事では Vue を使用する必要があるので、私が最近触ったのは Vue です。Vue コンポーネント間のイベント転送について紹介します。必要な方は参考にしてください。新しい仕事には Vue を使用する必要があります。私が最近接した仕事も Vue です。以前は React を使用していたので、すぐに Vue を使い始めました。
また、いくつかの補助的なメソッドに加えて、この 2 つの類似点と相違点も見つけようとします。最大の違いは、コンポーネント間の通信だけでなく、イベントの監視もあります。コンポーネント間で渡されて使用されます。
しかし、vue2.+ では、vue は効率を向上させるために diff アルゴリズムと仮想 dom を導入しました。 DOM 要素の頻繁な更新に対処するために、コンポーネントを変更する必要がある場合、イベント リスナーの頻繁な変更と初期化の間に競合はありますか? それを確認するために簡単なコードを書いてみましょう。
p で作成した 2 つのボタンを記述します。1 つは HTML コードで記述され、もう 1 つはコンポーネントの形式で挿入されます。2 つのボタンはまったく同じですが、外側のレイヤーに disabled 属性を追加し、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>
できるだけ見栄えを良くするために、2 つのボタンがクリック可能な場合はクリック イベントにバインドされ、クリックできない場合はバインドされません。違いは、1 つは直接記述された HTML コードであり、もう 1 つはコンポーネントであることです。コンポーネントのコードは次のとおりです:
<template> <p class="btn" @click="handleClick"><slot></slot></p> </template> <script> export default { methods: { handleClick() { this.$emit('clickTest') } } } </script>
次に、マウントされたサイクルに 1 秒の settimeout を追加して、disabled を false に変更し、テストしてみましょう
disabled が true のままの場合、両方のボタンはクリックするとポップアップが表示されます。 クリックアラート。ただし、disabled を false に変更すると、HTML で書かれたものはポップアップされなくなりますが、以下のコンポーネントで書かれたものは引き続きポップアップされます。
この種の問題が発生した場合、コードがこの clicktest イベントを呼び出さないことは明らかであり、ページ上ではボタンがクリックできなくなっていることも確認できるため、この種の問題を特定することは非常に困難です。では、なぜこのイベントがまだ呼び出されているのでしょうか?
diff アルゴリズムから始めましょう。 従来の差分ツリー アルゴリズムのアルゴリズムの複雑さは O(n^3) で、react が diff アルゴリズムを導入すると、レベル間の移動がなくなり、同じレベルのみを比較します。ノードの類似点と相違点により、アルゴリズムの複雑さが O(n) に軽減され、ページ全体を制約なく頻繁に (もちろん適度に) 更新できるようになります。
(笑、写真はありません)
diff の 1 つの戦略は、同じクラスを持つ 2 つのコンポーネントが同様のツリー構造を生成し、異なるクラスを持つ 2 つのコンポーネントが異なるツリー構造を生成することです。したがって、その比較順序は
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 を調べたところ、クリックテストがまだ存在していることがわかりました。それが問題なのです。
では、このような問題を回避するにはどうすればよいでしょうか? diff 比較によって問題を解決するべきでしょうか、それともコードを調べるべきでしょうか?
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 は通常、キャンバスの生成など、react や vue 以外のイベントまたはアクションにバインドされます)。 )。
では、この不要なキー値をコンポーネントに追加する以外に、それを解決する他の方法はあるのでしょうか?
はい、非常に反 Vue ですが React に似た方法があります。これは、次のように props を介してコールバック イベントを渡すことです。
<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 中国語 Web サイトの他の関連記事を参照してください。