vue组件封装

1、封装组件的目的
把业务逻辑相同,高度重复的代码封装起来,为了提高代码的复用,减少代码的冗余。
2、组件使用
(1)引用

import navs from '@/views/nav/index'

(2)注册

components:{
  'v-nav':navs
}

(3)使用

< v-nav> < /v-nav>

3、组件三要素

属性:props属性 ,inheritAttrs属性
事件:event
插槽:slot

(1)属性:
数据从父组件传到子组件props属性

父对子传参,就需要用到 props,通常的 props 是这样的:

props:[‘data’,‘type’]

但是通用组件的的应用场景比较复杂,对 props 传递的参数应该添加一些验证规则,常用格式如下:

props:{
// 基础类型检测 (`null` 意思是任何类型都可以)
 propA: Number,
 // 多种类型
 propB: [String, Number]
  // 必传且是字符串
    propC: {
      type: String,
      required: true //必须传
    },
     // 数字,有默认值
    propD: {
      type: Number,
      default: 100  //default默认值,默认是100
    },
    // 数组/对象的默认值应当由一个工厂函数返回
    propE: {
      type: Object,
      default: function () {
        return { message: 'hello' }
      }
    },
    // 自定义验证函数
    propF: {
      validator: function (value) {
        return value > 10
      }
    }

inheritAttrs属性
vue官网对于inheritAttrs的属性解释:如果你不希望组件的根元素继承特性,你可以在组件的选项中设置 inheritAttrs: false。

举个例子来验证一下。

父组件 parent-component.vue

<template>
 <div class="parent">
    <child-component aaa="1111"></child-component>
  </div>
</template>
<script>
import ChildComponent from './child-component'
export default {
  components: {
    ChildComponent
  }
}
</script>

子组件 child-component.vue 设置 inheritAttrs: true(默认)

<template>
  <div class="child">子组件</div>
</template>
<script>
export default {
  inheritAttrs: true,
  mounted() {
    console.log('this.$attrs', this.$attrs)
  }
}
</script>

最后渲染的结果:

Elements

Console
在这里插入图片描述
子组件 child-component.vue 设置 inheritAttrs: false

<template>
  <div class="child">子组件</div>
</template>
<script>
export default {
  inheritAttrs: fasle,
  mounted() {
    console.log('this.$attrs', this.$attrs)
  }
}
</script>

最后渲染的结果:

Elements
在这里插入图片描述
Console
在这里插入图片描述

总结:

由上述例子可以看出,前提:子组件的props中未注册父组件传递过来的属性。

1.当设置inheritAttrs: true(默认)时,子组件的顶层标签元素中(本例子的div元素)会渲染出父组件传递过来的属性(本例子的aaa=“1111”)。

2.当设置inheritAttrs: false时,子组件的顶层标签元素中(本例子的div元素)不会渲染出父组件传递过来的属性(本例子的aaa=“1111”)。

3.不管inheritAttrs为true或者false,子组件中都能通过$attrs属性获取到父组件中传递过来的属性。

(2)events:子组件触发父组件事件
在通用组件中,通常会需要有各种事件,比如复选框的 change 事件,或者组件中某个按钮的 click 事件,有时子组件需要触发一个事件,并传递给父组件。

// 子组件方法:触发父组件方法,并传递参数data到父组件
handleSubmit(data){
    this.$emit('submitToParent', data)
}
// 父组件调用子组件
<child-component @submitToParent="parentSubmit"></child-component>
... ...
// 父组件中被触发的方法,接受到子组件传来的参数
parentSubmit(data){
    // 父组件的逻辑处理
}

父组件中的逻辑要放在父组件处理,子组件基于父组件的数据做的逻辑放在子组件中处理; 这样既降低了耦合性,也保证了各自的数据不被污染

(3)留一个 slot
一个通用组件,往往不能够完美的适应所有应用场景 所以在封装组件的时候,只需要完成组件 80% 的功能,剩下的 20% 让父组件通过 solt 解决。
例如:
上面是一个通用组件,在某些场景中,右侧的按钮是 “处理” 和 “委托”。在另外的场景中,按钮需要换成 “查看” 或者 “删除” 在封装组件的时候,就不用写按钮,只需要在合适的位置留一个 slot,将按钮的位置留出来,然后在父组件写入按钮。

子组件
<div class="child-btn">
    <!-- 具名插槽 -->
    <slot name="button"></slot>
    <!-- 匿名插槽(每个组件只能有一个) -->
    <slot><slot>
</div>
 
父组件
<child>
    <!-- 对应子组件中button的插槽 -->
    <button slot="button">slot按钮</button>
</child>

开发通用组件的时候,只要不是独立性很高的组件,建议都留一个 slot,即使还没想好用来干什么。

开发过程中,常常需要在子组件内添加新的内容,这时候可以在子组件内部留一个或者多个插口

(4)子组件改变父组件的数据
当我们把父元素的数据给子组件时,要传一个非基础类型,即传递对象or数组,子组件通过访问对象中的属性操作数据,因为对象和数组是传引用,所以在子组件中修改的时候,父组件也会同步改变,如下:

// 父组件要props传递给子组件的数据
data:{
    info:'父组件信息'
}
 
// 子组件
 <template id="tpl">
    <div>
        <button @click="change">change</button>
        <p>{{data.info}}</p>
    </div>
</template>
... 省略部分无关代码 ...
props:['data'],
methods:{
    change(){
        this.data.info = 'change info'
    }
}

当子组件点击change按钮改变数据的时候,父组件也会同步改变
(5)、vue组件封装v-model
首先来理解下 v-model

<input v-model="something">
<input
  v-bind:value="something"
  v-on:input="something = $event.target.value">

与组件一起使用时,简化为

<custom-input
  :value="something"
  @input="value => { something = value }">
</custom-input>

所以对于一个组件来说v-model,它应该

1.接受value道具
2.用新值发出一个input事件

一个组件上的 v-model 默认会利用名为 value 的 prop 和名为 input 的事件,但是像单选框、复选框等类型的输入控件可能会将 value 特性用于不同的目。model 选项可以用来避免这样的冲突:

<template>
    <input
      type="checkbox"
      v-bind:checked="checked"
      v-on:change="$emit('change', $event.target.checked)"
    >
</template>
<script>
export default {
    name: 'base-checkbox',
    model:{
        prop: 'checked',
        event: 'change'
    },
    props: {checked: Boolean}
}

现在在这个组件上使用 v-model 的时候:

<base-checkbox v-model="lovingVue"></base-checkbox>

这里的 lovingVue 的值将会传入这个名为 checked 的 prop。同时当 触发一个 change 事件并附带一个新的值的时候,这个 lovingVue 的属性将会被更新。

我们来继续看嵌套组件时怎么再封装一个v-model,我们新建一个组件special-checkbox里面包含上面的组件base-checkbox,如何将子父组件的v-model打通

<template>
  <base-checkbox v-model="newlovingVue"></base-checkbox>
</template>
<script>
export default {
    name: 'special-checkbox ',
    model:{
        prop:'lovingVue',//要存在于proops
        event:'change'//当组件的值发生改变时要emit的事件名
    },
    props: ['lovingVue'],
    data:function(){
        return{//要重新定义一个data,赋值为props中的值,因为组件时单数据流,不能直接修改props
            newlovingVue:this.lovingVue
        } 
    },
    watch:{//这里检测data中的值,一旦发生变化就提交事件到父组件
        newlovingVue:function(newVal,oldVal){
            this.$emit('change',newVal)
        }
    }
}
</script>

现在使用这个二次封装的组件的时候

<special-checkbox v-model="lovingVue"></base-checkbox>

(6)、子组件发射事件,父组件接收事件

自组件通过this.$emit(“inChange”,index)进行发射事件,inChange为事件名称,index为参数。

<template>
  <div class="child">
    <v-switch v-model="item.in_switch" :label="item.in_switch? 'on':'off' " @change="inChange(index)"></v-switch>
  </div>
</template>
<script>

export default {
 	 props:{
        item: Object,
        index: Number
    },
    
    methods:{
        inChange(index){
            // 父组件监听 inChange事件,将index传过去,将处理逻辑放在父组件
            this.$emit("inChange",index);
        }
    }
}
</script>

父组件接收

<template>
  <div class="parent">
	  // 注意这里inChange的参数可有可无
	  <child :item="item" :index="index" @inChange="inChange" />
  </div>
</template>
import child from "../child";

export default {
  
  components: {
   child
  },
  data() {
	    return {
		    item: {
		          in_switch: true,
		        },
		    index: 0
	    }
	  },
  methods:{
    // inchange事件处理,有index参数
    inChange(index){
       console.log( index)
    },
  }
}
</script>

你可能感兴趣的:(前端,vue)