保姆级别Vue2组件通信(面试常问)

简介: 如果父组件传递了一些属性到子组件,但子组件并没有声明这些属性,则它们称之为attribute,这些属性会直接附着在子组件的根元素上

在面试过程中,经常会被面试官问的鸦雀无言,但是如果我们日常积累些知识点,等到被面试的时候,就可以和面试官来交流技术。


父子组件通信


绝大部分vue本身提供的通信方式,都是父子组件通信


prop


最常见的组件通信方式之一,由父组件传递到子组件


父组件


<template>
  <div id="app">
    <HelloWorld
      msg="Welcome to Your Vue.js App"
    />
  </div>
</template>
<script>
import HelloWorld from "./components/HelloWorld.vue";
export default {
  components: {
    HelloWorld,
  },
};
</script>
复制代码


子组件


<template>
  <div>
    <h1>{{msg}}</h1>
  </div>
</template>
<script>
export default {
  name: "HelloWorld",
  props: {
    msg: String,
  },
  created(){
      console.log(this.msg) // 打印出父组件给子组件传递的值 Welcome to Your Vue.js App
  }
};
</script>
复制代码


event(emit)


最常见的组件通信方式之一,当子组件发生了某些事,可以通过event通知父组件


父组件


<template>
  <div id="app" 
:style="{fontSize: postFontSize + 'em'}">
    <HelloWorld
      msg="Welcome to Your Vue.js App"
      @enlarge-text="postFontSize += 0.1"
    />
  </div>
</template>
<script>
import HelloWorld from "./components/HelloWorld.vue";
export default {
  components: {
    HelloWorld,
  },
  data(){
      return {
        postFontSize: 1
      }
  }
};
</script>
复制代码


子组件


<template>
  <div>
    <h1>{{msg}}</h1>
    // 点击按钮后,子组件会触发事件,传递到父组件中,放大字体
   <button @click="$emit('enlarge-text')">放大字号</button>
  </div>
</template>
<script>
export default {
  name: "HelloWorld",
  props: {
    msg: String,
  },
};
</script>
复制代码


style和class


父组件可以向子组件传递style和class,它们会合并到子组件的根元素中


示例


父组件


<template>
  <div id="app">
    <HelloWorld
      style="color:red"
      class="hello"
      msg="Welcome to Your Vue.js App"
    />
  </div>
</template>
<script>
import HelloWorld from "./components/HelloWorld.vue";
export default {
  components: {
    HelloWorld,
  },
};
</script>
复制代码


子组件


<template>
  <div class="world" style="text-align:center">
    <h1>{{msg}}</h1>
  </div>
</template>
<script>
export default {
  name: "HelloWorld",
  props: {
    msg: String,
  },
};
</script>
复制代码


渲染结果:


<div id="app">
  <div class="hello world" style="color:red; text-aling:center">
    <h1>Welcome to Your Vue.js App</h1>
  </div>
</div>
复制代码


attribute


如果父组件传递了一些属性到子组件,但子组件并没有声明这些属性,则它们称之为attribute,这些属性会直接附着在子组件的根元素上


不包括style和class,它们会被特殊处理


示例


父组件


<template>
  <div id="app">
    <!-- 除 msg 外,其他均为 attribute -->
    <HelloWorld
      data-a="1"
      data-b="2"
      msg="Welcome to Your Vue.js App"
    />
  </div>
</template>
<script>
import HelloWorld from "./components/HelloWorld.vue";
export default {
  components: {
    HelloWorld,
  },
};
</script>
复制代码


子组件


<template>
  <div>
    <h1>{{msg}}</h1>
  </div>
</template>
<script>
export default {
  name: "HelloWorld",
  props: {
    msg: String,
  },
  created() {
    console.log(this.$attrs); // 得到: { "data-a": "1", "data-b": "2" }
  },
};
</script>
复制代码


渲染结果:


<div id="app">
  <div data-a="1" data-b="2">
    <h1>Welcome to Your Vue.js App</h1>
  </div>
</div>
复制代码


子组件可以通过inheritAttrs: false配置,禁止将attribute附着在子组件的根元素上,但不影响通过$attrs获取


natvie修饰符


在注册事件时,父组件可以使用native修饰符,将事件注册到子组件的根元素上


示例


父组件


<template>
  <div id="app">
    <HelloWorld @click.native="handleClick" />
  </div>
</template>
<script>
import HelloWorld from "./components/HelloWorld.vue";
export default {
  components: {
    HelloWorld,
  },
  methods: {
    handleClick() {
      console.log(1);
    },
  },
};
</script>
复制代码


子组件


<template>
  <div>
    <h1>Hello World</h1>
  </div>
</template>
复制代码


渲染结果


<div id="app">
  <!-- 点击该 div,会输出 1 -->
  <div>
    <h1>Hello World</h1>
  </div>
</div>
复制代码


$listeners


子组件可以通过$listeners获取父组件传递过来的所有事件处理函数, 爷爷组件


<template>
    <div class="father-page">
        <div>这是父组件</div>
        <child-1 @child2Info="getInfo"></child-1>
    </div>
</template>
<script>
    import child1 from './Child1'
    export default {
        name: 'Father',
        components:{child1},
        methods: {
            getInfo:function(data){
                console.log(data)
                //这是子组件1-1-1发送的信息
            }
        }
    }
</script>
复制代码


父亲组件 child-1


<template>
    <div class="child1-page">
        <div>这是子组件-1</div>
        <child-2 v-on="$listeners"></child-2>
    </div>
</template>
<script>
    import child2 from "./Child2"
    export default {
        name: 'Child1',
        components:{child2},
    }
</script>
复制代码


孙子组件


<template>
    <div>这是子组件1-1</div>
</template>
<script>
    export default {
        mounted: function() {
            this.$emit("child2Info","这是子组件1-1-1发送的信息")
        }
    };
</script>
复制代码


可以在子孙组件中执行祖先组件的函数,从而实现数据传递。 demo或小型项目可以使用listeners进行数据传递,中大型项目不推荐,数据流会变的难于理解。‘listeners进行数据传递,中大型项目不推荐,数据流会变的难于理解。 `listeners`的真正目的是将所有的事件监听器指向这个组件的某个特定的子元素。


v-model


v-model 实际上是vue给表单做单的一个双向数据绑定


<input v-model="aaa" />
复制代码


等价于


<input
  :value="aaa"
  @input="aaa = $event.target.value"
>
复制代码


sync修饰符


和v-model的作用类似,用于双向绑定,不同点在于v-model只能针对一个数据进行双向绑定,而sync修饰符没有限制


示例


子组件


<template>
  <div>
    <p>
      <button @click="$emit(`update:num1`, num1 - 1)">-</button>
      {{ num1 }}
      <button @click="$emit(`update:num1`, num1 + 1)">+</button>
    </p>
    <p>
      <button @click="$emit(`update:num2`, num2 - 1)">-</button>
      {{ num2 }}
      <button @click="$emit(`update:num2`, num2 + 1)">+</button>
    </p>
  </div>
</template>
<script>
export default {
  props: ["num1", "num2"],
};
</script>
复制代码


父组件


<template>
  <div id="app">
    <Numbers :num1.sync="n1" :num2.sync="n2" />
    <!-- 等同于 -->
    <Numbers
      :num1="n1"
      @update:num1="n1 = $event"
      :num2="n2"
      @update:num2="n2 = $event"
    />
  </div>
</template>
<script>
import Numbers from "./components/Numbers.vue";
export default {
  components: {
    Numbers,
  },
  data() {
    return {
      n1: 0,
      n2: 0,
    };
  },
};
</script>
复制代码


v-model VS .sync


先明确一件事情,在 vue 1.x 时,就已经支持 .sync 语法,但是此时的 .sync 可以完全在子组件中修改父组件的状态,造成整个状态的变换很难追溯,所以官方在2.0时移除了这个特性。然后在 vue2.3时,.sync又回归了,跟以往不同的是,现在的.sync完完全全就是一个语法糖的作用,跟v-model的实现原理是一样的,也不容易破环院有的数据模型,所以使用上更安全也更方便。


  • 两者都是用于实现双向数据传递的,实现方式都是语法糖,最终通过 prop + 事件 来达成目的。


  • vue 1.x 的 .sync 和 v-model 是完全两个东西,vue 2.3 之后可以理解为一类特性,使用场景略微有区别


  • 当一个组件对外只暴露一个受控的状态,切都符合统一标准的时候,我们会使用v-model来处理。.sync则更为灵活,凡是需要双向数据传递时,都可以去使用。


$parent和$children


在组件内部,可以通过$parent和$children属性,分别得到当前组件的父组件和子组件实例


$slots 插槽


Vue.compopnent('my-cmp', {
  template: `
    <div class="container">
      <header>
        <slot name="header"></slot>
      </header>
      <main>
        <slot></slot>
      </main>
      <footer>
        <slot name="footer"></slot>
      </footer>
    </div>
  `
})
复制代码


使用时


<my-cmp>
  <template v-slot:header>
    <h1>头部</h1>
  </template>
  <p>内容</p>
  <p>内容</p>
  <template v-slot:footer>
    <p>底部</p>
  </template>
</my-cmp>
复制代码


ref


父组件可以通过ref获取到子组件的实例


$parent


可以在子组件中访问父实例的数据。


对于 demo 或非常小型的有少量组件的应用来说这是很方便的。中大型项目不适用。会使应用难于调试和理解。


$children


可以在父组件中访问子实例的数据。


对于 demo 或非常小型的有少量组件的应用来说这是很方便的。中大型项目不适用。会使应用难于调试和理解


跨组件通信


Provide和Inject


示例


// 父级组件提供 'foo'
var Provider = {
  provide: {
    foo: 'bar'
  },
  // ...
}
// 组件注入 'foo'
var Child = {
  inject: ['foo'],
  created () {
    console.log(this.foo) // => "bar"
  }
  // ...
}
复制代码


详见:cn.vuejs.org/v2/api/?#pr…


router


如果一个组件改变了地址栏,所有监听地址栏的组件都会做出相应反应


最常见的场景就是通过点击router-link组件改变了地址,router-view组件就渲染其他内容


vuex


适用于大型项目的数据仓库


store模式


适用于中小型项目的数据仓库


// store.js
const store = {
  loginUser: ...,
  setting: ...
}
// compA
const compA = {
  data(){
    return {
      loginUser: store.loginUser
    }
  }
}
// compB
const compB = {
  data(){
    return {
      setting: store.setting,
      loginUser: store.loginUser
    }
  }
}
复制代码


eventbus


组件通知事件总线发生了某件事,事件总线通知其他监听该事件的所有组件运行某个函数


Vue.prototype.$bus = new Vue();
复制代码
Vue.component(‘cmp-a’, {
data () {
return {
a: ‘a’
}
},
methods: {
onClick () {
this.b u s . bus.bus.on(‘click’, this.a)
}
},
template: <div> <button @click="onClick">点击</button> </div>,
})
复制代码
Vue.component('cmp-a', {
  mounted () {
    this.$bus.$on('click', data => {
      console.log(data);  // 拿到a的数据
    })
  },
  template: `
    <div>b</div>
  `,
})
复制代码
```


相关文章
|
1天前
|
JavaScript
vue面试
vue面试
15 4
|
2月前
|
JavaScript 前端开发 API
vue面试题目汇总
vue面试题目汇总
40 4
|
2月前
|
缓存 JavaScript 前端开发
Vue常见面试题 标准答案汇总一
Vue常见面试题 标准答案汇总一
62 1
|
3月前
|
监控 JavaScript 前端开发
vue基础面试题10问
vue基础面试题10问
40 0
|
4月前
|
人工智能 缓存 JavaScript
【利用AI刷面试题】AI:十道Vue面试题巩固一下知识
【利用AI刷面试题】AI:十道Vue面试题巩固一下知识
|
4月前
|
负载均衡 前端开发 Java
字节后端面试题(前端发送请求到后端的过程(MVC),网关gateway作用,怎么解决跨域,各微服务组件作用)
字节后端面试题(前端发送请求到后端的过程(MVC),网关gateway作用,怎么解决跨域,各微服务组件作用)
142 0
|
4月前
|
存储 JavaScript 安全
Vue基础面试题题目一
Vue基础面试题题目一
31 0
|
4月前
|
JavaScript 数据安全/隐私保护 开发者
常见的vue面试中的proxy和object.defineProperty的区别
常见的vue面试中的proxy和object.defineProperty的区别
|
4月前
|
Java Spring 容器
面试题:怎样为组件在创建的时候指定执行一个函数,在销毁的时候也先执行一个函数
面试题:怎样为组件在创建的时候指定执行一个函数,在销毁的时候也先执行一个函数
31 0
|
4月前
|
XML Java 数据格式
面试题:怎样把所有的组件的lazy-init值都设置为默认true?
面试题:怎样把所有的组件的lazy-init值都设置为默认true?
20 0