1. 认识组件化

1.1 什么是组件化

人面对复杂问题的处理方式

任何一个人处理信息的逻辑能力都是有限的,所以当面对一个非常复杂的问题时我们不太可能一次性搞定一大堆的内容。

但是我们人有一种天生的能力就是将问题进行拆解。如果将一个复杂的问题拆分成很多个可以处理的小问题再将其放在整体当中,你会发现大的问题也会迎刃而解。

组件化也是类似的思想

如果我们将一个页面中所有的处理逻辑全部放在一起,处理起来就会变得非常复杂,而且不利于后续的管理以及扩展。

但如果我们将一个页面拆分成一个个小的功能块,每个功能块完成属于自己这部分独立的功能,那么之后整个页面的管理和维护就变得非常容易了。

1.2 Vue组件化思想

组件化是Vue.js中的重要思想

它提供了一种抽象,让我们可以开发出一个个独立可复用的小组件来构造我们的应用,任何的应用都会被抽象成一颗组件树

组件化思想的应用

有了组件化的思想,我们在之后的开发中就要充分的利用它

尽可能的将页面拆分成一个个小的、可复用的组件

这样让我们的代码更加方便组织和管理,并且扩展性也更强

2. 注册组件

2.1 注册组件的基本步骤

组件的使用分成三个步骤

  • 创建组件构造器

  • 注册组件

  • 使用组件

三个步骤的含义

  • Vue.extend()

    • 调用 Vue.extend() 创建的是一个组件构造器
    • 通常在创建组件构造器时,传入template代表我们自定义组件的模板
    • 该模板就是在使用到组件的地方要显示的HTML代码
    • 事实上,这种写法在 Vue2.x 的文档中几乎已经看不到了,它会直接使用下面如 2.4 形式的语法糖,但是在很多资料还是会提到这种方式,而且这种方式是学习后面方式的基础
  • Vue.component()

    • 调用 Vue.component() 是将刚才的组件构造器注册为一个组件并且给它起一个组件的标签名称
    • 所以需要传递两个参数:① 注册组件的标签名 ② 组件构造器
  • 在Vue实例的作用范围内使用组件

    • 组件必须挂载在某个Vue实例下,否则它不会生效
    • 下面我使用了三次<my-cpn></my-cpn> ,而第三次其实并没有生效

2.2 组件的作用域

全局组件

当我们通过调用 Vue.component() 注册组件时,组件的注册是全局的这意味着该组件可以在任意Vue示例下使用

局部组件

如果我们注册的组件是挂载在某个实例中, 那么就是一个局部组件

2.3 父子组件

在前面我们看到了组件树

组件和组件之间存在层级关系

而其中一种非常重要的关系就是父子组件的关系

我们来看通过代码如何组成的这种层级关系

父子组件错误用法:以子标签的形式在Vue实例中使用

因为当子组件注册到父组件的components时,Vue会编译好父组件的模块

该模板的内容已经决定了父组件将要渲染的HTML(相当于父组件中已经有了子组件中的内容了

<child-cpn></child-cpn> 是只能在父组件中被识别的

类似这种用法,<child-cpn></child-cpn>是会被浏览器忽略的

2.4 注册组件语法糖

在上面注册组件的方式,可能会有些繁琐

Vue为了简化这个过程,提供了注册的语法糖

主要是省去了调用 Vue.extend() 的步骤,而是可以直接使用一个对象来代替

语法糖注册全局组件和局部组件如下

3. 组件其他补充

3.1 模板的分离写法

刚才我们通过语法糖简化了Vue组件的注册过程,另外还有一个地方的写法比较麻烦,就是template模块写法

如果我们能将其中的HTML分离出来写,然后挂载到对应的组件上,必然结构会变得非常清晰

Vue提供了两种方案来定义HTML模块内容:

  • 使用 <script> 标签
  • 使用 <template> 标签

3.2 组件可以Vue的实例数据吗?

组件是一个单独功能模块的封装

这个模块有属于自己的HTML模板,也应该有属性自己的数据data

组件中的数据是保存在哪里呢?顶层的Vue实例中吗?

  • 如下测试发现不能并不能访问,而且即使可以访问,如果将所有的数据都放在Vue实例中Vue实例就会变的非常臃肿
  • 结论:组件并不能直接访问Vue实例中的data,Vue组件应该有自己保存数据的地方

3.3 组件数据的存放

组件自己的数据存放在哪里呢?

组件对象也有一个data属性(当然也可以有methods等属性,下面我们有用到)

只是这个data属性必须是一个函数

而且这个函数返回一个对象,对象内部保存着数据

为什么data在组件中必须是一个函数呢?

首先,如果不是一个函数Vue直接就会报错

其次,原因是在于Vue让每个组件对象都返回一个新的对象。因为如果是同一个对象的,组件在多次使用后会相互影响

4. 父子组件通信

4.1 父子组件通信理解

之前我们提到了子组件是不能引用父组件或者Vue实例的数据的

但是,在开发中往往一些数据确实需要从上层传递到下层:

  • 比如在一个页面中我们从服务器请求到了很多的数据

  • 其中一部分数据并非是我们整个页面的大组件来展示的,而是需要下面的子组件进行展示

  • 这个时候并不会让子组件再次发送一个网络请求,而是直接让 大组件(父组件) 将数据传递给 小组件(子组件)

  • 如何进行父子组件间的通信呢?Vue官方提到

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

在下面的代码中,我直接将Vue实例当做父组件并且其中包含子组件来简化代码

真实的开发中,Vue实例和子组件的通信和父组件和子组件的通信过程是一样的

4.2 父组件向子组件传递数据 - props

基本用法

在组件中,使用选项props来声明需要从父级接收到的数据

props的值有两种方式

  • 字符串数组,数组中的字符串就是传递时的名称
  • 对象,对象可以设置传递时的类型,也可以设置默认值等

我们先来看一个最简单的props传递

props数据验证

在前面我们的props选项是使用一个数组

除了数组之外我们也可以使用对象,当需要对props进行类型等验证时就需要对象写法了

验证支持如下数据类型

  • String
  • Number
  • Boolean
  • Array
  • Object
  • Date
  • Function
  • Symbol

当我们有自定义构造函数时,验证也支持自定义的类型

4.4 子组件向父组件传递数据或事件 - $emit()

自定义事件

props用于父组件向子组件传递数据,还有一种比较常见的是子组件传递数据或事件到父组件中我们应该如何处理呢?这个时候我们需要使用自定义事件来完成

什么时候需要自定义事件?

当子组件需要向父组件传递数据时,就要用到自定义事件了

我们之前学习的v-on不仅仅可以用于监听DOM事件,也可以用于组件间的自定义事件

自定义事件的流程

在子组件中,通过$emit()来触发事件

在父组件中,通过v-on来监听子组件事件

我们来看一个简单的例子:

  • 我们之前做过一个两个按钮 +1-1 ,点击后修改 counter
  • 我们整个操作的过程还是在子组件中完成,但是之后的展示交给父组件
  • 这样我们就需要将子组件中的 counter,传给父组件的某个属性比如 total

4.5 父子组件的直接访问方式 - $children或$refs  /  $parent

理解

有时候我们需要父组件直接访问子组件,子组件直接访问父组件,或者是子组件访问根组件

  • 父组件访问子组件:使用$children$refs

  • 子组件访问父组件:使用$parent

$children

this.$children是一个数组类型,它包含所有子组件对象

我们这里通过一个遍历,取出所有子组件的message状态

$refs

$children的缺陷

通过 $children 访问子组件时,是一个数组类型,访问其中的子组件必须通过索引值

但是当子组件过多,我们需要拿到其中一个时往往不能确定它的索引值,甚至还可能会发生变化

有时候我们想明确获取其中一个特定的组件,这个时候就可以使用$refs

$refs的使用

$refsref指令 通常是一起使用的

首先我们通过 ref 给某一个子组件绑定一个特定的ID

其次通过 this.$refs.ID 就可以访问到该组件了

$parent

如果我们想在子组件中直接访问父组件,可以通过$parent

注意

尽管在Vue开发中,我们允许通过$parent来访问父组件,但是在真实开发中尽量不要这样做

子组件应该尽量避免直接访问父组件的数据,因为这样耦合度太高了

如果我们将子组件放在另外一个组件之内,很可能该父组件没有对应的属性,往往会引起问题

另外更不好做的是通过$parent直接修改父组件的状态,那么父组件中的状态将变得飘忽不定,很不利于我的调试和维护

5. 非父子组件通信

5.1 理解

刚才我们讨论的都是父子组件间的通信,那如果是非父子关系呢?

非父子组件关系包括多个层级的组件,也包括兄弟组件的关系

Vue1.x的时候,可以通过 $dispatch$broadcast完成,但是在 Vue2.x都被取消了

  • $dispatch用于向上级派发事件

  • $broadcast用于向下级广播事件

Vue2.x 中,有一种方案是通过中央事件总线,也就是一个中介来完成

  • 但是这种方案和直接使用 Vuex 的状态管理方案还是逊色很多
  • 并且 Vuex 提供了更多好用的功能,所以这里我们暂且不讨论这种方案,后续我们专门学习 Vuex 的状态管理
5.2 中央事件总线
5.3 Vuex状态管理(后面专门讲)

6. 插槽slot

6.1 编译作用域

在真正学习插槽之前我们需要先理解一个概念:编译作用域

官方对于编译的作用域解析比较简单,我们自己来通过一个例子来理解这个概念

我们来考虑下面的代码是否最终是可以渲染出来的:

  • <my-cpn v-show="isShow"></my-cpn>中,我们使用了 isShow属性

  • isShow属性包含在组件中,也包含在Vue实例中

答案:最终可以渲染出来,也就是使用的是Vue实例的属性。为什么呢?

  • 官方给出了一条准则:父组件模板的所有东西都会在父级作用域内编译;子组件模板的所有东西都会在子级作用域内编译
  • 而我们在使用 <my-cpn v-show="isShow"></my-cpn> 的时候,整个组件的使用过程是相当于在父组件中出现的
  • 那么他的作用域就是父组件,使用的属性也是属于父组件的属性
  • 因此 isShow使用的是Vue实例中的属性,而不是子组件的属性

6.2 为什么使用slot

slot翻译为插槽

在生活中很多地方都有插槽,电脑的USB插槽,插板当中的电源插槽

插槽的目的是让我们原来的设备具备更多的扩展性

比如电脑的USB我们可以插入U盘、硬盘、手机、音响、键盘、鼠标等

组件的插槽

组件的插槽也是为了让我们封装的组件更加具有扩展性

让使用者可以决定组件内部的一些内容到底展示什么

例子:移动网站中的导航栏

  • 移动开发中,几乎每个页面都有导航栏

  • 导航栏我们必然会封装成一个插件,比如nav-bar组件

  • 一旦有了这个组件,我们就可以在多个页面中复用了

  • 但是,每个页面的导航是一样的吗?No,我以京东M站为例

6.3 如何在封装组件时正确使用slot

如何去封装京东M站导航栏这类的组件呢?

它们也很多区别,但是也有很多共性

如果我们每一个单独去封装一个组件显然不合适:比如每个页面都返回,这部分内容我们就要重复去封装

但是如果我们封装成一个好像也不合理:有些左侧是菜单,有些是返回,有些中间是搜索,有些是文字等

如何封装合适呢?抽取共性,保留不同

  • 最好的封装方式就是将共性抽取到组件中,将不同暴露为插槽

  • 一旦我们预留了插槽,就可以让使用者根据自己的需求,决定插槽中插入什么内容

  • 是搜索框,还是文字,还是菜单。由调用者自己来决定

  • 这就是为什么我们要学习组件中的插槽slot的原因

6.4 slot基本使用

了解了为什么用slot,我们再来谈谈如何使用slot?

  • 在子组件中,使用特殊的元素 <slot> 就可以为子组件开启一个插槽。

  • 该插槽插入什么内容取决于父组件如何使用。

我们通过一个简单的例子,来给子组件定义一个插槽

  • <slot> 中的内容表示,如果没有在该组件中插入任何其他内容,就默认显示该内容
  • 有了这个插槽后,父组件如何使用呢?

6.5 具名插槽slot

当子组件的功能复杂时,子组件的插槽可能并非是一个

比如我们封装一个导航栏的子组件,可能就需要三个插槽,分别代表左边、中间、右边。、

那么,外面在给插槽插入内容时,如何区分插入的是哪一个呢?

这个时候,我们就需要给插槽起一个名字

如何使用具名插槽呢?

非常简单,只要给slot元素一个name属性即可

<slot name='myslot'></slot>

我们来给出一个案例:

这里我们先不对导航组件做非常复杂的封装,先了解具名插槽的用法。

6.6 作用域插槽

作用域插槽是slot一个比较难理解的点,而且官方文档说的又有点不清晰。

这里,我们用一句话对其做一个总结,然后我们在后续的案例中来体会:父组件替换插槽的标签,但是内容由子组件来提供。

我们先提一个需求

子组件中包括一组数据,比如:pLanguages: ['JavaScript', 'Python', 'Swift', 'Go', 'C++']

需要在多个界面进行展示:

  • 某些界面是以水平方向一一展示的,
  • 某些界面是以列表形式展示的,
  • 某些界面直接展示一个数组

内容在子组件,希望父组件告诉我们如何展示,怎么办呢

  • 利用slot作用域插槽就可以了

我们来看看子组件的定义:

在父组件使用我们的子组件时,从子组件中拿到数据

我们通过 <template slot-scope="slotProps"> 获取到 slotProps 属性

在通过 slotProps.data 就可以获取到刚才我们传入的data了

三. Vue组件化的更多相关文章

  1. 3.VUE前端框架学习记录三:Vue组件化编码1

    VUE前端框架学习记录三:Vue组件化编码1文字信息没办法描述清楚,主要看编码Demo里面,有附带完整的代码下载地址,有需要的同学到脑图里面自取.脑图地址http://naotu.baidu.com/ ...

  2. Vue组件化开发

    Vue的组件化 组件化是Vue的精髓,Vue就是由一个一个的组件构成的.Vue的组件化设计到的内容又非常多,当在面试时,被问到:谈一下你对Vue组件化的理解.这时候又有可能无从下手,因此在这里阐释一下 ...

  3. vue - Vue组件化编程

    今天是对vue组件化的一个理解,最主要的单文件组件,然后就可以脚手架的学习了,本来昨晚就该上传的,但是用的那个上传博客园的Python脚本不行了,换了一个新的. 组件化让我越来越感觉到框架的力量了 一 ...

  4. vue组件化的应用

    前言:vue组件化的应用涉及到vue-cli的内容,所以在应用之前是需要安装node和vue-cli的,具体如何安装我就不一一赘述了.可能一会儿我心情好的时候,可以去整理一下. 1.应用的内容:在一个 ...

  5. vue组件化之模板优化及注册组件语法糖

    vue组件化之模板优化及注册组件语法糖 vue组件化 模板 优化  在 https://www.cnblogs.com/singledogpro/p/12054895.html 这里我们对vue.js ...

  6. vue组件化初体验 全局组件和局部组件

    vue组件化初体验 全局组件和局部组件 vue组件化 全局组件 局部组件  关于vue入门案例请参阅 https://www.cnblogs.com/singledogpro/p/11938222.h ...

  7. 4.VUE前端框架学习记录四:Vue组件化编码2

    VUE前端框架学习记录四:Vue组件化编码2文字信息没办法描述清楚,主要看编码Demo里面,有附带完整的代码下载地址,有需要的同学到脑图里面自取.脑图地址http://naotu.baidu.com/ ...

  8. 二、vue组件化开发(轻松入门vue)

    轻松入门vue系列 Vue组件化开发 五.组件化开发 1. 组件注册 组件命名规范 组件注册注意事项 全局组件注册 局部组件注册 2. Vue调试工具下载 3. 组件间数据交互 父组件向子组件传值 p ...

  9. Vue基础(三)---- 组件化开发

    基本结构: ◆1.组件化开发思想 ◆2.组件注册 ◆3.Vue调试工具用法 ◆4.组件间数据交互 ◆5.组件插槽 ◆6.基于组件的案例   ◆1.组件化开发思想 优点: 提高开发效率 方便重复使用 简 ...

随机推荐

  1. K8S 使用 SideCar 模式部署 Filebeat 收集容器日志

    对于 K8S 内的容器日志收集,业内一般有两种常用的方式: 使用 DaemonSet 在每台 Node 上部署一个日志收集容器,用于收集当前 Node 上所有容器挂载到宿主机目录下的日志 使用 Sid ...

  2. Java技术平台介绍

    Java的名字的来源:Java是印度尼西亚爪哇岛的英文名称,因盛产咖啡而闻名.Java语言中的许多库类名称,多与咖啡有关, 如JavaBeans(咖啡豆).NetBeans(网络豆)以及ObjectB ...

  3. UI自动化测试不稳定的因素

    1.进行测试的时候,经常会有一些无法预测的弹框出现: 2.页面很多元素是会动态变化的: 3.进入页面时,经常会因为网络等一些原因,使得页面元素加载延迟: 4.数据变更.

  4. 浅谈 Tarjan 算法

    目录 简述 作用 Tarjan 算法 原理 出场人物 图示 代码实现 例题 例题一 例题二 例题三 例题四 例题五 总结 简述 对于初学 Tarjan 的你来说,肯定和我一开始学 Tarjan 一样无 ...

  5. Positions of Large Groups

    Positions of Large Groups In a string S of lowercase letters, these letters form consecutive groups ...

  6. leetcode17gas-station

    题目描述 环形路上有n个加油站,第i个加油站的汽油量是gas[i]. 你有一辆车,车的油箱可以无限装汽油.从加油站i走到下一个加油站(i+1)花费的油量是cost[i],你从一个加油站出发,刚开始的时 ...

  7. day86:luffy:前端发送请求生成订单&结算页面优惠劵的实现

    目录 1.前端发送请求生成订单 1.前端点击支付按钮生成订单 2.结算成功之后应该清除结算页面的数据 3.后端计算结算页面总原价格和总的真实价格并存到数据库订单表中 2.优惠劵 1.准备工作 2.前端 ...

  8. JavaScript中.、[]与setAttribute()在设置属性上的区别

    .和[] javaScript.和[]既可以对所有js对象设置属性,但是对于DOM对象它设置的属性有些特殊.对于元素DOM标准属性,实现属性值的设置/更改;对于元素DOM非标准属性,仅在js中有效,在 ...

  9. TypeError: Cannot read property 'Component' of undefined

    继续跟着阮一峰的教程走,下面写到PropTypes的getDefaultProps时,又出现了问题,基于上一个createClass的报错换成了Component写法 错误描述: 解决方法:引入rea ...

  10. 西数WD2T硬盘分区对齐的方法

    新购一个西数2T硬盘,也就是绿盘的那种,淘宝500左右,支持高级格式化. 到手以后,分区格式化,前几天格式化完成以后,fdisk -l 发现如下文字 引用 Partition 1 does not s ...