Vue入门笔记之组件化

(一) 组件化 概述

在一个页面中存在大量的处理逻辑, 如果全部放在一起, 处理起来就会变得非常复杂, 且不利于后续维护以及扩展

组件化思想: 将页面拆分成一个个小的功能块, 每个功能块完成属于自己这部分独立的功能, 那么之后整个页面的管路和维护就变得非常容易
在这里插入图片描述
组件化是Vue.js中的重要思想

  • 它提供一种抽象, 可以发出一个个独立可复用的小组件来构造整个应用
  • 任何应用都会被抽象成一颗组件树. (图摘自Vue官方文档)
    在这里插入图片描述

Vue实例与组件的关系

  • 一个Vue项目是由一个根实例和多个组件组成
  • 组件是可复用的 Vue 实例, 因此组件与Vue实例内部极其相似, 拥有 data、methods、computed、生命周期函数等等选项
  • 组件中的data选项必须是一个函数, 且返回一个对象(内部存储数据). 组件是可复用的, 如果data选项是一个对象的话, 多个组件使用同一份data数据, 造成数据交叉错误.



(二) 组件注册

组件的使用分成三个步骤:

  • 创建组件构造器
  • 注册组件
  • 使用组件
1. 注册全局组件

全局组件可以在多个Vue实例中使用

<div id="app">
   <!-- 3.在 app Vue实例中使用自定义组件 -->
   <my-component></my-component>
</div>
<script>
	// 1.创建组件构造器对象
    const componentConstructor = Vue.extend({
    	// template: 自定义组件模板, 最终会替换挂载的元素(组件名). 挂载元素的内容都将被忽略,除非模板的内容有分发插槽
	    template: `
	       <div>
	          <h3>我是标题</h3>
	          <p>我是内容: 哈哈哈哈</p>
	          <h3>我是页尾</h3>
	       </div>
	    `
	 })

     // 2.注册全局组件 Vue.component(组件标签名称, 组件构造器)
     Vue.component('my-component', componentConstructor);

     const app = new Vue({
        el: '#app'
     })
   </script>

在这里插入图片描述

2. 注册局部组件

局部组件:在Vue实例的components选项中注册组件, 组件只能在此Vue实例中使用

const app = new Vue({
   el: '#app',
   // components 选项: 注册局部组件, 组件名称: 组件构造器
   components: {
      myComponent: componentConstructor
   }
})

3. 注册组件简写

注册组件的语法糖

// 注册全局组件 Vue.component('组件名', { 传入Vue.extend()的对象 })
Vue.component('global-component', {
	template: `
		<div>全局组件</div>
	`
})

// 注册局部组件
const app = new Vue({
	el: '#app',
	components: {
		partComponent: {
			template: `<div>局部组件</div>`
		}
	}
})

注册组件的模板抽离, 使用template标签

<div id="app">
   <parent-component></parent-component>
</div>
<template id="part">
	<div>局部组件</div>
</template>
const partComponet = {
	template: '#part' // css选择器 或 HTMLElement
}

const app = new Vue({
	el: '#app',
	components: {
		partComponet
	}
})



(三) 父子组件

1. 注册父子组件

在组件实例中通过components选项 注册子组件, 且在template选项中使用子组件

<div id="app">
   <parent-component></parent-component>
</div>

<template id="child">
   <p>子组件内容</p>
</template>

<template id="parent">
   <div>
      <h3>父组件标题</h3>

      <!-- 父组件中使用子组件 -->
      <child-component></child-component>

      <h3>父组件页尾</h3>
   </div>
</template>
// 创建子组件对象
const childComponent = {
   template: '#child'
}

// 创建父组件对象
const parentComponent = {
   // 父组件中局部注册子组件
   components: {
      childComponent
   },
   template: '#parent'
}

const app = new Vue({
   el: '#app',
   // Vue实例(根组件)中局部注册父组件
   components: {
      parentComponent
   }
})

在这里插入图片描述

注意:

2. 父子组件的通信

在Vue开发中, 往往一些数据需要从父组件传递到子组件, 或者 数据从子组件传递到父组件.Vue官方分别对这两种情况提供传递方式:

  • 通过props向子组件传递数据
  • 通过自定义事件向父组件发送消息

在这里插入图片描述

Props: 父组件向子组件传递数据

  • 父组件通过 v-bind:变量名 = “变量” 传递给子组件
    • 变量名: 自定义变量名, v-bind 不支持驼峰标识, 需要做转换: v-bind:myInfo --> v-bind:my-info
    • 变量: 父组件data函数返回的对象
  • 子组件通过props 选项接受父组件传来的变量, props选项的类型为:
    • Array数组: props: [‘自定义变量名’, …]
    • Object对象:
      props: {
      	变量名: {
      		type: Array,	// 对父组件传递的变量进行类型校验(String, Number, Boolean, Array, Object, Data)
       		default() {		// 对父组件传递的变量初始化默认值, 当变量类型为 Object/Array时, default属性是一个函数
       			return []
       		},			
       		required: true	// boolean, 是否必须传递
      	}, ...
      }
      

将父组件中movies数据传递给子组件显示

<!-- Vue实例挂彩目标 -->
<div id="app">
   <parent-component></parent-component>
</div>

<!-- 子组件template模板抽离 -->
<template id="child">
   <ul>
      <li v-for="item in movies" :key="item">{{item}}</li>
   </ul>
</template>

<!-- 父组件template模板抽离 -->
<template id="parent">
   <!-- 父组件通过 v-bind:变量名 = "变量" 传递给 子组件-->
   <child-component :movies="movies"></child-component>
</template>
// 创建子组件对象
const childComponent = {
   template: '#child',
   // 子组件通过props 选项接受父组件传来的变量
   props: {
      movies: {
      	type: Array,
      	default() {
      		return []
      	},
      	required: true
      }
   }
}

// 创建父组件对象
const parentComponent = {
   template: '#parent',
   components: {
      childComponent
   },
   data() {
      return {
         movies: ['海贼王', '火影忍者', '魁拔', '大鱼海棠']
      }
   }
}

const app = new Vue({
   el: '#app',
   components: {
      parentComponent
   }
})

在这里插入图片描述
单向数据流:
所有的porp都使得器父子之间形成了一个单向下行绑定: 父组件prop的更新会向下流动到子组件中(实时更新). 但反之不行, 这样做的目的是防止子组件意外变更父组件的状态, 从而导致数据流向难以理解.

单向下行绑定, 意味着我们不应该在子组件内部改变prop(控制台会发出警告), 如果非得改变porp的值, 那么可以将它定义为本地data数据 或 computed计算属性


$emit event: 子组件通过向父组件发送自定义事件来传递数据

  • 子组件通过 $emit(‘自定义事件名称’, …data) 向父组件发送自定事件, 同时传递数据
  • 父组件通过 v-on:自定义事件名称 来监听子组件事件

父组件打印输出子组件点击分类对象

<!-- Vue实例挂彩目标 -->
<div id="app">
   <parent-compent></parent-compent>
</div>

<!-- 子组件template模板抽离 -->
<template id="child">
   <ul>
      <li v-for="item in categories" :key="item.id">
         <a href="javascript:void(0)" @click="handleClick(item)">{{item.id}}.{{item.name}}</a>
      </li>
   </ul>
</template>

<!-- 父组件template模板抽离 -->
<template id="parent">
	<!-- 父组件通过 v-on:自定义事件名称 来监听子组件事件 -->
   <child-component @on-click="handleClick"></child-component>
</template>
// 创建子组件对象
const childComponent = {
   template: '#child',
   data() {
      return {
         categories: [
            {id: 1, name: '热门推荐'},
            {id: 2, name: '手机数码'},
            {id: 3, name: '家用家电'},
            {id: 4, name: '电脑办公'},
         ]
      }
   },
   methods: {
      handleClick(item) {
         // 子组件发送自定义事件 on-click, 且传递数据 分类对象
         this.$emit('on-click', item)
      }
   }
}

// 创建父组件对象
const parentCompent = {
   template: '#parent',
   components: {
      childComponent
   },
   methods: {
      handleClick(item) {
         console.log(JSON.stringify(item))
      }
   }
}

const app = new Vue({
   el: '#app',
   components: {
      parentComponent
   }
})

在这里插入图片描述

3. 父子组件的访问方式
  • 父组件访问子组件: 使用$children 或 $refs
  • 子组件访问父组件: 使用$parent

$children: this.$children 获取到是一个数组对象, 它包含当前组件所有的子组件对象

<template id="parent">
   <div>
      <!-- 子组件复用三次 -->
      <child-component></child-component>
      <child-component></child-component>
      <child-component></child-component>
   </div>
</template>
const parentCompent = {
   template: '#parent',
   components: {
      childComponent
   },
   mounted() {
      console.log(this.$children) // 长度为3的子组件数组对象
      console.log(this.$children[0].message) // 输出第一个子组件的message对象
      this.$children[0].showInfo() // 调用第一个子组件的showInfo方法
   }
}

在这里插入图片描述

$refs: 需要在子组件上指定ref属性, 然后通过this.$refs.ref 获取到指定的子组件对象

<template id="parent">
   <div>
      <!-- 子组件添加ref属性 -->
      <child-component ref="childComponent"></child-component>
   </div>
</template>
const parentCompent = {
   template: '#parent',
   components: {
      childComponent
   },
   mounted() {
      // 通过this.$refs.ref 获取到指定的子组件对象
      console.log(this.$refs.childComponent)
      this.$refs.childComponent.showInfo()
      console.log(this.$refs.childComponent.message)
   }
}

在这里插入图片描述

$parent: this.$parent 获取到父组件对象

const childComponent = {
   template: '#child',
   mounted() {
      console.log(this.$parent) // this.$parent 获取到父组件对象
      this.$parent.showInfo() // 调用父组件的showInfo方法
      console.log(this.$parent.message) // 打印输入父组件的message对象
   }
}

在这里插入图片描述

$root: this.$root 获取到根Vue实例



(三) Slot 插槽

1. 插槽的基本使用

Slot: Vue实现了一套内容分发的API, 将<slot>元素作为承接分发内容的出口. 组件的插槽也是为了让我们封装的组件更加具有扩展性, 可以让使用者在组件内部自定义展示内容

<div id="app">
   <child-component>
      <button>按钮</button>
   </child-component><hr>

   <child-component>
      <a href="#">www.baidu.com</a>
   </child-component><hr>
   
   <!-- 组件内没元素: 使用插槽默认值 -->
   <child-component></child-component>
</div>

<template id="child">
   <div>
      <h3>组件标题</h3>
      <p>组件内容</p>
      <slot>插槽具有默认值</slot>
   </div>
</template>
const childComponent = {
   template: '#child',
}

const app = new Vue({
   el: '#app',
   components: {
      childComponent
   }
})

在这里插入图片描述

2. 具名插槽

组件内可以拥有多个插槽内容, 使用 name属性区分, 使用 slot属性指定替换的插槽

<div id="app">
   <child-component>
      <!-- 添加slot属性, 指定替换的插槽 -->
      <span slot="center"><input type="text" placeholder="搜索"></span>
   </child-component>
</div>

<template id="child">
   <div>
      <!-- 添加name属性, 区分插槽 -->
      <slot name="left">左边默认插槽</slot>
      <slot name="center">中间默认插槽</slot>
      <slot name="right">右边默认插槽</slot>
   </div>
</template>

在这里插入图片描述

3. 作用域插槽

编译作用域: 父组件模板的所有东西都会在父级作用域内编译, 子组件模板的所有东西都会在子级作用域内编译

作用域插槽: 父组件替换插槽的标签, 但是内容由子组件来提供. 更通俗的来讲: 父组件使用子组件插槽时, 需要用到子组件的数据

  • 子组件定义插槽传递数据: v-bind:自定义变量名 = “子级作用域的变量”
  • 父组件使用<template>标签接受数据: v-slot:插槽名称(默认default) = “插槽属性名称”, 通过 插槽属性名称.自定义变量名 访问数据

父组件通过不同的标签显示子组件的movies

<h3>默认插槽: ui标签展示movies</h3>
<child-component>
</child-component><hr>

<h3>自定义插槽: ui + a标签展示movies</h3>
<child-component>
	<!-- <template>标签 + v-slot:插槽名称(默认default) = "插槽属性名称" -->
   <template v-slot:default="slotPorp" >
   	  <!-- 通过 插槽属性名称.自定义变量名 访问数据 -->
      <li v-for="item in slotPorp.data"><a href="#">{{item}}</a></li>
   </template>
</child-component><hr>

<template id="child">
   <div>
   	  <!-- v-bind:自定义变量名 = "子级作用域的变量" -->
      <slot :data="movies" name="center">
         <ul>
            <li v-for="item in movies">{{item}}</li>
         </ul>
      </slot>
   </div>
</template>
const childComponent = {
   template: '#child',
   data() {
      return {
         movies: ['海贼王', '火影忍者', '魁拔', '大鱼海棠']
      }
   }
}

const app = new Vue({
   el: '#app',
   components: {
      childComponent
   }
})

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值