什么是组件

组件(Component)是 Vue.js 最强大的功能之一。(好比电脑中的每一个元件(键盘,鼠标,CPU),它是一个具有独立的逻辑和功能或界面,同时又能根据规定的接口规则进行互相融合,变成一个完整的应用)

页面就是由一个个类似这样的部分组成的,比如:导航、列表、弹窗、下拉菜单等,页面只不过是这些组件的容器,组件自由结合形成功能完整的界面,当不需要某个组件,或者想要替换某个组件时,可以随时进行替换和删除,而不影响整个应用的运行

前端组件化的核心思想就是将巨大复杂的东西拆分成粒度合理的小东西

使用组件的好处

提高开发效率,方便重复使用,简化调试步骤,提升整个项目的可维护性,便于协同开发

前端组件化的必要性

多年以前前端开发者们就一直尝试组件化的道路上不断探索,从一开始YUI、ExtJs到现在的Angular 、React、Vue、Bootstrap等,前端的组件化道路从来没有停止过

组件的特性

高内聚性,组件功能必须是完整的,如我要实现下拉菜单功能,那在下拉菜单这个组件中,就把下拉菜单所需要的所有功能全部实现

低耦合性,通俗点说,代码独立不会和项目中的其他代码发生冲突,实际工程中,我们经常会涉及到团队协作,传统按照业务线去编写代码的方式,就很容易相互冲突,所以运用组件化方式就可以大大避免这种冲突的存在

每个组件都有自己清晰的职责,完整的功能,较低的耦合便于单元测试和重复利用

vue中的组件

Vue中组件是一个自定义标签,vue.js的编译器为它添加特殊功能,也可以扩展原生的html元素,封装成可重复使用的代码

vue组件的基本组成(单文件组件):Js,css,html 存在一个文件中,是一个单文件组件,下面vue模板文件里的Hello.vue就是一个单文件组件

<template>
<div class="hello">
<h2>{{ msg }}</h2>
</div>
</template> <script>
export default {
name: 'hello',
data () {
return {
msg: 'Hello Vue'
}
}
}
</script> <!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h1, h2 {
font-weight: normal;
}
</style>

组件的引入方式一

首先定义两个组件:Hello.vue和HelloWorld.vue

<template>
<div class="hello">
<h2>{{ msg }}</h2>
</div>
</template> <script>
export default {
name: 'hello',
data () {
return {
msg: 'Hello Vue'
}
}
}
</script> <!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h1, h2 {
font-weight: normal;
}
</style>
<template>
<div class="helloWorld">
{{ helloWorld }}
</div>
</template> <script type="text/ecmascript-6">
export default {
name: 'HelloWorld',
data () {
return {
helloWorld: 'Hello World'
}
}
}
</script> <style scoped></style>

然后再父组件中引入这两个子组件,引入的时候需要注意标签名的写法

<template>
<div id="app">
{{ message }}
<hello></hello> <!-- 如果组件名是这样的形式:AaaBbb,那么在标签这里应该这样写:aaa-bbb的格式 -->
<hello-world></hello-world>
</div>
</template> <script>
import Hello from './components/Hello'
import HelloWorld from './components/HelloWorld'
export default {
components: {
HelloWorld,
Hello
},
name: 'app',
data () {
return {
message: 'Welcome'
}
}
}
</script> <style>
</style>

组件引入方式二(动态组件)

这种引入方式的好处就是可以动态的引入组件,就是将组件作为一个变量,下面示例中如果comToRender这个变量的值发生改变那么引入的组件就发生改变

<keep-alive></keep-alive>标签,这个标签的作用会将子组件缓存起来,比如两个组件之间相互切换,当a组件,切换到b组件的时候,a组件会被缓存起来,切换回a组件的时候,b组件缓存起来

下面来做一个示例,点击切换按钮后,切换引入的组件

<template>
<div id="app">
<button v-on:click="change">切换</button>
<!-- 引入组件还有下面这种方式:这表示div标签就是组件Hello -->
<keep-alive><div v-bind:is="comToRender"></div></keep-alive>
</div>
</template> <script>
import Hello from './components/Hello'
import HelloWorld from './components/HelloWorld'
export default {
components: {
HelloWorld,
Hello
},
name: 'app',
data () {
return {
message: 'Welcome',
comToRender: 'hello'
}
},
methods: {
change () {
if (this.comToRender === 'hello') {
this.comToRender = 'hello-world'
} else {
this.comToRender = 'hello'
}
}
}
}
</script> <style>
</style>

测试,点击切换的时候即可实现两个组件之间切换

组件之间进行通信

组件间通信基本原则——不要在子组件中直接修改父组件的状态数据,数据在哪, 更新数据的行为(函数)就应该定义在哪

父子组件之间是需要进行通信的,比如select列表,父组件需要告诉子组件有哪些选项,子组件也需要向父组件传递,比如子组件选择了哪个选项,需要告诉父组件

prop 是父组件用来传递数据的一个自定义属性。父组件的数据需要通过 props 把数据传给子组件,子组件需要显式地用 props 选项声明 "prop":

父子组件通信示意图

组件之间的通信示例一父组件向子组件传递数据:prop 是单向绑定的:当父组件的属性变化时,将传导给子组件,但是不会反过来

父组件中传递数据的方法有几种

直接将一个固定的值传递给子组件,下面是将一个字符串为5的一个值传给了hello-world这个子组件

<hello-world number-size="5"></hello-world>

如果是动态引入的组件,写法也一样,他会将这个值传递给每个子组件(引入那个子组件就传给那个子组件)

<keep-alive><div v-bind:is="comToRender" number-size="5"></div></keep-alive>

使用动态绑定的方式将父组件的某个变量值传递给子组件,比如下面将父组件中的number这个变量传递给子组件使用v-bind动态绑定的方式

<hello-world v-bind:number-size="number"></hello-world>
<keep-alive><div v-bind:is="comToRender" v-bind:number-size="number"></div></keep-alive>

在传递数据的时候也能规定传递的数据类型,下面表示传递的是一个Number类型的数据,子组件接收的时候也要规定有Number类型,否则报错

<keep-alive><div v-bind:is="comToRender" v-bind:number-size.number="number"></div></keep-alive>

子组件接收父组件传递过来的数据

需要知道的是,子组件接收到数据之后就相当于data里面有了这个变量,可以在任意地方进行使用这个变量

首先需要将父组件传递过来的数据进行注册,使用props进行注册,props接收两种类型的值,一种是数组,一种是对象,对象可以做的事情比较多

首先来看接收数组的形式

props: ['number-size']
<template>
<div class="helloWorld">
{{ helloWorld }}
<p>{{ numberSize }}</p>
</div>
</template> <script type="text/ecmascript-6">
export default {
props: ['number-size'],
name: 'HelloWorld',
data () {
return {
helloWorld: 'Hello World'
}
}
}
</script> <style scoped></style>

测试结果是可以将numberSize打印出来的

如果使用对象的方式去接收数据,可以规定接收的数据类型,比如下面是Number,如果子组件规定了只接收Number,那么父组件只能传递Number类型的数据给子组件,否则会报错

props: {
'number-size': Number
}

当然可以接收多种数据类型,下面表示可以接受Number和String类型的数据,父组件就要传递这两种数据类型的数据,否则就报错了

props: {
'number-size': [Number, String]
}

组件之间的通信—子组件向外传递事件(自定义事件)

父组件是使用 props 传递数据给子组件,但如果子组件要把数据传递回去,就需要使用自定义事件!

我们可以使用 v-on 绑定自定义事件, 每个 Vue 实例都实现了事件接口(Events interface),即:

使用 $on(eventName) 监听事件

使用 $emit(eventName) 触发事件

另外,父组件可以在使用子组件的地方直接用 v-on 来监听子组件触发的事件。

首先子组件定义一个事件,这个事件向父组件提交子组件的数据

<template>
<div class="helloWorld">
<p>{{ helloWorld }}</p>
<button v-on:click="emitMyEvent">提交</button>
</div>
</template> <script type="text/ecmascript-6">
export default {
name: 'HelloWorld',
data () {
return {
helloWorld: 'Hello World'
}
},
methods: {
emitMyEvent () {
this.$emit('my-event', this.helloWorld)
}
}
}
</script> <style scoped></style>

父组件中接收子组件传递过来的数据

<template>
<div id="app">
<button v-on:click="change">切换</button>
<keep-alive><div v-bind:is="comToRender" v-on:my-event="getMyEvent"></div></keep-alive>
</div>
</template> <script>
import Hello from './components/Hello'
import HelloWorld from './components/HelloWorld'
export default {
components: {
HelloWorld,
Hello
},
name: 'app',
data () {
return {
message: 'Welcome',
comToRender: 'hello',
number: 3
}
},
methods: {
change () {
if (this.comToRender === 'hello') {
this.comToRender = 'hello-world'
} else {
this.comToRender = 'hello'
}
},
getMyEvent (param) {
console.log(param)
}
}
}
</script> <style>
</style>

使用 Slot 分发内容

使用 Slot 分发内容也是一种组件之间通信的方式,此方式用于父组件向子组件传递`标签数据`

在父组件中的的模板标签内可以插入元素,子组件中使用slot接收父组件插入的元素,这个功能的使用场景,比如这个子组件是一个footer或者header,这样就可以很方便的在父组件中向子组件插入元素

<template>
<div>
<slot name="xxx">不确定的标签结构 1</slot>
<div>组件确定的标签结构</div>
<slot name="yyy">不确定的标签结构 2</slot>
</div> </template> <script type="text/ecmascript-6">
export default {
name: 'child'
}
</script> <style scoped>
</style>
<template>
<child>
<div slot="xxx">xxx 对应的标签结构</div>
<div slot="yyy"><h1>yyyy 对应的标签结构</h1></div>
</child>
</template> <script>
import Child from './child'
export default {
name: 'prent',
components: {
Child
}
}
</script> 

通过slot的属性name来标识父组件的元素插入到哪个slot,当父组件中没有对应的slot元素,那么就是显示子组件中的slot中的内容

当父组件中没有插入元素的时候,就会显示子组件中的slot里面的内容, 如下父组件中solt为yyy的没有写上,那么就显示子组件中的内容

<template>
<child>
<div style="background-color: red" @click="sss" slot="xxx">xxx 对应的标签结构</div>
</child>
</template> <script>
import Child from './child'
export default {
name: 'prent',
components: {
Child
},
methods: {
sss () {
alert('sss')
}
}
}
</script> <style scoped> </style>

可以看到父组件对应的solt中的事件和样式都是有效的

组件之间的通信:消息订阅与发布(PubSubJS 库)

PubSub是一种设计模式,中文叫发布订阅模式,简单来说就是消息发布者不直接向订阅者发布消息,而是发布到中介,而中介根据不同主题对消息进行过滤,并通知对该主题感兴趣的订阅者。该模式在前端现在很火的组件化开发十分常用,因为该模式松耦合,易于扩展的优点正式组件化开发所需要的。

此方式可实现任意关系组件间通信(数据),在发送数据的组件中定义发布消息方法,在接收数据的组件中定义订阅方法

兄弟组件之间的通信

<template>
<div id="header">
<div class="container">
<div class="row center">
<h1>Search Github Users</h1>
<input type="text" v-model="searchName">
<input type="button" value="Search" class="btn btn-primary" placeholder="请输入github用户名" @click="search">
</div>
</div>
</div>
</template>
<script>
import PubSub from 'pubsub-js'
export default {
name: 'Header',
data () {
return {
searchName: ''
}
},
methods: {
search () {
// 发布消息
const searchName = this.searchName.trim()
if (searchName) {
PubSub.publish('search', searchName)
}
}
}
}
</script>
<style>
#header{
height: 200px;
}
.container .center{
text-align: center;
}
</style>
<template>
<div id="main">
{{searchName}}
</div>
</template>
<script>
import PubSub from 'pubsub-js'
export default {
name: 'Main',
data () {
return {
searchName: ''
}
},
mounted () {
// 订阅消息(一般定义在mounted周期函数中,当订阅了该消息的组件发布了消息,这边就能接收到)
PubSub.subscribe('search', (msg, searchName) => { // 这里使用箭头函数,那么函数体内的this就是外部的this(vue实例对象),如果不是箭头函数那么this就不是vue实例了
this.searchName = searchName
})
}
}
</script>
<template>
<div id="app">
<search-header></search-header>
<serach></serach>
</div>
</template> <script>
import Header from './view/header'
import Serach from './view/serach'
export default {
name: 'app',
components: {
'search-header': Header, // 需要注意的是当自定义的组件名跟原生的h5标签有冲突时,需要自定义一个别的标签名
Serach
},
data () {
return {}
}
}
</script>

父子组件之间的通信

<template>
<div id="app">
<search-header></search-header>
<p>{{searchName}}</p>
</div>
</template> <script>
import Header from './view/header'
import PubSub from 'pubsub-js' export default {
name: 'app',
components: {
'search-header': Header // 需要注意的是当自定义的组件名跟原生的h5标签有冲突时,需要自定义一个别的标签名
},
data () {
return {
searchName: ''
}
},
mounted () {
// 订阅消息(一般定义在mounted周期函数中,当订阅了该消息的组件发布了消息,这边就能接收到)
PubSub.subscribe('search', (msg, searchName) => { // 这里使用箭头函数,那么函数体内的this就是外部的this(vue实例对象),如果不是箭头函数那么this就不是vue实例了
this.searchName = searchName
})
}
}
</script>
<template>
<div id="header">
<div class="container">
<div class="row center">
<h1>Search Github Users</h1>
<input type="text" v-model="searchName">
<input type="button" value="Search" class="btn btn-primary" placeholder="请输入github用户名" @click="search">
</div>
</div>
</div>
</template>
<script>
import PubSub from 'pubsub-js'
export default {
name: 'Header',
data () {
return {
searchName: ''
}
},
methods: {
search () {
// 发布消息
const searchName = this.searchName.trim()
if (searchName) {
PubSub.publish('search', searchName)
}
}
}
}
</script>
<style>
#header{
height: 200px;
}
.container .center{
text-align: center;
}
</style>

其他关系的组件之间通信也是一样的写法,发布消息的地方发送消息,接收消息的地方订阅消息(写在mounted周期函数内)

vue的组件详解的更多相关文章

  1. vue函数式组件详解

    本篇将详细介绍vue组件化之函数式组件,会用到以下api: Vue.component().Vue.extend().$createElement.patch(). 从事vue开发的小伙伴,平时组件化 ...

  2. Vue(七) 组件详解

    组件 (Component) 是 Vue.js 最核心的功能,也是整个框架设计最精彩的部分,当然也是最难掌握的. 组件与复用 组件用法 组件与创建 Vue 实例类似,需要注册后才可以使用.注册有全局注 ...

  3. vue.js基础知识篇(6):组件详解

    第11章:组件详解 组件是Vue.js最推崇也最强大的功能之一,核心目标是可重用性. 我们把组件代码按照template.style.script的拆分方式,放置到对应的.vue文件中. 1.注册 V ...

  4. vue 源码详解(二): 组件生命周期初始化、事件系统初始化

    vue 源码详解(二): 组件生命周期初始化.事件系统初始化 上一篇文章 生成 Vue 实例前的准备工作 讲解了实例化前的准备工作, 接下来我们继续看, 我们调用 new Vue() 的时候, 其内部 ...

  5. vue 文件目录结构详解

    vue 文件目录结构详解 本篇文章主要介绍了vue 文件目录结构详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考.一起跟随小编过来看看吧 项目简介 基于 vue.js 的前端开发环境,用于前后 ...

  6. Angular6 学习笔记——组件详解之组件通讯

    angular6.x系列的学习笔记记录,仍在不断完善中,学习地址: https://www.angular.cn/guide/template-syntax http://www.ngfans.net ...

  7. Vue props用法详解

    Vue props用法详解 组件接受的选项之一 props 是 Vue 中非常重要的一个选项.父子组件的关系可以总结为: props down, events up 父组件通过 props 向下传递数 ...

  8. main.js index.html与app.vue三者关系详解

    main.js index.html与app.vue三者关系详解 2019年01月23日 11:12:15 Pecodo 阅读数 186   main.js与index.html是nodejs的项目启 ...

  9. vue 源码详解(一):原型对象和全局 `API`的设计

    vue 源码详解(一):原型对象和全局 API的设计 1. 从 new Vue() 开始 我们在实际的项目中使用 Vue 的时候 , 一般都是在 main.js 中通过 new Vue({el : ' ...

随机推荐

  1. [深度学习]CNN--卷积神经网络中用1*1 卷积有什么作用

    1*1卷积过滤器 和正常的过滤器一样,唯一不同的是它的大小是1*1,没有考虑在前一层局部信息之间的关系.最早出现在 Network In Network的论文中 ,使用1*1卷积是想加深加宽网络结构 ...

  2. SpringMVC入门学习(二)

    SpringMVC入门学习(二) ssm框架 springMVC  在上一篇博客中,我简单介绍了一下SpringMVC的环境配置,和简单的使用,今天我们将进一步的学习下Springmvc的操作. mo ...

  3. Django学习(6)配置静态文件

      本文将详细讲述如何在Django中配置静态文件,如图片(images),JavaScript,CSS等.   我们将要实现的网页如下: 当按下按钮"Change Text"时, ...

  4. Ado.net怎么执行存储过程?

    与ADO.Net执行SQL语句的地方只有两点不同1.使用存储过程名代替sql语句2. 使用查询对象SqlCommand,需配置一个CommandType属性 存储过程的执行语法-> exec 存 ...

  5. 编写计算器程序学习JS责任链模式

    设计模式中的责任链模式能够很好的处理程序过程的逻辑判断,提高程序可读性. 责任链模式的核心在于责任链上的元素判断能够处理该数据,不能处理的话直接交给它的后继者. 计算器的基本样式: 通过div+css ...

  6. springboot整合freemarker----一点小小的错误

    最近小弟正在学习springboot,没办法,现在微服务太火了.小弟也要顺应时代的潮流啊 :( 好了,废话不多说了!!!! 首先在springboot的pom.xml添加freemarker的依赖 & ...

  7. Web应用架构入门之11个基本要素

    译者: 读完这篇博客,你就可以回答一个经典的面试题:当你访问Google时,到底发生了什么? 原文:Web Architecture 101 译者:Fundebug 为了保证可读性,本文采用意译而非直 ...

  8. windows下navicate for mysql 零填充不显示

    在mysql数据库中我们在需要某个字段时经常要用到零填充 zerofill,之前碰到了一个大坑,在数据表sql语句中明明规定了 unsigned zerofill:但是一直没有显示出来,以为自己写的s ...

  9. HappenBefore

    计算机芯片在操作指令的步骤: 1.获取指令 2.指令进行解码 3.去寄存器里取值 4.开始计算结果(操作) 5.将结果写会到寄存器中 执行代码的顺序可能与编写代码不一致,及虚拟机优化代码顺序,则为指令 ...

  10. 【CSS学习】--- 文本样式

    一.前言 CSS文本属性可以定义文本的外观.通过文本属性,可以定义文本的颜色.字符间距,对齐文本,装饰文本,对文本进行缩进,等等. CSS常用的文本属性目录: text-align 文本对齐属性 te ...