VUE系列-Vue中组件的应用(三)

大家好,我是前端岚枫,一枚二线城市的程序媛,上篇主要跟分享了《VUE系列-Vue核心应用(二)》,今天主要跟大家分享我整理的vue的组件相关的,主要包括组件声明、组件种类、组件间的几种数据通信方式及一些常用的面试题,vue面试题相关的答案我后期会专门整理分享。

Vue中的组件是开发中非常重要的一部分,组件化的好处:方便维护、方便复用 、 减少不必要的渲染

一.组件的声明

1.1 全局组件

<my-button></my-button>  
Vue.component('my-button',{
  template:'<button>点我啊</button>'
})
let vm = new Vue({
  el:'#app'
})

1.2 局部组件

<my-button></my-button>
let vm = new Vue({
  el:'#app',
  components:{
      'MyButton':{
          template:'<button>按钮</button>'
      }
  }
});

HTML不支持自闭合的自定义元素,在DOM模板里永远不要使用自闭合组件,在HTML中也不支持MyButton的写法,所以组件调用全部使用短横线连接的方式!

二.组件的数据

在组件中的数据必须是函数的形式,目的为了保证每个组件间的数据数据相互独立

'MyButton':{
    data(){
        return {content:'按钮'}
    },
    template:'<button>{{content}}</button>'
}

三.组件的属性应用及校验

  • 属性应用
<my-button button-content="按钮"></my-button>
components:{
    'MyButton':{
        props:['buttonContent'],
        template:'<button>{{buttonContent}}</button>'
    }
}

属性在组件标签上需要使用短横线命名法,在组件中声明需要采用驼峰命名法

  • 属性校验
<my-button button-content="按钮" :number="'1'"></my-button>
components:{
    'MyButton':{
        props:{
            buttonContent:String,
            arr:{
                type:Array,
                default:()=>([])
            },
            number:{
                type:Number,
                validator:(value)=>{
                    return typeof value == 'number'
                }
            },

        },
        template:'<button>{{buttonContent}} {{arr}} {{number}}</button>'
    }
}

四.Vue组件间的通信

快速原型开发: 可以快速识别.vue文件封装组件插件等功能,也是基于Vue-Cli

sudo npm install @vue/cli -g
sudo npm install -g @vue/cli-service-global
vue serve App.vue

4.1 Props传递数据

components
   ├── Grandson1.vue // 孙子1
   ├── Grandson2.vue // 孙子2
   ├── Parent.vue   // 父亲
   ├── Son1.vue     // 儿子1
   └── Son2.vue     // 儿子2

在父组件中使用儿子组件

<template>
 <div>
  父组件:{{mny}}
  <Son1 :mny="mny"></Son1>
 </div>
</template>
<script>
import Son1 from "./Son1";
export default {
 components: {
  Son1
 },
 data() {
  return { mny: 100 };
 }
};
</script>

子组件接受父组件的属性

<template>
 <div>子组件1: {{mny}}</div>
</template>
<script>
export default {
 props: {
  mny: {
   type: Number
  }
 }
};
</script>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <div id="app">
        <!-- 将父组件的数据 通过儿子的属性传入 -->
        {a:1,b:2,c:3}
        <my a=1 b=2 c=3></my>
    </div>
    <script src="node_modules/vue/dist/vue.js"></script>
    <script>
        // 单向数据流 父组件将数据传递给儿子
        //  每个组件都应该有三部分 1) html  2) js 3) 样式
        let component = {
            template:`<div>儿子 {{mny}} <button @click="change">更改</button></div>`,
            // props:['mny'], // this.mny = 100
            props:{ // 表示 我要对属性进行校验
                mny:{
                    // 普通类型直接写 默认值即可 如果是对象或者是数组 必须写成函数返回值的效果
                    type:Object, // 类型校验
                    default:()=>({a:1})// 默认值校验
                    // required:true //必填校验
                }
            },  
            methods:{
                change(){
                    this.mny = 200; // 不靠谱,不应该儿子去更改父亲数据
                }
            },
            data(){
                return {m:1}
            },
            beforeCreate(){ // 这里可以描述 父子关系 
                console.log(this.$parent.$children[0] === this);
            },
        }
        // Vue.component('my',component)
        let vm = new Vue({
            el:'#app',
            beforeCreate(){
                console.log(this.$children);
                debugger;
            },
            data:{
                arr:[1,2,3],
                mny:'100'
            },
            components:{ // 在实例上注册组件
                my:component
            }
        });
        // 组件的使用 三部 1)导入一个组件 2) 注册 3) 使用 在当前组件定义的模板中使用
    </script>
</body>
</html>

4.2 $emit使用

子组件触发父组件方法,通过回调的方式将修改的内容传递给父组件

<template>
 <div>
  父组件:{{mny}}
  <Son1 :mny="mny" @input="change"></Son1>
 </div>
</template>
<script>
import Son1 from "./Son1";
export default {
 methods: {
  change(mny) {
   this.mny = mny;
  }
 },
 components: {
  Son1
 },
 data() {
  return { mny: 100 };
 }
};
</script>

子组件触发绑定自己身上的方法

<template>
 <div>
  子组件1: {{mny}}
  <button @click="$emit('input',200)">更改</button>
 </div>
</template>
<script>
export default {
 props: {
  mny: {
   type: Number
  }
 }
};
</script>

这里的主要目的就是同步父子组件的数据,->语法糖的写法

.sync

<Son1 :mny.sync="mny"></Son1>
<!-- 触发的事件名 update:(绑定.sync属性的名字) -->
<button @click="$emit('update:mny',200)">更改</button>

v-model

<Son1 v-model="mny"></Son1>
<template>
 <div>
  子组件1: {{value}} // 触发的事件只能是input
  <button @click="$emit('input',200)">更改</button>
 </div>
</template>
<script>
export default {
 props: {
  value: { // 接收到的属性名只能叫value
   type: Number
  }
 }
};
</script>

4.3 parent 、children

继续将属性传递

<Grandson1 :value="value"></Grandson1>
<template>
 <div>
  孙子:{{value}}
  <!-- 调用父组件的input事件 -->
  <button @click="$parent.$emit('input',200)">更改</button>
 </div>
</template>
<script>
export default {
 props: {
  value: {
   type: Number
  }
 }
};
</script>

如果层级很深那么就会出现parent.parent.....我们可以封装一个$dispatch方法向上进行派发

$dispatch

Vue.prototype.$dispatch = function $dispatch(eventName, data) {
  let parent = this.$parent;
  while (parent) {
    parent.$emit(eventName, data);
    parent = parent.$parent;
  }
};

既然能向上派发那同样可以向下进行派发

$broadcast

Vue.prototype.$broadcast = function $broadcast(eventName, data) {
  const broadcast = function () {
    this.$children.forEach((child) => {
      child.$emit(eventName, data);
      if (child.$children) {
        $broadcast.call(child, eventName, data);
      }
    });
  };
  broadcast.call(this, eventName, data);
};

4.4 attrs、listeners

$attrs

批量向下传入属性

<Son2 name="小珠峰" age="10"></Son2>

<!-- 可以在son2组件中使用$attrs属性,可以将属性继续向下传递 -->
<div>
  儿子2: {{$attrs.name}}
  <Grandson2 v-bind="$attrs"></Grandson2>
</div>


<template>
 <div>孙子:{{$attrs}}</div>
</template>

$listeners

批量向下传入方法

<Son2 name="小珠峰" age="10" @click="()=>{this.mny = 500}"></Son2>
<!-- 可以在son2组件中使用listeners属性,可以将方法继续向下传递 -->
<Grandson2 v-bind="$attrs" v-on="$listeners"></Grandson2>

<button @click="$listeners.click()">更改</button>

4.5 Provide & Inject

Provide

在父级中注入数据

provide() {
  return { parentMsg: "父亲" };
},

Inject

在任意子组件中可以注入父级数据

inject: ["parentMsg"] // 会将数据挂载在当前实例上

provide inject 和 context (可以在父组件中声明一个公共数据),在子组件中可以注入原理 (比较混乱,名称问题 他不会在业务代码中使用) 组件库 多级通信为了方便你可以使用provide

4.6 Ref使用

获取组件实例
ref 获取真实dom元素,如果放到组件上 代表的是 当前组件的实例 ,父组件中可以直接获取子组件的方法或者数据

<Grandson2 v-bind="$attrs" v-on="$listeners" ref="grand2"></Grandson2>
mounted() { // 获取组件定义的属性
  console.log(this.$refs.grand2.name);
}

4.7 EventBus

用于跨组件通知(不复杂的项目可以使用这种方式)

Vue.prototype.$bus = new Vue();

eventbus (parent,children) 绑定on 只能通过绑定on的那个组件来触发 (混乱)

Son2组件和Grandson1相互通信

 mounted() {
  this.$bus.$on("my", data => {
   console.log(data);
  });
 },
mounted() {
  this.$nextTick(() => {
   this.$bus.$emit("my", "我是Grandson1");
  });
 },

五.面试题环节

  • 组件间通信方式
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章