进修笔记:
组件详解
组件详解
组件与复用
Vue组件须要注册后才能够运用。注册有全局注册和部份注册两种体式格局。
全局注册
Vue.component('my-component', {});
要在父实例中运用这个组件,必需要在实例建立前注册,以后就能够用<my-component></my-component>
的情势来运用组件。
Vue.component('my-component', {
template: `<div>这是一个组件</div>`
});
template
的DOM组织必需被一个元素包含,缺乏<div></div>
会没法衬着并报错。
在Vue实例中,运用components
选项能够部份注册组件,注册后的组件只在该实例作用域下有效。
组件中也能够运用components
选项来注册组件,使组件能够嵌套。
var Child = {
template: `<div>部份注册组件的内容</div>`
};
new Vue({
el: '#app',
components: {
'my-component': Child
},
});
Vue组件的模板在某些状况下会遭到HTML的限定,比方<table>
内划定只许可是<tr>
、<td>
、<th>
等这些表格元素,所以在<table>
内直接运用组件时无效的。这类状况下,能够运用特别的is
属性来挂载组件。
<div id="app">
<table>
<tbody is="my-component"></tbody>
</table>
</div>
Vue.component('my-component', {
template: `<div>这里是组件内容</div>`
});
罕见的限定元素另有<ul>
、<ol>
、<select>
。
除了template
选项外,组件中还能够像Vue实例那样运用其他的选项,比方data
、computed
、methods
等。
然则在运用data
时,data
必需是函数,然后将数据return
出去。
JavaScript对象是援用关联,假如return
的对象援用了外部的一个对象,那这个对象就是同享的,任何一方修正都邑同步。
运用props
通报数据
组件不仅要把模板的内容举行复用,更重要的是组件间举行通讯。
平常父组件的模板中包含子组件,父组件要正向地向子组件通报数据或参数,子组件吸收后依据参数的差别衬着差别的内容或许实行操纵。这个正向通报数据的历程经由过程props
来完成。
在组件中,运用选项props
声明须要从父级吸收的数据,props
的值能够是两种,一种是字符串数组,一种是对象。
<my-component message="来自父组件的数据"></my-component>
props: ['message'],
template: `<div>{{message}}</div>`,
props
中声明的数据与组件data
函数中return
的数据重要辨别就是props
的数据来自父级,而data
中的是组件本身的数据,作用域是组件本身,这两种数据都能够在模板template
及盘算属性computed
和要领methods
中运用。
由于HTML特征不辨别大小写,当运用DOM模板时,驼峰定名的props
称号要转为短横线支解定名。
<my-component warning-text="提醒信息"></my-component>
有时候,通报的数据并非直接写死,而是来自父级的动态数据,这时候能够运用指令v-bind
动态绑定props
的值,当父组件的数据变化时,也会通报子组件。
<div id="app">
<input type="text" v-model="parentMessage">
<my-component :message="parentMessage"></my-component>
</div>
props: ['message'],
template: `<div>{{message}}</div>`,
data: {
parentMessage: ''
}
这里用v-model
绑定了父级的数据parentMessage
,当经由过程输入框恣意输入时,子组件吸收到的props["message"]
也会及时相应,并更新组件模板。
单向数据流
营业中会常常碰到两种须要改变prop
的状况,一种是父组件通报初始值进来,子组件将它作为初始值保存起来,在本身的作用域下能够随便运用和修正。这类状况能够在组件data
内再声明一个数据,援用父组件的prop
。
<div id="app">
<my-component :init-count="1"></my-component>
</div>
Vue.component('my-component', {
props: ['initCount'],
template: `<div>{{count}}</div>`,
data() {
return {
count:this.initCount
}
}
});
组件中声清楚明了数据count
,它在组件初始化时会猎取来自父组件的initCount
,以后就与之无关了,只用保护count
,如许就能够防止直接操纵initCount
。
另一种状况就是prop
作为须要被改变的原始值传入,这类状况用盘算属性就能够。
<div id="app">
<my-component :width="100"></my-component>
</div>
Vue.component('my-component', {
props: ['width'],
template: `<div :style="style">组件内容</div>`,
computed: {
style: function () {
return {
width: this.width + 'px'
}
}
}
});
由于用CSS通报宽度要带单元(px),数值盘算平常不带单元,所以统一在组件内运用盘算属性。
在JavaScript中对象和数组时援用范例,指向同一个内存空间,所以
props
是对象和数组时,在子组件内改变是会影响父组件。
数组考证
当prop
须要考证时,须要对象写法。
平常当组件须要供应给他人运用时,引荐都举行数据考证。比方某个数据必需是数字范例,假如传入字符串,就会在控制台弹出正告。
<p data-height=”565″ data-theme-id=”0″ data-slug-hash=”WywyjV” data-default-tab=”js” data-user=”whjin” data-embed-version=”2″ data-pen-title=”prop” class=”codepen”>See the Pen prop by whjin (@whjin) on CodePen.</p>
<script async src=”https://static.codepen.io/ass…;></script>
考证的type
范例能够是:
String
Number
Boolean
Object
Array
Function
type
也能够是一个自定义组织器,运用instanceof
检测。
组件通讯
组件关联可分为父组件通讯、兄弟组件通讯、跨级组件通讯。
自定义事宜
当子组件须要向父组件通报数据时,就要用到自定义事宜。
v-on
除了监听DOM事宜外,还能够用于组件之间的自定义事宜。
JavaScript的设想形式——观察者形式要领:
dispatchEvent
addEventListener
Vue组件的子组件用$emit()
来触发事宜,父组件用$on()
来监听子组件的事宜。
父组件也能够直接在子组件的自定义标签上运用v-on
来监听子组件触发的自定义事宜。
<p data-height=”365″ data-theme-id=”0″ data-slug-hash=”ZRWjKv” data-default-tab=”js,result” data-user=”whjin” data-embed-version=”2″ data-pen-title=”自定义事宜” class=”codepen”>See the Pen 自定义事宜 by whjin (@whjin) on CodePen.</p>
<script async src=”https://static.codepen.io/ass…;></script>
在改变组件的data "counter"
后,经由过程$emit()
再把它通报给父组件,父组件用@increase
和@reduce
。$emit()
要领的第一个参数是自定义事宜的称号。
除了用v-on
在组件上监听自定义事宜外,也能够监听DOM事宜,这时候能够用.native
修饰符示意监听时一个原生事宜,监听的是该组件的根元素。
<my-component @click:native="handleClick"></my-component>
运用v-model
Vue能够在自定义组件上运用v-model
指令。
<my-component v-model="total"></my-component>
组件$emit()
的事宜名时特别的input
,在运用组件的父级,并没有在<my-component>
上运用@input="handler"
,而是直接用了v-model
绑定的一个数据total
。
<my-component @input="handleGetTotal"></my-component>
v-model
还能够用来建立自定义的表单输入组件,举行数据双向绑定。
<p data-height=”365″ data-theme-id=”0″ data-slug-hash=”zaqJBQ” data-default-tab=”html,result” data-user=”whjin” data-embed-version=”2″ data-pen-title=”v-model双向绑定” class=”codepen”>See the Pen v-model双向绑定 by whjin (@whjin) on CodePen.</p>
<script async src=”https://static.codepen.io/ass…;></script>
完成如许一个具有双向绑定的v-model
组件要满足下面两个请求:
- 吸收一个
value
属性 - 在有新的
value
时触发input
事宜
非父子组件通讯
在现实营业中,除了父子组件通讯外,另有许多非父子组件通讯的场景,非父子组件平常有两种,兄弟组件和跨多级组件。
在Vue 1.x版本中,除了$emit()
要领外,还供应了¥dispatch()
和$broadcast()
。
$dispatch()
用于向上级派发事宜,只如果它的父级(一级或多级以上),都能够在Vue实例的events
选项内吸收。
此实例只在Vue 1.x版本中有效:
<p data-height=”365″ data-theme-id=”0″ data-slug-hash=”pKyOOY” data-default-tab=”html,result” data-user=”whjin” data-embed-version=”2″ data-pen-title=”dispatch派发事宜” class=”codepen”>See the Pen dispatch派发事宜 by whjin (@whjin) on CodePen.</p>
<script async src=”https://static.codepen.io/ass…;></script>
$broadcast()
是由上级向下级播送事宜,用法完全一致,方向相反。
这两种要领一旦发出事宜后,任何组件都能够吸收到,就近准绳,而且会在第一次吸收到后住手冒泡,除非返回true
。
这些要领在Vue 2.x版本中已烧毁。
在Vue 2.x中,引荐任何一个空的Vue实例作为中心事宜总线(bus
),也就是一个中介。
<p data-height=”365″ data-theme-id=”0″ data-slug-hash=”dKMgvJ” data-default-tab=”html,result” data-user=”whjin” data-embed-version=”2″ data-pen-title=”Vue-bus事宜总线” class=”codepen”>See the Pen Vue-bus事宜总线 by whjin (@whjin) on CodePen.</p>
<script async src=”https://static.codepen.io/ass…;></script>
起首建立了一个名为bus
的空的Vue实例;然后全局定义了组件component-a
;末了建立了Vue实例app
。
在app
初始化时,也就是在生命周期mounted
钩子函数里监听了来自bus
的事宜on-message
,而在组件component-a
中,点击按钮后会经由过程bus
把事宜on-message
发出去。此时app
就会吸收到来自bus
的事宜,进而在回调里完成本身的营业逻辑。
这类要领奇妙而轻量地完成了任何组件间的通讯,包含父子、兄弟、跨级。
假如深切运用,能够扩大bus
实例,给它增加data
、methods
、computed
等选项,这些都是能够公用的。
在营业中,尤其是协同开辟时异常有效,由于常常须要同享一些通用的信息,比方用户登录的昵称、性别、邮箱等,另有效户的受权token
等。
只需在初始化时让bus
猎取一次,任何时候、任何组件就能够从中直接运用,在单页面富运用(SPA)中会很有用。
除了中心事宜总线bus
外,另有两种要领能够完成组件间通讯:父链和子组件索引。
父链
在子组件中,运用this.$parent
能够直接接见该组件的父实例或组件,父组件也能够经由过程this.$children
接见它一切的子组件,而且能够递归向上或向下无穷接见,直到根实例或最内层的组件。
<div id="app">
<p>{{message}}</p>
<component-a></component-a>
</div>
Vue.component('component-a', {
template: `<button @click="handleEvent">经由过程父链直接修正数据</button>`,
methods: {
handleEvent: function () {
this.$parent.message = '来自组件component-a的内容'
}
}
});
var app = new Vue({
el: '#app',
data: {
message: ''
}
});
只管Vue许可如许操纵,但在营业中,子组件应该尽量地防止依靠父组件的数据,更不应该去主动修正它的数据,由于如许使得父子组件紧耦合,只看父组件,很难明白父组件的状况,由于它能够被恣意组件修正,抱负状况下,只要组件本身能修正它的状况。
父子组件最好照样经由过程props
和$emit()
来通讯。
子组件索引
当子组件较多时,经由过程this.$children
来遍历出须要的一个组件实例是比较难题的,尤其是组件动态衬着时,它们的序列是不牢固的。
Vue供应了子组件索引的要领,用特别的属性ref
来为子组件指定一个索引称号。
<p data-height=”365″ data-theme-id=”0″ data-slug-hash=”dKMLXY” data-default-tab=”html,result” data-user=”whjin” data-embed-version=”2″ data-pen-title=”Vue-$refs” class=”codepen”>See the Pen <a href=”https://codepen.io/whjin/pen/dKMLXY/”>Vue-$refs</a> by whjin (@whjin) on CodePen.</p>
<script async src=”https://static.codepen.io/ass…;></script>
在父组件模板中,子组件标签上运用ref
指定一个称号,并在父组件内经由过程this.$refs
来接见指定称号的子组件。
$refs
只在组件衬着完成后才添补,而且它黑白相应式的。它仅仅作为一个直接接见子组件的应急计划,应该防止在模板或盘算属性中运用
$refs
。
Vue 2.x将v-el
和v-ref
合并成ref
,Vue会自动去推断是一般标签照样组件。
运用slot
分发内容
当须要让组件组合运用,夹杂父组件的内容与子组件的模板时,就会用到slot
,这个历程叫做内容分发。
-
<app>
组件不知道它的挂载点会有什么内容。挂载点的内容是由<app>
的父组件决议的。 -
<app>
组件很能够有它本身的模板。
props
通报数据、events
触发事宜和slot
内容分发就组成了Vue组件的3个API泉源,再庞杂的组件也是由这3部份组成。
作用域
父组件中的模板:
<child-component>
{{message}}
</child-component>
这里的message
就是一个slot
,然则它绑定的是父组件的数据,而不是组件<child-component>
的数据。
父组件模板的内容是在父组件作用域内编译,子组件模板的内容是在子组件作用域内编译。
<div id="app">
<child-component v-modle="showChild"></child-component>
</div>
Vue.component('child-component', {
template: `<div>子组件1</div>`,
});
var app = new Vue({
el: '#app',
data: {
showChild: true
}
});
这里的状况showChild
绑定的是父组件的数据。
在子组件上绑定数据:
<div id="app">
<child-component></child-component>
</div>
Vue.component('child-component', {
template: `<div v-model="showChild">子组件</div>`,
data() {
return {
showChild: true
}
}
});
var app = new Vue({
el: '#app',
});
因此,slot
分发的内容,作用域是在父组件上。
单个slot
在子组件内运用特别的<slot>
元素就能够为这个组件开启一个slot
(插槽),在父组件模板里,插进去在子组件标签内的一切内容将替代子组件的<slot>
标签及它的内容。
<div id="app">
<child-component>
<p>分发的内容</p>
<p>更多分发的内容</p>
</child-component>
</div>
Vue.component('child-component', {
template: `
<div>
<slot>
<p>假如没有父组件插进去内容,我将作为默许涌现。</p>
</slot>
</div>
`,
});
var app = new Vue({
el: '#app',
});
子组件child-component
的模板内定义了一个<slot>
元素,而且用一个<p>
作为默许的内容,在父组件没有运用slot
时,会衬着这段默许的文本;假如写入了slot
,就会替代全部<slot>
。
子组件
<slot>
内的备用内容,它的作用域是子组件本身。
签字Slot
给<slot>
元素指定一个name
后能够分发多个内容,签字slot
能够与单个slot
共存。
<p data-height=”265″ data-theme-id=”0″ data-slug-hash=”RJRVQJ” data-default-tab=”html,result” data-user=”whjin” data-embed-version=”2″ data-pen-title=”Vue-slot” class=”codepen”>See the Pen Vue-slot by whjin (@whjin) on CodePen.</p>
<script async src=”https://static.codepen.io/ass…;></script>
子组件内声清楚明了3个<slot>
元素,个中在<div class="main">
内的<slot>
没有运用name
特征,它将作为默许slot
涌现,父组件没有运用slot
特征的元素与内容都将涌现在这里。
假如没有指定默许的匿名slot
,父组件内过剩的内容都将被扬弃。
在组合运用组件时,内容分发API至关重要。
作用域插槽
作用域插槽是一种特别的slot
,运用一个能够复用的模板替代已衬着元素。
<div id="app">
<child-component>
<template scope="props">
<p>来自父组件的内容</p>
<p>{{props.msg}}</p>
</template>
</child-component>
</div>
Vue.component('child-component', {
template: `
<div class="container">
<slot msg="来自子组件的内容"></slot>
</div>
`,
});
var app = new Vue({
el: '#app',
});
子组件的模板,在<slot>
元素上有一个相似props
通报数据给组件的写法msg="xxx"
,将数据通报到插槽。
父组件中运用了<template>
元素,而且具有一个scope="props"
的特征,这里的props
是一个暂时变量。
template
内能够经由过程暂时变量props
接见来自子组件插槽的数据msg
。
作用域插槽更具代表性的用例是列表组件,许可组件自定义应该怎样衬着列表每一项。
<div id="app">
<my-list :book="books">
<!--作用域插槽也能够是签字的Slot-->
<template slot="book" scope="props">
<li>{{props.bookName}}</li>
</template>
</my-list>
</div>
Vue.component('my-list', {
props: {
books: {
type: Array,
default: function () {
return [];
}
}
},
template: `
<ul>
<slot name="book" v-for="book in books" :book-name="book.name"></slot>
</ul>
`,
});
子组件my-list
吸收一个来自父级的prop
数组books
,而且将它在name
为book
的slot
上运用v-for
指令轮回,同时暴露一个变量bookName
。
作用域插槽的运用场景是既能够复用子组件的slot
,又能够使slot
内容不一致。
接见slot
Vue 2.x供应了用来接见被slot
分发的内容的要领$slots
。
<p data-height=”365″ data-theme-id=”0″ data-slug-hash=”vrKZew” data-default-tab=”html,result” data-user=”whjin” data-embed-version=”2″ data-pen-title=”Vue-$slots” class=”codepen”>See the Pen <a href=”https://codepen.io/whjin/pen/vrKZew/”>Vue-$slots</a> by whjin (@whjin) on CodePen.</p>
<script async src=”https://static.codepen.io/ass…;></script>
经由过程$slots
能够接见某个签字slot
,this.$slots.default
包含了一切没有被包含在签字slot
中的节点。
组件高等用法
递归组件
给组件设置name
选项,组件在它的模板内能够递归地挪用本身。
<div id="app">
<child-component :count="1"></child-component>
</div>
Vue.component('child-component', {
name: 'child-component',
props: {
count: {
type: Number,
default: 1
}
},
template: `
<div class="child">
<child-component :count="count+1" v-if="count<3"></child-component>
</div>
`,
});
组件递归运用能够用来开辟一些具有未知层级关机的自力组件,比方级联选择器和树形控件等。
内联模板
组件的模板平常都是在template
选项内定义的,Vue供应了一个内联模板的功用,在运用组件时,给组件标签运用inline-template
特征,组件就会把它的内容当作模板,而不是把它当内容分发,这让模板更天真。
<p data-height=”265″ data-theme-id=”0″ data-slug-hash=”OEXjLb” data-default-tab=”html,result” data-user=”whjin” data-embed-version=”2″ data-pen-title=”Vue-inline-template” class=”codepen”>See the Pen Vue-inline-template by whjin (@whjin) on CodePen.</p>
<script async src=”https://static.codepen.io/ass…;></script>
在父组件中声明的数据message
和子组件中声明的数据msg
,两个都能够衬着(假如同名,优先运用子组件的数据)。这是内联模板的瑕玷,就是作用域比较难明白,假如不是异常特别的场景,发起不要随意马虎运用内联模板。
动态组件
Vue.js供应了一个特别的元素<component>
用来动态地挂载差别的组件,运用is
特征来选摘要挂载的组件。
<p data-height=”365″ data-theme-id=”0″ data-slug-hash=”zaBdyY” data-default-tab=”html,result” data-user=”whjin” data-embed-version=”2″ data-pen-title=”Vue-component” class=”codepen”>See the Pen Vue-component by whjin (@whjin) on CodePen.</p>
<script async src=”https://static.codepen.io/ass…;></script>
能够直接绑定在组件对象上:
<div id="app">
<component :is="currentView"></component>
</div>
var Home = {
template: `<p>Welcome home!</p>`
};
var app = new Vue({
el: '#app',
data: {
currentView: Home
}
});
异步组件
Vue.js许可将组件定义为一个工场函数,动态地剖析组件。
Vue.js只在组件须要衬着时触发工场函数,而且把效果缓存起来,用于背面的再次衬着。
<div id="app">
<child-component></child-component>
</div>
Vue.component('child-component', function (resolve, reject) {
window.setTimeout(function () {
resolve({
template: `<div>我是异步衬着的!</div>`
})
}, 1000)
});
var app = new Vue({
el: '#app',
});
工场函数吸收一个resolve
回调,在收到从服务器下载的组件定义时挪用。也能够挪用reject(reason)
指导加载失利。
其他
$nextTick
异步更新行列
Vue在观察到数据变化时并非直接更新DOM,而是开启一个行列,并缓冲在同一个事宜轮回中发作的一切数据变化。在缓冲时会去除反复数据,从而防止不必要的盘算和DOM操纵。然后,在一下个事宜轮回tick
中,Vue革新行列并实行现实(已去重的)事情。
Vue会依据当前浏览器环境优先运用原生的Promise.then
和MutationObserver
,假如都不支撑,就会采纳setTimeout
替代。
$nextTick
就是用来肯定什么时候DOM更新已完成。
<p data-height=”365″ data-theme-id=”0″ data-slug-hash=”RJRjgm” data-default-tab=”html,result” data-user=”whjin” data-embed-version=”2″ data-pen-title=”Vue-$nextTick” class=”codepen”>See the Pen <a href=”https://codepen.io/whjin/pen/RJRjgm/”>Vue-$nextTick</a> by whjin (@whjin) on CodePen.</p>
<script async src=”https://static.codepen.io/ass…;></script>
X-Templates
Vue供应了另一种定义模板的体式格局,在<script>
标签中运用text/x-template
范例,而且指定一个id
,将这个id
赋给template
。
<div id="app">
<my-component></my-component>
<script type="text/x-template" id="my-component">
<div>这是组件的内容</div>
</script>
</div>
Vue.component('my-component', {
template: `#my-component`,
});
var app = new Vue({
el: '#app',
});
手动挂载实例
在一些异常特别的状况下,须要动态地建立Vue实例,Vue供应了Vue.extend
和$mount
两个要领来手动挂载一个实例。
Vue.extend
是基本Vue组织器,建立一个“子类”,参数是一个包含组件选项的对象。
假如Vue实例在实例化时没有收到el
选项,它就处于“未挂载”状况,没有关联的DOM元素。能够运用$mount
手动地挂载一个未挂载的实例。这个要领返回实例本身,因此能够链式挪用其他实例要领。
<p data-height=”265″ data-theme-id=”0″ data-slug-hash=”BVzmbL” data-default-tab=”html,result” data-user=”whjin” data-embed-version=”2″ data-pen-title=”Vue-$mount” class=”codepen”>See the Pen <a href=”https://codepen.io/whjin/pen/BVzmbL/”>Vue-$mount</a> by whjin (@whjin) on CodePen.</p>
<script async src=”https://static.codepen.io/ass…;></script>
除了以上写法外,另有两种写法:
new MyComponent().$mount("#app");
new MyComponent({
el: '#app'
})
手动挂载实例(组件)是一种比较极度的高等用法,在营业中险些用不到,只在开辟一些庞杂的自力组件时能够会运用。