vue学习-2-组件及组件通讯

组件

好处:

  1. 可复用 可以提高开发效率
  2. 方便后期的维护和修改
  3. 可以减少渲染

全局组件 && 局部组件

全局组件:
注册全局组件 ,可以用横杠也可用驼峰,如myButton / my-button,但使用时,只支持横杠

Vue.component('my-button', {
    template: '
{{msg}}
'
, //template设置组件中的内容 data() { //组件中的data是个函数,它返回对象,与实例中的data不同,那个值是一个对象 return { msg: 'aaa' } } })

使用全局组件:

<div id="app">
    <!-- 使用全局组件 ,DOM中只支持-的写法my-button -->
    <my-button></my-button>
</div>

局部组件:是在实例内部写的。

let vm = new Vue({
    // template: '
1111
', //Vue实例中也有template,若在实例中发现直接渲染,没有就会渲染挂载的Vue实例
el: '#app', data: {}, // 局部组件:在实例中 一般不这么写,写太多会不好维护 components: { 'my-button': { template: '
{{msg}}
'
, //设置组件中的内容 data() { //组件的data是个函数,返回一个对象 return { msg: '局部组件bbb' } } } } })

通常这么写:

<!-- //组件的三步骤:1. 定义组件;2.注册组件;3.使用组件  -->
// 1.定义组件
let myButton = {
    template: '
{{msg}}
'
, //设置组件中的内容 data() { //组件的data是个函数,返回一个对象 return { msg: '局部组件bbb' } } } let vm = new Vue({ el: '#app', data: {}, // 局部组件:在实例中 components: { //2.注册组件,可以简写myButton,ES6写法 myButton: myButton } }) // dom中相应位置使用组件: <div id="app"> <!-- 使用全局组件 ,在dom中只支持横杠写法 my-button --> <!-- 3.使用组件 --> <my-button></my-button> </div>

若在组件的template中,有很多div,以及有很多的层级,有点繁琐且不美观,可以使用下面这个写法:

<body>
<div id="app">
    <!-- 3.使用组件 -->
    <my-button></my-button>
</div>
<template id="button">
    <div>{{msg}}</div>
</template>
</body>

把组件中的内容,在dom中定义一个id名为button的template,然后在写组件时,这么写:

let myButton = {
    // template: '
{{msg}}
', //设置组件中的内容
template: '#button', //设置组件中的内容,如果有很多的代码,写到这里太繁琐,可以在dom中写一个template,命名一个id,将组件中的内容都放到template中,在这使用时直接写上id data() { //组件的data是个函数,返回一个对象 return { msg: '局部组件bbb' } } }

若有多个组件,也是如此写:先定义,再注册,最后使用

// 定义组件:
    let hello = {
        template: '#hello'
    }
// dom中的:
    <template id="hello">
        <div>hello</div>
    </template>

// 注册组件:
     components: {
        myButton, //2.注册组件,myButton: myButton可以简写myButton,ES6写法
        hello
    }
// 使用组件:
    <div id="app">
        <!-- 3.使用组件 -->
        <my-button></my-button>
        <hello></hello>
    </div>

此时myButton与hello是兄弟组件的关系,若想变成父子,也是可以的,注册的时候放到myButton组件components中,使用的时候也在myButton的dom中使用

// 1.定义组件
 let hello = { //在调用前要定义,因let定义的变量不会提升 
        template: '#hello'
    }
// 2.注册组件
    let myButton = {
        template: '#button',
        data() { 
            return {
                msg: '局部组件bbb'
            }
        },
        //每个组件中都有components,里面放的是它的子组件
        components: {
            hello  //使用组件的时候要放到父组件中,hello是mybutton的一个子组件
        }
    }
// 3.使用组件
// 
    <template id="button">
        <div>
            {{msg}}
            <hello></hello>
        </div>
    </template>

组件通讯

支持.vue后缀文件(快速原型开发)

npm install -g @vue/cli
npm install -g @vue/cli-service-global
开发环境:vue serve App.vue
生产环境:vue build App.vue

  1. 父组件传递参数给子组件,使用属性的方式:m=“money”,而子组件使用props:[‘m’]来获取。
    子组件使用观察者模式,使用$emit将值传给父组件
    父组件:
<template>
  <div>
    Parent
    <!-- m="500"是静态的字符串,若是要传动态的使用:m -->
    <!-- 父组件监听子组件的事件,并给事件绑定函数 -->
    <!-- <Son :m="money" @change="fn"></Son> -->
    <!-- 父子传递数据,语法糖的写法 -->
    <!-- <Son :m.sync="money"></Son>-->
    <!-- v-model的原理是把money绑定在属性value中 绑定的事件名@input -->
    <!-- <Son :value="money" @input="(data)=>money=data"></Son> -->
    <Son v-model="money"></Son>
  </div>
</template>
<script>
import Son from "./Son";
export default {
  data() {
    return {
    //   money: 'ABC',
      money: 500,
    };
  },
  components: {
    Son
  },
//    父组件中的绑定事件执行 
  methods: {
    fn(data) {
      // 当父组件监听到子组件的点击事件执行了,绑定方法就执行,并且会拿到子组件对应传过来的值
      this.money = data;
    }
  }
};
</script>

子组件:

<template>
  <div>
    Son
    <!-- 获取传过来的值用这个 -->
    <!-- {{m}}   -->
    <!-- 这是改变后的值 -->
    {{changeM}} 
    <button @click="giveData">给父组件数据</button>
     <!--这是v-model对应的值value  -->
    {{value}}
  </div>
</template>
<script>
export default {
// props: ["m"], //获取从父组件拿到的值使用,props
  // computed: {
  //   //若想对传过来的值修改,例如变成小写的,推荐使用计算属性
  //   changeM() {
  //     return this.m.trim().toLowerCase();
  //   }
  // },
  
  // 验证传过来的值,下面会详解
  props: {
    m: {
      type: [String, Number] //值的类型
      // default: 100 //默认值 , 它与required不能同时使用,用一个就可以
      // required: true //必传
      // validator: value => {
      //   //自定义验证规则
      //   return value > 400 && value < 1000;
      // }
    },
    arr: {
      type: Array, //若是数组或对象,默认值是函数
      default: () => [1]
    },
    obj: {
      type: Object,
      default: () => ({}) //ES6中,函数中的直接写{}是作用域,所以需要加()
    },
    value: {
      //这是v-model对应的value
      type: Number
    }
  },
  computed: {   //若想对传过来的值修改,推荐使用计算属性
    changeM() {
      return this.m.trim().toLowerCase();
    }
  },
//   
  methods: {
    giveData() {
      // 子组件执行父组件监听的事件,子组件使用$emit将数据发送给父组件
    //   this.$emit("change", 999); //change是监听的事件,999是参数
    // this.$emit("update:m", 999);  //若在父组件使用:m.sync='money',在这要使用update带属性名表示事件
    this.$emit("input", 999); //使用v=model传的值,事件是input事件
    }
  }
};
</script>

props验证

父组件传的值为:

data() {
    return {
      money: 400,
    };
  },//控制台会报错,自定义验证规则失败,因不满足条件

子组件:

<template>
  <div>
    Son
    <!-- 获取传过来的值用这个 -->
    {{m}}  
  </div>
</template>
<script>
export default {
// 验证传过来的值
props:{
    m:{
        type:[String,Number], //值的类型
        // default:100, //默认值 , 它与required不能同时使用,用一个就可以   
        required:true, //必传
        validator:(value)=>{ //自定义验证规则
            return value>400 &&  value<1000
        }
    }
},
};
</script>

若传的值是一个对象或数组(空数组,或空对象),可以设置默认值,需要注意:
数组的话,默认值是一个函数。

props:{
    arr: {
      type: Array, //若是数组或对象,默认值是函数
      default: () => [1]
    },
    obj: {
      type: Object,
      default: () => ({}) //ES6中,函数中的直接写{}是作用域,所以需要加()
    }
}
  1. $attrs $listeners:批量把属性和方法往下传
    a t t r s ( 通 过 v − b i n d = " attrs(通过 v-bind=" attrs(vbind="attrs" 再往下一层传递,批量把属性往下传);
    l i s t e n e r s ( 使 用 v − o n 向 下 传 递 v − o n = " listeners(使用v-on向下传递 v-on=" listeners(使vonvon="listeners.click()"可以在GrandSon组件中执行Parent父组件的事件,批量把方法往下传)。
    父组件:
    <template>
  <div>
    Parent
    <!-- m="500"是静态的字符串,若是要传动态的使用:m -->
    <!-- 父组件监听子组件的事件,并给事件绑定函数 -->
    <!-- <Son :m="money" @change="fn"></Son> -->
    <!-- 父子传递数据,语法糖的写法 -->
    <!-- <Son :m.sync="money"></Son> -->
    <!-- v-model的原理是把money绑定在属性value中 绑定的事件名@input -->
    <!-- <Son :value="money" @input="(data)=>money=data"></Son> -->
    <!-- <Son v-model="money"></Son> -->
    <Son :name="{a:1}" :age="2" @click="fn1"></Son>
  </div>
</template>
<script>
import Son from "./Son";
export default {
  data() {
    return {
      // money: "ABC" //传递的数据是大写
      money: 400 //传递的数据是大写
    };
  },
  components: {
    Son
  },
  methods: {
    fn1() {
      alert(1);
    },
    fn(data) {
      // 当父组件监听到子组件的点击事件执行了,绑定方法就执行,并且会拿到子组件对应传过来的值
      this.money = data;
    }
  }
};
</script>

子组件:

<template>
  <div>
    Son
    <!--
    获取传过来的值用这个   
    -->
    <!-- {{m}} -->
    {{arr}}
    {{obj}}
    <!--这是v-model对应的值value  -->
    {{value}}
    <!-- 这是修改过的值 -->
    <!-- {{changeM}} -->

    <!-- 直接使用这个$attrs可以获取父组件传过来的属性{ "name": {"a":1}, "age": 2 } -->
    {{$attrs}}
    {{$attrs.name}}
    <!-- { "a": 1 }-->
    <!-- 执行父组件的事件使用$listeners,里面的事件click() -->
    <!-- {{$listeners.click()}} -->

    <button @click="giveData">给父组件数据</button>
    <!-- 把从父组件拿到的属性$attrs.name传给GrandSon组件 -->
    <GrandSon v-bind="$attrs" :name1="$attrs.name" v-on="$listeners"></GrandSon>
  </div>
</template>
<script>
import GrandSon from "./GrandSon";
export default {
  // props: ["m"], //获取从父组件拿到的值使用,props
  // computed: {
  //   //若想对传过来的值修改,例如变成小写的,推荐使用计算属性
  //   changeM() {
  //     return this.m.trim().toLowerCase();
  //   }
  // },
  props: {
    // 验证传过来的值
    m: {
      type: [String, Number] //值的类型
      // default: 100 //默认值 , 它与required不能同时使用,用一个就可以
      // required: true //必传
      // validator: value => {
      //   //自定义验证规则
      //   return value > 400 && value < 1000;
      // }
    },
    arr: {
      type: Array, //若是数组或对象,默认值是函数
      default: () => [1]
    },
    obj: {
      type: Object,
      default: () => ({}) //ES6中,函数中的直接写{}是作用域,所以需要加()
    },
    value: {
      //这是v-model对应的value
      type: Number
    }
  },
  methods: {
    giveData() {
      // 子组件执行父组件监听的事件,子组件使用$emit将数据发送给父组件
      // this.$emit("change", 999); //change是监听的事件,999是参数
      // this.$emit("update:m", 999); //若在父组件使用:m.sync='money',在这要使用update带属性名表示事件
      this.$emit("input", 999); //使用v=model传的值,事件时input
    }
  },
  components: {
    GrandSon
  }
};
</script>

GrandSon组件:

<template>
  <div>
    GrandSon
    <!-- 这是从Son传来的来自Parent的数据,还有Parent的click事件,可以直接调用Parent组件中的事件 -->
    {{$attrs}}{{name1}}
    {{$listeners.click()}}
  </div>
</template>
<script>
export default {
  props: ["name1"]
};
</script>
  1. provide inject 因为是全局的,只要定义,在任意地方都可以使用,但没法找到是在哪里定义的变量
    使用provide定义全局变量,想在哪里使用就使用inject注入即可
    Parent组件:
<template>
  <div>
    Parent
    <Son :name="{a:1}" :age="2" @click="fn1"></Son>
  </div>
</template>
<script>
import Son from "./Son";
export default {
  provide() {
    return {
      parentMsg: "Parent中使用provide定义的全局数据"
    };
  },
  data() {
    return {
      money: 400 //传递的数据是大写
    };
  },
  components: {
    Son
  },
  methods: {
    fn1() {
      alert(1);
    },
    fn(data) {
      // 当父组件监听到子组件的点击事件执行了,绑定方法就执行,并且会拿到子组件对应传过来的值
      this.money = data;
    }
  }
};
</script>

GrandSon组件,使用inject注入Parent中定义的全局属性parentMsg:

<template>
  <div>
    GrandSon
    <!-- 这是调用从全局注入的parentMsg -->
    {{parentMsg}}
  </div>
</template>
<script>
export default {
  // 注入全局定义的parentMsg属性(定义的位置在Parent组件)
  inject: ["parentMsg"],
  props: ["name1"]
};
</script>
  1. $parent $children ref
    $parent 获取父组件 $children 获取子组件 ref 实例如下:
    父组件:
<template>
  <div>
    Parent
    <Son :name="{a:1}" :age="2" @click="fn1"></Son>
  </div>
</template>
<script>
import Son from "./Son";
export default {
  data() {
    return {
      money: 400 //传递的数据是大写
    };
  },
  components: {
    Son
  },
  methods: {
    fn1() {
      alert("Parent组件中的方法111");
    }
  }
};
</script>

Son子组件:

<template>
  <div>
    Son
    <GrandSon ref="grandSon"></GrandSon>
  </div>
</template>
<script>
import GrandSon from "./GrandSon";
export default {
  props: {
    //这是v-model对应的value
    value: {
      type: Number
    },
    // 验证传过来的值
    m: {
      type: [String, Number] //值的类型
      // default: 100 //默认值 , 它与required不能同时使用,用一个就可以
      // required: true //必传
      // validator: value => {
      //   //自定义验证规则
      //   return value > 400 && value < 1000;
      // }
    }
  }
  components: {
    GrandSon
  },
  //DOM加载完成
  mounted() {
    // 调用父组件的方法:获取父组件,调用fn1的方法
    // this.$parent.fn1();
    // 调用子组件的方法:子组件是个数组,取第一个子组件的fn2方法
    // this.$children[0].fn2();
    // 调用子组件的方法:通过$refs.grandSon可以拿到GrandSon组件,同样可以调用fn2方法
    this.$refs.grandSon.fn2();
  }
};
</script>

GrandSon组件:

<template>
  <div>
    GrandSon
  </div>
</template>
<script>
export default {
  methods: {
    fn2() {
      alert("GrandSon222中的方法");
    }
  }
};
</script>
  1. eventBus 兄弟之间传递数据 $on e m i t V u e . p r o t o t y p e . emit Vue.prototype. emitVue.prototype.bus = new Vue; // b u s 是 公 共 的 。 t h i s . bus是公共的。 this. busthis.bus. o n ( ′ t e s t ′ , f u n c t i o n ( d a t a ) c o n s o l e . l o g ( d a t a ) ) ; / / 一 个 兄 弟 组 件 写 这 个 , 监 听 事 件 , 改 变 值 。 t h i s . on('test',function(data){console.log(data)}); //一个兄弟组件写这个,监听事件,改变值。 this. on(test,function(data)console.log(data));//this.bus, e m i t ( ′ t e s t ′ , 1000 ) ; / / 另 一 个 兄 弟 组 件 发 送 数 据 。 示 例 如 下 : A p p . v u e 入 口 文 件 中 定 义 emit('test',1000); //另一个兄弟组件发送数据。 示例如下: App.vue入口文件中定义 emit(test,1000);//App.vuebus:
<template>
  <div>
    <!--3.使用组件  -->
    <Parent></Parent>
  </div>
</template>
<style scoped>
</style>
<script>
// 1.引入组件
import Parent from "./components/Parent";
import Vue from "vue";
export default {
  data() {
    return {};
  },
  components: {
    Parent //2.注册组件
  }
};
Vue.prototype.$bus = new Vue(); //$bus是公共的
</script>

父组件:

<template>
  <div>
    Parent
    <Son :name="{a:1}" :age="2" @click="fn1"></Son>
    <Brother></Brother>
  </div>
</template>
<script>
import Son from "./Son";
import Brother from "./brother";
export default {
  data() {
    return {
      // money: "ABC" //传递的数据是大写
      money: 400 //传递的数据是大写
    };
  },
  components: {
    Son,
    Brother
  }
};
</script>

Son组件:

<template>
  <div>
    Son
    <button @click="change">改变兄弟</button>
  </div>
</template>
<script>
import GrandSon from "./GrandSon";
export default {
  methods: {
    change() {
      this.$bus.$emit("test", 1000);
      console.log(this.$bus);
    }
  }
};
</script>

Brother兄弟组件:

<template>
  <div>我是Son组件的兄弟组件{{m}}</div>
</template>
<script>
export default {
  mounted() {
    this.$bus.$on("test", data => { //使用箭头函数,this指向现在的Vue示例,因要改当前Brother组件的m的值。若不用箭头函数,this指向App.vue中定义的存储值的vue实例
    //   console.log(data, this);
      this.m = data;
    });
  },
  data() {
    return {
      m: 222
    };
  }
};
</script>

执行:
vue serve App.vue
点击修改兄弟按钮,就可以看到运行结果了。

这里没有package.json文件,因是用的快速原型开发,下载完后(全局安装)就可以通过vue serve App.vue(入口文件名)使用。

学习代码已上传git

你可能感兴趣的:(vue)