인터뷰어: Vue구성 요소 간의 통신을 구현하려면 몇 가지 방법이 있나요? 다음 기사에서는 Vue 구성요소 간의 통신을 구현하는 여러 가지 방법을 요약하고 공유할 것입니다. 더 이상 인터뷰를 두려워하지 않으실 것입니다.
컴포넌트화는 Vue 프레임워크의 중요한 아이디어 중 하나입니다. 프론트엔드 프레임워크가 등장하기 전에는 일반적으로 웹사이트 페이지가 모든 사람이 사용해야 하는 데이터가 있는 경우 전역 파일을 선언하면 됩니다. 직접적으로 . 그러나 Vue 프레임워크가 등장한 이후에는 페이지가 컴포넌트화되어 페이지가 여러 개의 파일로 나누어지게 되었고, 컴포넌트 간의 데이터 공유가 큰 문제가 되었습니다. 컴포넌트, 메소드, 오늘은 어떤 메소드가 있는지 정리하겠습니다. (학습 영상 공유: vuejs 튜토리얼)
프로젝트에서 흔히 사용되는 몇 가지만 있기 때문에 많은 친구들이 인터뷰 중에 모든 것을 설명하지 못하는 경우가 많기 때문에 정리하는 것이 좋습니다.
Vue의 모든 구성 요소는 구성 요소 트리 형식이므로 구성 요소 간 통신 상황은 대략 다음과 같습니다.
각 시나리오마다 권장되는 통신 방법이 다릅니다. 다양한 시나리오에 따라 구성 요소 간 가장 적절한 통신 방법을 선택해야 합니다.
이 방법은 일반적으로 부모 컴포넌트와 자식 컴포넌트 간에 값을 전달하는 데 사용됩니다. 부모 컴포넌트는 속성을 통해 자식 컴포넌트에 값을 전달하고, 자식 컴포넌트는 props를 통해 이를 받습니다. 하위 구성 요소는 이벤트를 통해 상위 구성 요소에 데이터를 전달합니다.
초기화 프로젝트:
가장 간단한 Vue 프로젝트를 빌드하고 각각 3개의 구성 요소(parent, child1, child2)를 빌드한 다음 APP.vue에 상위 구성 요소를 소개하고 상위 구성 요소 구성 요소에 두 하위 child1 및 child2를 소개합니다. , 초기 실행 인터페이스는 다음과 같습니다.
다음으로 속성을 사용하여 상위 구성 요소에서 하위 구성 요소로 값을 전달합니다.
상위 구성 요소 샘플 코드:
// src/views/parent.vue <template> <div class="parent-box"> <p>父级组件</p> <div> <button @click="changeMsg">更改数据</button> </div> <child1 :msg="msg"></child1> <child2 :msg="msg"></child2> </div> </template> <script> import child1 from "./child1.vue"; import child2 from "./child2.vue"; export default { data() { return { msg: "我是父组件的数据", }; }, components: { child1, child2, }, methods: { // 点击按钮更改数据 changeMsg() { this.msg = "变成小猪课堂"; }, }, }; </script>
msg="msg"를 통해 상위 구성 요소의 msg를 하위 구성 요소에 전달하고 버튼을 클릭하면 상위 구성 요소의 메시지가 수정됩니다.
하위 구성 요소 샘플 코드:
// src/views/child1.vue <template> <div class="child-1"> <p>child1组件</p> <div> <p>parent组件数据:{{ msg }}</p> </div> </div> </template> <script> export default { props: { msg: { type: String, default: "", }, }, }; </script>
하위 구성 요소는 props 속성을 통해 상위 구성 요소로부터 데이터를 받습니다.
출력 결과:
버튼을 클릭하면 상위 컴포넌트의 데이터가 변경되고, 하위 컴포넌트가 받는 데이터도 변경됩니다.
참고: :msg="msg"로 수신된 메시지는 변수입니다. :을 추가하지 않으면 수신된 메시지는 문자열입니다.
하위 구성 요소는 $emit 사용자 정의 이벤트를 통해 상위 구성 요소에 값을 전달할 수 있습니다. 상위 구성 요소는 하위 구성 요소로부터 값을 받기 위해 이 이벤트를 수신해야 합니다. .
부모 컴포넌트 샘플 코드:
// src/views/parent.vue <template> <div class="parent-box"> <p>父级组件</p> <div> <button @click="changeMsg">更改数据</button> </div> <div>子组件数据:{{ childData }}</div> <child1 :msg="msg" @childData="childData"></child1> <child2 :msg="msg"></child2> </div> </template> <script> import child1 from "./child1.vue"; import child2 from "./child2.vue"; export default { data() { return { msg: "我是父组件的数据", childData: "", }; }, components: { child1, child2, }, methods: { changeMsg() { this.msg = "变成小猪课堂"; }, // 监听子组件事件 childData(data) { this.childData = data; }, }, }; </script>
하위 컴포넌트 샘플 코드:
// src/views/child1.vue <template> <div class="child-1"> <p>child1组件</p> <div> <button @click="sendData">传递数据给父组件</button> </div> <div> <p>parent组件数据:{{ msg }}</p> </div> </div> </template> <script> export default { props: { msg: { type: String, default: "", }, }, methods: { // 点击按钮,使用$emit向父组件传递数据 sendData() { this.$emit("childData", "我是子组件数据"); }, }, }; </script>
출력 결과:
부모 컴포넌트의 @childData="getChildData"를 통해 childData 이벤트를 수신합니다. 이를 통해 하위 구성 요소에 의해 전달된 데이터를 가져옵니다. $emit 이벤트는 하위 구성 요소의 버튼을 클릭하여 데이터를 상위 구성 요소에 전달함으로써 트리거됩니다. "상위 구성 요소에 데이터 전달" 버튼을 클릭하면 상위 구성 요소가 데이터를 얻을 수 있습니다.
이 방법을 사용하면 데이터뿐만 아니라 메소드도 포함하여 하위 컴포넌트가 상위 컴포넌트의 값을 매우 편리하게 가져올 수 있습니다.
하위 구성 요소 샘플 코드:
// src/views/child1.vue <template> <div class="child-1"> <p>child1组件</p> <div> <button @click="sendData">传递数据给父组件</button> </div> <div> <button @click="getParentData">使用$parent</button> </div> <div> <p>parent组件数据:{{ msg }}</p> </div> </div> </template> <script> export default { props: { msg: { type: String, default: "", }, }, methods: { sendData() { this.$emit("childData", "我是子组件数据"); }, // 通过$parent方式获取父组件值 getParentData() { console.log("父组件", this.$parent); }, }, }; </script>
点击“使用parent获取父组件的属性或数据。
输出结果:
我们可以看到控制台打印出了父组件的所有属性,不仅仅包含了data数据,还有里面定义的一些方法等。
$children
和$refs
获取子组件值这两种方式和$parent非常的类似,它们可以直接获取子组件的相关属性或方法,不仅限于数据。
父组件示例代码:
// src/views/parent.vue <template> <div class="parent-box"> <p>父级组件</p> <div> <button @click="changeMsg">更改数据</button> </div> <div> <button @click="getChildByRef">使用$children和$refs</button> </div> <div>子组件数据:{{ childData }}</div> <child1 ref="child1" :msg="msg" @childData="getChildData"></child1> <child2 :msg="msg"></child2> </div> </template> <script> import child1 from "./child1.vue"; import child2 from "./child2.vue"; export default { data() { return { msg: "我是父组件的数据", childData: "", }; }, components: { child1, child2, }, methods: { changeMsg() { this.msg = "变成小猪课堂"; }, // 监听子组件的自定义事件 getChildData(data) { this.childData = data; }, // 使用$chilren和$refs获取子组件 getChildByRef() { console.log("使用$children", this.$children); console.log("使用$refs", this.$refs.child1); }, }, }; </script>
输出结果:
上段代码中,我们点击按钮,分别通过refs的方式获取到了子组件,从而拿到子组件数据。需要注意的是,refs时,需要在子组件上添加ref属性,有点类似于直接获取DOM节点的操作。
$attrs
和$listeners
$attrs
의 사용은 Vue2.4.0 이후에 새로 제안되었으며 일반적으로 다중 계층 구성 요소가 데이터를 전송할 때 사용됩니다. 많은 친구들이 다층 구성 요소 데이터 전송 시나리오를 접할 경우 전송을 위해 Vuex를 직접 선택할 수 있습니다. 그러나 전송해야 하는 데이터에 데이터 업데이트 및 수정이 포함되지 않은 경우 결국 $arrts 방법을 사용하는 것이 좋습니다. , Vuex는 여전히 상대적으로 무겁습니다.
공식 웹사이트 설명:
에는 상위 범위에서 소품으로 인식(및 획득)되지 않는 속성 바인딩(클래스 및 스타일 제외)이 포함되어 있습니다. 구성 요소가 소품을 선언하지 않으면 모든 상위 범위 바인딩(클래스 및 스타일 제외)이 여기에 포함되며 내부 구성 요소는 v-bind="$attrs"를 통해 전달될 수 있습니다. - 상위 수준 구성 요소를 생성할 때 매우 유용합니다.
공식 홈페이지의 설명은 아직 이해하기 어렵습니다.
인기 있는 설명:
상위 구성 요소가 하위 구성 요소에 많은 데이터를 전달하고 하위 구성 요소가 이를 수신할 prop을 선언하지 않은 경우
说的再多可能还是没有代码来得简单易懂,我们新建一个孙子组件child1-child.vue,编写之后界面如下:
我们在parent父组件中多传一点数据给child1组件。
parent组件示例代码:
// src/views/parent.vue <template> <div class="parent-box"> <p>父级组件</p> <child1 ref="child1" :msg="msg" :msg1="msg1" :msg2="msg2" :msg3="msg3" :msg4="msg4" @childData="getChildData" ></child1> </div> </template> <script> import child1 from "./child1.vue"; import child2 from "./child2.vue"; export default { data() { return { msg: "我是父组件的数据", msg1: "parent数据1", msg2: "parent数据2", msg3: "parent数据3", msg4: "parent数据4", childData: "", }; }, components: { child1, child2, } }; </script>
这里我们删除了一些本节用不到的代码,大家需要注意一下。
child1组件示例代码:
// src/views/child1.vue <template> <div class="child-1"> <p>child1组件</p> <!-- 子组件child1-child --> <child1-child v-bind="$attrs"></child1-child> </div> </template> <script> import Child1Child from "./child1-child"; export default { components: { Child1Child, }, props: { msg: { type: String, default: "", }, }, mounted() { console.log("child1组件获取$attrs", this.$attrs); } }; </script>
输出结果:
上段代码中我们的parent父组件传递了5个数据给子组件:msg、msg1、msg2、msg3、msg4。但是在子组件中的props属性里面,我们只接收了msg。然后我们在子组件mounted中打印了$attrs,发现恰好少了props接收过的msg数据。
当我们在child1组件中使用attrs"的形式在传递给它的子组件child1-child,上段代码中我们已经加上了v-bind。
child1-child组件示例代码:
// src/views/child1-child.vue <template> <div class="child1-child"> <p>我是孙子组件child1-child</p> </div> </template> <script> export default { props: { msg1: { type: String, default: "", }, }, mounted() { console.log("child1-child组件$attrs", this.$attrs); }, }; </script>
输出结果:
我们发现child1-child组件中打印的$attrs中少了msg1,因为我们已经在props中接收了msg1。
attrs属性和类型,只是它们传递的东西不一样。
官网的解释:
包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件——在创建更高层次的组件时非常有用。
通俗的解释:
当父组件在子组件上定义了一些自定义的非原生事件时,在子组件内部可以通过$listeners属性获取到这些自定义事件。
它和attrs用来传递属性,$listeners用来传递非原生事件,我们在child1组件中打印一下看看。
child1组件示例代码:
// src/views/child1.vue mounted() { console.log("child1组件获取$attrs", this.$attrs); console.log("child1组件获取$listeners", this.$listeners); },
输出结果:
可以发现输出了childData方法,这是我们在它的父组件自定义的监听事件。除次之外,$listeners可以通过v-on的形式再次传递给下层组件。
child1组件示例代码:
// src/views/child1.vue <template> <div class="child-1"> <p>child1组件</p> <div> <button @click="sendData">传递数据给父组件</button> </div> <div> <button @click="getParentData">使用$parent</button> </div> <div> <p>parent组件数据:{{ msg }}</p> </div> <!-- 子组件child1-child --> <child1-child v-bind="$attrs" v-on="$listeners"></child1-child> </div> </template>
child1-child组件示例代码:
// src/views/child1-child.vue mounted() { console.log("child1-child组件$attrs", this.$attrs); console.log("child1-child组件$listerners", this.$listeners); },
输出结果:
可以看到在child1-child孙子组件中也获得了parent父组件中的childData自定义事件。使用emit的方式逐级向上触发事件,只需要使用$listerners就可以得到父组件中的自定义事件,相当于偷懒了。
可能细心的小伙伴会发现,我们在使用$attrs时,child1子组件渲染的DOM节点上将我们传递的属性一起渲染了出来,如下图所示:
这并不是我们想要的,为了解决这个问题,我们可以在子组件中设置inheritAttrs属性。
官网解释:
默认情况下父作用域的不被认作 props 的 attribute 绑定 (attribute bindings) 将会“回退”且作为普通的 HTML attribute 应用在子组件的根元素上。当撰写包裹一个目标元素或另一个组件的组件时,这可能不会总是符合预期行为。通过设置 inheritAttrs 到 false,这些默认行为将会被去掉。而通过 (同样是 2.4 新增的) 实例 property $attrs 可以让这些 attribute 生效,且可以通过 v-bind 显性的绑定到非根元素上。
官网说了非常多,但是不太通俗,我们可以简单的理解。
通俗解释:
父组件传递了很多数据给子组件,子组件的props没有完全接收,那么父组件传递的这些数据就会渲染到HTML上,我们可以给子组件设置inheritAttrs 为false,避免这样渲染。
child1组件示例代码:
// src/views/child1.vue props: { msg: { type: String, default: "", }, }, inheritAttrs: false,
输出结果:
此时我们节点上就没有那些无关的节点属性了。
在我们做项目的时候,会发现不相关的组件之间的数据传递是较为麻烦的,比如兄弟组件、跨级组件,在不使用Vuex情况下,我们可以使用自定义事件(也可以称作事件中心)的方式来实现数据传递。
事件中心的思想也比较简单:中间中心主要就两个作用:触发事件和监听事件。假如两个组件之间需要传递数据,组件A可以触发事件中心的事件,组件B监听事件中心的事件,从而让两个组件之间产生关联,实现数据传递。
实现步骤:
为了演示简单,我们在全局注册一个事件中心,修改main.js。
main.js代码如下:
// src/main.js Vue.config.productionTip = false Vue.prototype.$EventBus = new Vue() new Vue({ router, store, render: h => h(App) }).$mount('#app')
child1组件示例代码:
<template> <div class="child-1"> <p>child1组件</p> <div> <button @click="toChild2">向child2组件发送数据</button> </div> </div> </template> <script> import Child1Child from "./child1-child"; export default { methods: { // 通过事件总线向child2组件发送数据 toChild2() { this.$EventBus.$emit("sendMsg", "我是child1组件发来的数据"); }, }, }; </script>
child1组件中调用emit向事件中心添加sendMsg事件,这个用法有点类似与props和$emit的关系。
child2组件2示例代码:
// src/views/child1.vue <template> <div class="child-2"> <p>child2组件</p> <div> <p>parent组件数据:{{ msg }}</p> </div> </div> </template> <script> export default { props: { msg: { type: String, default: "", }, }, mounted() { this.$EventBus.$on("sendMsg", (msg) => { console.log("接收到child1发送来的数据", msg); }); }, }; </script>
当我们点击child1组件中的按钮时,就会触发sendMsg事件,在child2组件中我们监听了该事件,所以会接收到child1组件发来的数据。
输出结果:
事件中心实现数据传递的这种方式,其实就是一个发布者和订阅者的模式,这种方式可以实现任何组件之间的通信。
这两个是在Vue2.2.0新增的API,provide和inject需要在一起使用。它们也可以实现组件之间的数据通信,但是需要确保组件之间是父子关系。
官网的解释:
这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在其上下游关系成立的时间里始终生效。
官网的解释就已经说得很明确了,所以这里我们就不需要通俗的解释了,简单一句话:父组件可以向子组件(无论层级)注入依赖,每个子组件都可以获得这个依赖,无论层级。
parent示例代码:
// src/views/parent.vue <script> import child1 from "./child1.vue"; import child2 from "./child2.vue"; export default { provide() { return { parentData: this.msg }; }, data() { return { msg: "我是父组件的数据", msg1: "parent数据1", msg2: "parent数据2", msg3: "parent数据3", msg4: "parent数据4", childData: "", }; }, components: { child1, child2, }, }; </script>
child1-child组件示例代码:
// src/views/child1-child.vue <template> <div class="child1-child"> <p>我是孙子组件child1-child</p> <p>parent组件数据:{{parentData}}</p> </div> </template> <script> export default { inject: ["parentData"], props: { msg1: { type: String, default: "", }, }, mounted() { console.log("child1-child组件$attrs", this.$attrs); console.log("child1-child组件$listerners", this.$listeners); console.log("child1-child组件获取parent组件数据", this.parentData) }, }; </script>
输出结果:
通过provide和inject结合的方式,我们在child1-child组件中获取到了parent组件中的数据。如果你下来尝试过的话,可能会发现一个问题,此时数据不是响应式,也就是parent组件更改了数据,child1-child组件中的数据不会更新。
想要变为响应式的,我们需要修改一下provide传递的方式。
parent代码如下:
// src/views/parent.vue <script> import child1 from "./child1.vue"; import child2 from "./child2.vue"; export default { provide() { return { parentData: this.getMsg }; }, data() { return { msg: "我是父组件的数据", msg1: "parent数据1", msg2: "parent数据2", msg3: "parent数据3", msg4: "parent数据4", childData: "", }; }, components: { child1, child2, }, methods: { // 返回data数据 getMsg() { return this.msg; }, }, }; </script>
这个时候我们会发现数据变为响应式的了。
porvide和inject的原理可以参考下图:
这两种方式应该是小伙伴们在实际项目中使用最多的了,所以这里就不但展开细说,只是提一下这两者的区别即可。
Vuex:
localstorage:
v-model是vue中的一个内置指令,它通常用在表单元素上以此来实现数据的双向绑定,它的本质是v-on和v-bind的语法糖。在这里我们也可以借助它来实现某些场景下的数据传递。注意,这儿的场景必须是父子组件。
parent组件示例代码:
<template> <div class="parent-box"> <p>父级组件</p> <div>modelData: {{modelData}}</div> <child2 :msg="msg" v-model="modelData"></child2> <!-- 实际等同于 --> <!-- <child2 v-bind:value="modelData" v-on:input="modelData=$event"></child2> --> </div> </template> <script> import child2 from "./child2.vue"; export default { provide() { return { parentData: this.getMsg }; }, data() { return { modelData: "parent组件的model数据" }; }, components: { child1, }, }; </script>
child2组件示例代码:
<template> <div class="child-2"> <p>child2组件</p> <div> <button @click="confirm">修改v-model数据</button> </div> </div> </template> <script> export default { props: { value: { type: String, default: "", }, }, mounted() { console.log("child2组件接收附件见v-model传递的数据", this.value); }, methods: { // 通过$emit触发父组件的input事件,并将第二个参数作为值传递给父组件 confirm() { this.$emit("input", "修改parent传递的v-model数据"); }, }, }; </script>
我们在父组件中使用v-model向child2子组件传递数据,子组件的props中使用默认的value属性接收,在子组件中利用$emit触发父组件中默认input事件,此时传递的数据便会在子组件和父组件中发生变化,这就是数据双向绑定。
如果想要更加详细的学习v-model的使用,可以参考官网。
Vue中组件通讯的方式有很多种,每一种应用的场景可能都有一些不一样,我们需要在合适的场景下选择合适的通讯方式。
위 내용은 Vue에서 컴포넌트 간 통신을 구현하는 다양한 방법을 요약하고 공유하므로 더 이상 인터뷰가 두렵지 않습니다!의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!