Vue.js每天必学之组件与组件间的通信

前端之家收集整理的这篇文章主要介绍了Vue.js每天必学之组件与组件间的通信前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

什么是组件?

组件(Component)是 Vue.js 最强大的功能之一。组件可以扩展 HTML 元素,封装可重用的代码。在较高层面上,组件是自定义元素,Vue.js 的编译器为它添加特殊功能。在有些情况下,组件也可以是原生 HTML 元素的形式,以 is 特性扩展。

使用组件

注册

之前说过,我们可以用 Vue.extend() 创建一个组件构造器:

要把这个构造器用作组件,需要用 `Vue.component(tag,constructor)` **注册** :

注册组件,tag 为 my-component Vue.component('my-component',MyComponent)

自定义标签名字,Vue.js 不强制要求遵循 W3C 规则(小写,并且包含一个短杠),尽管遵循这个规则比较好。

组件在注册之后,便可以在父实例的模块中以自定义元素 的形式使用。要确保在初始化根实例之前注册了组件:

// 定义
var MyComponent = Vue.extend({
template: '

A custom component!
'
})

// 注册
Vue.component('my-component',MyComponent)

// 创建根实例
new Vue({
el: '#example'
})

渲染为:

A custom component!

注意组件的模板替换了自定义元素,自定义元素的作用只是作为一个挂载点。可以用实例选项 replace 决定是否替换。

局部注册

不需要全局注册每个组件。可以让组件只能用在其它组件内,用实例选项 components 注册

var Parent = Vue.extend({
template: '...',components: {
// 只能用在父组件模板内
'my-component': Child
}
})

这种封装也适用于其它资源,如指令、过滤器和过渡。

注册语法糖

为了让事件更简单,可以直接传入选项对象而不是构造器给 Vue.component() 和 component 选项。Vue.js 在背后自动调用 Vue.extend():

注册 Vue.component('my-component',{ template: '
A custom component!
' })

// 局部注册也可以这么做
var Parent = Vue.extend({
components: {
'my-component': {
template: '

A custom component!
'
}
}
})

组件选项问题

传入 Vue 构造器的多数选项也可以用在 Vue.extend() 中,不过有两个特例: data 和 el。试想如果我们简单地把一个对象作为 data 选项传给 Vue.extend():

这么做的问题是 `MyComponent` 所有的实例将共享同一个 `data` 对象!这基本不是我们想要的,因此我们应当使用一个函数作为 `data` 选项,让这个函数返回一个新对象:

同理,`el` 选项用在 `Vue.extend()` 中时也须是一个函数

模板解析

Vue 的模板是 DOM 模板,使用浏览器原生的解析器而不是自己实现一个。相比字符串模板,DOM 模板有一些好处,但是也有问题,它必须是有效的 HTML 片段。一些 HTML 元素对什么元素可以放在它里面有限制。常见的限制: •a 不能包含其它的交互元素(如按钮,链接) •ul 和 ol 只能直接包含 li •select 只能包含 option 和 optgroup •table 只能直接包含 thead,tbody,tfoot,tr,caption,col,colgroup •tr 只能直接包含 th 和 td

在实际中,这些限制会导致意外的结果。尽管在简单的情况下它可能可以工作,但是你不能依赖自定义组件在浏览器验证之前的展开结果。例如 不是有效的模板,即使 my-select 组件最终展开为

另一个结果是,自定义标签包括自定义元素和特殊标签,如

<div id="events-example">

Messages: {{ messages | json }}

// 注册子组件
// 将当前消息派发出去
Vue.component('child',{
template: '#child-template',data: function () {
return { msg: 'hello' }
},methods: {
notify: function () {
if (this.msg.trim()) {
this.$dispatch('child-msg',this.msg)
this.msg = ''
}
}
}
})

// 初始化父组件
// 将收到消息时将事件推入一个数组
var parent = new Vue({
el: '#events-example',data: {
messages: []
},// 在创建实例时 events 选项简单地调用 $on
events: {
'child-msg': function (msg) {
// 事件回调内的 this 自动绑定到注册它的实例上
this.messages.push(msg)
}
}
})

使用 v-on 绑定自定义事件

上例非常好,不过从父组件的代码中不能直观的看到 "child-msg" 事件来自哪里。如果我们在模板中子组件用到的地方声明事件处理器会更好。为此子组件可以用 v-on 监听自定义事件:

这样就很清楚了:当子组件触发了 `”child-msg”` 事件,父组件的 `handleIt` 方法将被调用。所有影响父组件状态的代码放到父组件的 `handleIt` 方法中;子组件只关注触发事件。 子组件索引

尽管有 props 和 events,但是有时仍然需要在 JavaScript 中直接访问子组件。为此可以使用 v-ref 为子组件指定一个索引 ID。例如:

var parent = new Vue({ el: '#parent' })
// 访问子组件
var child = parent.$refs.profile

v-ref 和 v-for 一起用时,ref 是一个数组或对象,包含相应的子组件。

使用 Slot 分发内容

在使用组件时,常常要像这样组合它们:

注意两点: 1. 组件不知道它的挂载点会有什么内容,挂载点的内容是由 的父组件决定的。

2. 组件很可能有它自己的模板。

为了让组件可以组合,我们需要一种方式来混合父组件的内容与子组件自己的模板。这个处理称为内容分发(或 “transclusion”,如果你熟悉 Angular)。Vue.js 实现了一个内容分发 API,参照了当前 Web 组件规范草稿,使用特殊的 元素作为原始内容的插槽。

编译作用域

在深入内容分发 API 之前,我们先明确内容的编译作用域。假定模板为:

{{ msg }}

msg 应该绑定到父组件的数据,还是绑定到子组件的数据?答案是父组件。组件作用域简单地说是:

父组件模板的内容在父组件作用域内编译;子组件模板的内容在子组件作用域内编译 一个常见错误是试图在父组件模板内将一个指令绑定到子组件的属性/方法:

假定 someChildProperty 是子组件的属性,上例不会如预期那样工作。父组件模板不应该知道子组件的状态。

如果要绑定子组件内的指令到一个组件的根节点,应当在它的模板内这么做:

',data: function () { return { someChildProperty: true } } })

类似地,分发内容是在父组件作用域内编译。

单个 Slot

父组件的内容将被抛弃,除非子组件模板包含 。如果子组件模板只有一个没有特性的 slot,父组件的整个内容将插到 slot 所在的地方并替换它。

标签的内容视为回退内容。回退内容在子组件的作用域内编译,当宿主元素为空并且没有内容供插入时显示这个回退内容。

假定 my-component 组件有下面模板:

This is my component!

如果没有分发内容则显示我。

父组件模板:

This is some original content

This is some more original content

渲染结果:

This is my component!

This is some original content

This is some more original content

具名 Slot

元素可以用一个特殊特性 name 配置如何分发内容。多个 slot 可以有不同的名字。具名 slot 将匹配内容片段中有对应 slot 特性的元素。

仍然可以有一个匿名 slot,它是默认 slot,作为找不到匹配的内容片段的回退插槽。如果没有默认的 slot,这些找不到匹配的内容片段将被抛弃。

例如,假定我们有一个 multi-insertion 组件,它的模板为:

父组件模板:

Default A

渲染结果为:

Default A

在组合组件时,内容分发 API 是非常有用的机制。

动态组件

多个组件可以使用同一个挂载点,然后动态地在它们之间切换。使用保留的 元素,动态地绑定到它的 is 特性:

<component :is="currentView">

keep-alive

如果把切换出去的组件保留在内存中,可以保留它的状态或避免重新渲染。为此可以添加一个 keep-alive 指令参数:

activate 钩子

在切换组件时,切入组件在切入前可能需要进行一些异步操作。为了控制组件切换时长,给切入组件添加 activate 钩子:

注意 `activate` 钩子只作用于动态组件切换或静态组件初始化渲染的过程中,不作用于使用实例方法手工插入的过程中。 transition-mode

transition-mode 特性用于指定两个动态组件之间如何过渡。

在默认情况下,进入与离开平滑地过渡。这个特性可以指定另外两种模式: •in-out:新组件先过渡进入,等它的过渡完成之后当前组件过渡出去。

•out-in:当前组件先过渡出去,等它的过渡完成之后新组件过渡进入。

示例:

.fade-transition {
transition: opacity .3s ease;
}
.fade-enter,.fade-leave {
opacity: 0;
}

杂项

组件和 v-for

自定义组件可以像普通元素一样直接使用 v-for:

但是,不能传递数据给组件,因为组件的作用域是孤立的。为了传递数据给组件,应当使用 props:

不自动把 item 注入组件的原因是这会导致组件跟当前 v-for 紧密耦合。显式声明数据来自哪里可以让组件复用在其它地方。

编写可复用组件

在编写组件时,记住是否要复用组件有好处。一次性组件跟其它组件紧密耦合没关系,但是可复用组件应当定义一个清晰的公开接口。

Vue.js 组件 API 来自三部分——prop,事件和 slot: •prop 允许外部环境传递数据给组件;

•事件 允许组件触发外部环境的 action;

•slot 允许外部环境插入内容到组件的视图结构内。

使用 v-bind 和 v-on 的简写语法,模板的缩进清楚且简洁:

异步组件

在大型应用中,我们可能需要将应用拆分为小块,只在需要时才从服务器下载。为了让事情更简单,Vue.js 允许将组件定义为一个工厂函数,动态地解析组件的定义。Vue.js 只在组件需要渲染时触发工厂函数,并且把结果缓存起来,用于后面的再次渲染。例如:

I am async!
' }) },1000) })

工厂函数接收一个 resolve 回调,在收到从服务器下载的组件定义时调用。也可以调用 reject(reason) 指示加载失败。这里 setTimeout 只是为了演示。怎么获取组件完全由你决定。推荐配合使用 Webpack 的代码分割功能:

资源命名约定

一些资源,如组件和指令,是以 HTML 特性或 HTML 自定义元素的形式出现在模板中。因为 HTML 特性的名字和标签的名字不区分大小写,所以资源的名字通常需使用 kebab-case 而不是 camelCase 的形式,这不大方便。

Vue.js 支持资源的名字使用 camelCase 或 PascalCase 的形式,并且在模板中自动将它们转为 kebab-case(类似于 prop 的命名约定):

ES6 对象字面量缩写 也没问题:

// PascalCase
import TextBox from './components/text-box';
import DropdownMenu from './components/dropdown-menu';

export default {
components: {
// 在模板中写作
TextBox,DropdownMenu
}
}

递归组件

组件在它的模板内可以递归地调用自己,不过,只有当它有 name 选项时才可以:

' + // 递归地调用它自己 '' + '
' })

上面组件会导致一个错误 “max stack size exceeded”,所以要确保递归调用有终止条件。当使用 Vue.component() 全局注册一个组件时,组件 ID 自动设置为组件的 name 选项。

片断实例

在使用 template 选项时,模板的内容将替换实例的挂载元素。因而推荐模板的顶级元素始终是单个元素。

不这么写模板:

root node 1
root node 2

推荐这么写:

I have a single root node!
node 1
node 2

下面几种情况会让实例变成一个片断实例: 1.模板包含多个顶级元素。 2.模板只包含普通文本。 3.模板只包含其它组件(其它组件可能是一个片段实例)。 4.模板只包含一个元素指令,如 或 vue-router 的 。 5.模板根节点有一个流程控制指令,如 v-if 或 v-for。

这些情况让实例有未知数量的顶级元素,它将把它的 DOM 内容当作片断。片断实例仍然会正确地渲染内容。不过,它没有一个根节点,它的 $el 指向一个锚节点,即一个空的文本节点(在开发模式下是一个注释节点)。

但是更重要的是,组件元素上的非流程控制指令,非 prop 特性和过渡将被忽略,因为没有根元素供绑定:

当然片断实例有它的用处,不过通常给组件一个根节点比较好。它会保证组件元素上的指令和特性能正确地转换,同时性能也稍微好些。

内联模板

如果子组件有 inline-template 特性,组件将把它的内容当作它的模板,而不是把它当作分发内容。这让模板更灵活。

These are compiled as the component's own template

Not parent's transclusion content.

但是 inline-template 让模板的作用域难以理解,并且不能缓存模板编译结果。最佳实践是使用 template 选项在组件内定义模板。

本文已被整理到了《》,欢迎大家学习阅读。

关于vue.js组件的教程,请大家点击专题进行学习。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持编程之家。

vue.js组件组件组件通信通信

猜你在找的Vue相关文章