前言:常规问题

什么是组件:

  • 组件的出现,就是为了拆分Vue实例的代码量的,能够让我们以不同的组件,来划分不同的功能模块,将来我们需要什么样的功能,就可以去调用对应的组件即可;

组件化和模块化的不同:

  • 模块化: 是从代码逻辑的角度进行划分的;方便代码分层开发,保证每个功能模块的职能单一;
  • 组件化: 是从UI界面的角度进行划分的;前端的组件化,方便UI组件的重用;

为什么组件中data必须是函数:

  1. //伪代码
  2. var Component = functionComponent() {};
  3. Component.prototype.data = {
  4. demo: 123
  5. }
  6. var component1 = new Component();
  7. var component2 = new Component();
  8. component1.data.demo = 456;
  9. console.log(component2.data.demo); // 456
  1. 从上面可以看出,两个实例都引用同一个对象,其中一个改变的时候,另一个也发生改变。
  2. 每一个vue组件都是一个vue实例,通过new Vue()实例化,引用同一个对象,如果data直接是一个对象的话,那么一旦修改其中一个组件的数据,其他组件相同数据就会被改变。
  3. 而data是函数,并且在函数中return一个独立对象的时候,每个vue组件的data都因为函数有了自己的作用域,互不干扰。

一、组件使用

1.1 局部组件

  • 创建页面组件

image.png

  • 引用

image.png

1.2 全局组件

在src根目录下创建全局组件
然后在main.js下引入就行

  1. import Item from '../components/Item.vue'
  2. Vue.component("Item",Item)

二、组件传值

2.1 父组件传子组件

一、定义一个子组件

  1. 1.组件的名字是以大写字母开头的
  2. 2.驼峰命名
  3. //1.components/HomeTable.vue
  4. <template>
  5. <div>
  6. <div>
  7. <span>name:张三</span>
  8. <span>age:18</span>
  9. </div>
  10. </div>
  11. </template>
  12. <script>
  13. export default {
  14. name: "HomeTable"
  15. };
  16. </script>
  17. <style>
  18. </style>

二、在app.vue中导入子组件

  1. <script>
  2. //2.导入子组件
  3. import HomeTable from './components/HomeTable'
  4. export default {
  5. name: 'app',
  6. ....
  7. //3.需要在components属性中注册
  8. components:{
  9. HomeTable
  10. }
  11. }
  12. </script>
  13. //4.在模板中使用
  14. <template>
  15. <div id="app">
  16. ...
  17. //可以使用下划线命名使用
  18. <home-table ></home-table>
  19. </div>
  20. </template>

三、父组件向子组件传值

  1. 父组件:
  2. //1.子组件通过属性接收父组件传递的参数
  3. <home-table :data="arr"></home-table>
  4. 子组件:
  5. //2.子组件接收的参数需要在props属性中注册
  6. <script>
  7. export default {
  8. name: "HomeTable",
  9. props: {
  10. data: {
  11. type: Array
  12. }
  13. }
  14. };
  15. </script>
  • 传多个值的情况 ```javascript 父组件:

子组件接收 props:{ data:{ type:Object, }, index:{ type:Number } }

  1. 父组件传子组件:<br />父组件:<br />![image.png](https://cdn.nlark.com/yuque/0/2019/png/402644/1574245567016-09702491-78a9-4afc-9147-4021222a0ae3.png#align=left&display=inline&height=537&name=image.png&originHeight=537&originWidth=652&size=41699&status=done&style=none&width=652)
  2. 子组件:<br />![image.png](https://cdn.nlark.com/yuque/0/2019/png/402644/1574245516907-fb16c902-ba7f-41b1-9272-be51b5c9bce4.png#align=left&display=inline&height=554&name=image.png&originHeight=554&originWidth=651&size=57838&status=done&style=none&width=651)
  3. <a name="FNylQ"></a>
  4. #### 四、子组件自定义事件向父组件传参
  5. 原理:父组件将方法的引用,传递到子组件内部,子组件在内部调用父组件传递过来的方法,同时把要发送给父组件的数据,在调用方法的时候当作参数传递进去;
  6. ```javascript
  7. 子组件:
  8. //1.使用$emit方式自定义事件,向父组件传参
  9. <button @click="handleDelete(index)">删除</button>
  10. <script>
  11. export default {
  12. ...
  13. methods:{
  14. handleDelete(index){
  15. this.$emit("deleteItem",index)
  16. }
  17. }
  18. };
  19. </script>
  20. 父组件:
  21. //2.父组件接收子组件传递过来的事件参数
  22. <home-table :data="arr" @deleteItem="handleDelete"></home-table>
  23. <script>
  24. import HomeTable from './components/HomeTable'
  25. export default {
  26. name: 'app',
  27. ...
  28. methods:{
  29. handleDelete(index){
  30. console.log(index)
  31. }
  32. }
  33. }
  34. </script>

2.2 跳转页面传id

第一种:页面直接跳转

image.png

image.png

第二种:子组件获取id 通过子组件自定义事件传参回父组件跳转

子组件
image.png

父组件
image.png

三、组件通讯

3.1 单向数据流

子组件改变,父组件不变 父组件改变,子组件跟着变

props/$emit

父组件A通过props的方式向子组件B传递,B to A 通过在 B 组件中 $emit, A 组件中 v-on 的方式实现。

1.父组件向子组件传值

父组件通过props向下传递数据给子组件

2.子组件向父组件传值(通过事件形式)

子组件通过this.$emit(“methodName”,data),父组件定义同名方法即可。
子组件定义
image.png
父组件接收
image.png

3.2 双向数据流

子组件

image.png

父组件

image.png

四、中央事件总线

中央事件总线
就是一个名字可以叫做Bus的vue空实例,里边没有任何内容。
它就像一个公交车一样,来回输送人,将组件A输送到组件B,再将组件B输送到组件A;
这里A,B组件可以是父、子组件,也可以是兄、弟组件,或者两个没有任何关系的组件;
我们可以使用中央事件总线这种技术来实现vue组件之间的数据通信。

  1. //1.创建中央事件总线
  2. var bus = new Vue();
  3. //A组件发送
  4. //2.使用Bus中央事件总线在A组件中发送信息
  5. Bus.$emit('自定义事件名''$on发送过来的数据');
  6. //B组件接收
  7. //3.使用Bus中央事件总线在B组件中接收信息
  8. Bus.$on('自定义事件名'function(){
  9. //然后执行什么你自己懂的。。。
  10. });

aa.png

五、动态组件切换

  1. //1.定义组件
  2. //2.引入组件和data关联
  3. <template>
  4. <div>
  5. <button @click="handleToggle">toggle</button>
  6. <component :is="isToggle?one:two"></component>
  7. </div>
  8. </template>
  9. <script>
  10. import One from "../components/One.vue";
  11. import Two from '../components/Two.vue'
  12. export default {
  13. data() {
  14. return {
  15. isToggle:false,
  16. one: "One",
  17. two:"Two"
  18. };
  19. },
  20. components: {
  21. One,
  22. Two
  23. },
  24. methods:{
  25. handleToggle(){
  26. this.isToggle = !this.isToggle
  27. }
  28. }
  29. };
  30. </script>
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <meta http-equiv="X-UA-Compatible" content="ie=edge">
  7. <title>Document</title>
  8. <script src="https://cdn.bootcss.com/vue/2.6.11/vue.min.js"></script>
  9. <style>
  10. .v-enter,
  11. .v-leave-to {
  12. opacity: 0;
  13. transform: translateX(150px);
  14. }
  15. .v-enter-active,
  16. .v-leave-active {
  17. transition: all 0.5s ease;
  18. }
  19. </style>
  20. </head>
  21. <body>
  22. <div id="app">
  23. <a href="" @click.prevent="comName='login'">登录</a>
  24. <a href="" @click.prevent="comName='register'">注册</a>
  25. <!-- Vue提供了 component ,来展示对应名称的组件 -->
  26. <!-- component 是一个占位符, :is 属性,可以用来指定要展示的组件的名称 -->
  27. <!-- 通过 mode 属性,设置组件切换时候的模式,out-in 表示原先的组件先出去,后来的组件再进来 -->
  28. <transition mode="out-in">
  29. <component :is="comName"></component>
  30. </transition>
  31. </div>
  32. <script>
  33. // 组件名称是 字符串
  34. Vue.component('login', {
  35. template: '<h3>登录组件</h3>'
  36. })
  37. Vue.component('register', {
  38. template: '<h3>注册组件</h3>'
  39. })
  40. // 创建 Vue 实例,得到 ViewModel
  41. var vm = new Vue({
  42. el: '#app',
  43. data: {
  44. comName: 'login' // 当前 component 中的 :is 绑定的组件的名称
  45. },
  46. methods: {}
  47. });
  48. </script>
  49. </body>
  50. </html>